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:
-rw-r--r--release/scripts/presets/keyconfig/keymap_data/blender_default.py22
-rw-r--r--release/scripts/startup/bl_ui/space_dopesheet.py19
-rw-r--r--release/scripts/startup/bl_ui/space_toolsystem_toolbar.py46
-rw-r--r--release/scripts/startup/bl_ui/space_view3d.py7
-rw-r--r--release/scripts/startup/bl_ui/space_view3d_toolbar.py53
-rw-r--r--source/blender/blenkernel/BKE_gpencil.h7
-rw-r--r--source/blender/blenkernel/BKE_gpencil_curve.h8
-rw-r--r--source/blender/blenkernel/intern/gpencil.c28
-rw-r--r--source/blender/blenkernel/intern/gpencil_curve.c12
-rw-r--r--source/blender/blenkernel/intern/gpencil_geom.c1
-rw-r--r--source/blender/blenkernel/intern/scene.c2
-rw-r--r--source/blender/blenloader/intern/versioning_290.c8
-rw-r--r--source/blender/editors/gpencil/gpencil_data.c2
-rw-r--r--source/blender/editors/gpencil/gpencil_edit.c12
-rw-r--r--source/blender/editors/gpencil/gpencil_edit_curve.c2
-rw-r--r--source/blender/editors/gpencil/gpencil_intern.h49
-rw-r--r--source/blender/editors/gpencil/gpencil_interpolate.c1011
-rw-r--r--source/blender/editors/gpencil/gpencil_merge.c3
-rw-r--r--source/blender/editors/gpencil/gpencil_primitive.c1
-rw-r--r--source/blender/editors/gpencil/gpencil_select.c55
-rw-r--r--source/blender/editors/gpencil/gpencil_utils.c22
-rw-r--r--source/blender/makesdna/DNA_gpencil_types.h8
-rw-r--r--source/blender/makesdna/DNA_scene_types.h16
-rw-r--r--source/blender/makesrna/intern/rna_gpencil.c10
-rw-r--r--source/blender/makesrna/intern/rna_scene.c81
25 files changed, 993 insertions, 492 deletions
diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py
index fd9d699ed01..69582849308 100644
--- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py
+++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py
@@ -6757,6 +6757,15 @@ def km_3d_view_tool_paint_gpencil_eyedropper(params):
]},
)
+def km_3d_view_tool_paint_gpencil_interpolate(params):
+ return (
+ "3D View Tool: Paint Gpencil, Interpolate",
+ {"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
+ {"items": [
+ ("gpencil.interpolate", {"type": params.tool_tweak, "value": 'ANY'},
+ {"properties": [("release_confirm", True)]}),
+ ]},
+ )
def km_3d_view_tool_edit_gpencil_select(params):
return (
@@ -6858,6 +6867,17 @@ def km_3d_view_tool_edit_gpencil_transform_fill(params):
)
+def km_3d_view_tool_edit_gpencil_interpolate(params):
+ return (
+ "3D View Tool: Edit Gpencil, Interpolate",
+ {"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
+ {"items": [
+ ("gpencil.interpolate", {"type": params.tool_tweak, "value": 'ANY'},
+ {"properties": [("release_confirm", True)]}),
+ ]},
+ )
+
+
def km_3d_view_tool_sculpt_gpencil_select(params):
return (
"3D View Tool: Sculpt Gpencil, Tweak",
@@ -7173,6 +7193,7 @@ def generate_keymaps(params=None):
km_3d_view_tool_paint_gpencil_curve(params),
km_3d_view_tool_paint_gpencil_cutter(params),
km_3d_view_tool_paint_gpencil_eyedropper(params),
+ km_3d_view_tool_paint_gpencil_interpolate(params),
km_3d_view_tool_edit_gpencil_select(params),
km_3d_view_tool_edit_gpencil_select_box(params),
km_3d_view_tool_edit_gpencil_select_circle(params),
@@ -7183,6 +7204,7 @@ def generate_keymaps(params=None):
km_3d_view_tool_edit_gpencil_shear(params),
km_3d_view_tool_edit_gpencil_to_sphere(params),
km_3d_view_tool_edit_gpencil_transform_fill(params),
+ km_3d_view_tool_edit_gpencil_interpolate(params),
km_3d_view_tool_sculpt_gpencil_select(params),
km_3d_view_tool_sculpt_gpencil_select_box(params),
km_3d_view_tool_sculpt_gpencil_select_circle(params),
diff --git a/release/scripts/startup/bl_ui/space_dopesheet.py b/release/scripts/startup/bl_ui/space_dopesheet.py
index aa98e4292f4..39d232b2871 100644
--- a/release/scripts/startup/bl_ui/space_dopesheet.py
+++ b/release/scripts/startup/bl_ui/space_dopesheet.py
@@ -322,7 +322,7 @@ class DOPESHEET_MT_editor_menus(Menu):
if st.mode != 'GPENCIL':
layout.menu("DOPESHEET_MT_key")
else:
- layout.menu("DOPESHEET_MT_gpencil_frame")
+ layout.menu("DOPESHEET_MT_gpencil_key")
class DOPESHEET_MT_view(Menu):
@@ -558,8 +558,8 @@ class DOPESHEET_MT_gpencil_channel(Menu):
layout.operator_menu_enum("anim.channels_move", "direction", text="Move...")
-class DOPESHEET_MT_gpencil_frame(Menu):
- bl_label = "Frame"
+class DOPESHEET_MT_gpencil_key(Menu):
+ bl_label = "Key"
def draw(self, _context):
layout = self.layout
@@ -569,15 +569,14 @@ class DOPESHEET_MT_gpencil_frame(Menu):
layout.operator_menu_enum("action.mirror", "type", text="Mirror")
layout.separator()
- layout.operator("action.duplicate")
- layout.operator("action.delete")
+ layout.operator("action.keyframe_insert")
layout.separator()
- layout.operator("action.keyframe_type")
+ layout.operator("action.delete")
+ layout.operator("gpencil.interpolate_reverse")
- # layout.separator()
- # layout.operator("action.copy")
- # layout.operator("action.paste")
+ layout.separator()
+ layout.operator("action.keyframe_type", text="Keyframe Type")
class DOPESHEET_MT_delete(Menu):
@@ -761,7 +760,7 @@ classes = (
DOPESHEET_MT_key,
DOPESHEET_MT_key_transform,
DOPESHEET_MT_gpencil_channel,
- DOPESHEET_MT_gpencil_frame,
+ DOPESHEET_MT_gpencil_key,
DOPESHEET_MT_delete,
DOPESHEET_MT_context_menu,
DOPESHEET_MT_channel_context_menu,
diff --git a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py
index 68cded82ce3..75bca09a044 100644
--- a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py
+++ b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py
@@ -2035,6 +2035,26 @@ class _defs_gpencil_paint:
draw_settings=draw_settings,
)
+ @ToolDef.from_fn
+ def interpolate():
+ def draw_settings(context, layout, tool):
+ props = tool.operator_properties("gpencil.interpolate")
+ row = layout.row()
+ row.prop(props, "layers")
+ row.prop(props, "flip")
+ row.prop(props, "smooth_factor")
+ row.prop(props, "smooth_steps")
+
+ return dict(
+ idname="builtin.interpolate",
+ label="Interpolate",
+ icon="ops.pose.breakdowner",
+ cursor='DEFAULT',
+ widget=None,
+ keymap=(),
+ draw_settings=draw_settings,
+ )
+
class _defs_gpencil_edit:
def is_segment(context):
@@ -2198,6 +2218,27 @@ class _defs_gpencil_edit:
draw_settings=draw_settings,
)
+ @ToolDef.from_fn
+ def interpolate():
+ def draw_settings(context, layout, tool):
+ props = tool.operator_properties("gpencil.interpolate")
+ row = layout.row()
+ row.prop(props, "layers")
+ row.prop(props, "interpolate_selected_only")
+ row.prop(props, "flip")
+ row.prop(props, "smooth_factor")
+ row.prop(props, "smooth_steps")
+
+ return dict(
+ idname="builtin.interpolate",
+ label="Interpolate",
+ icon="ops.pose.breakdowner",
+ cursor='DEFAULT',
+ widget=None,
+ keymap=(),
+ draw_settings=draw_settings,
+ )
+
class _defs_gpencil_sculpt:
@@ -2877,6 +2918,8 @@ class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel):
_defs_gpencil_paint.box,
_defs_gpencil_paint.circle,
None,
+ _defs_gpencil_paint.interpolate,
+ None,
*_tools_annotate,
],
'EDIT_GPENCIL': [
@@ -2892,9 +2935,10 @@ class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel):
_defs_gpencil_edit.shear,
_defs_gpencil_edit.tosphere,
),
- None,
_defs_gpencil_edit.transform_fill,
None,
+ _defs_gpencil_edit.interpolate,
+ None,
*_tools_annotate,
],
'SCULPT_GPENCIL': [
diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py
index 0fe16720d81..43866add7dd 100644
--- a/release/scripts/startup/bl_ui/space_view3d.py
+++ b/release/scripts/startup/bl_ui/space_view3d.py
@@ -715,13 +715,6 @@ class VIEW3D_HT_header(Header):
text="Multiframe",
)
- if gpd.use_stroke_edit_mode or gpd.is_stroke_paint_mode:
- row = layout.row(align=True)
- row.popover(
- panel="VIEW3D_PT_tools_grease_pencil_interpolate",
- text="Interpolate",
- )
-
overlay = view.overlay
VIEW3D_MT_editor_menus.draw_collapsible(context, layout)
diff --git a/release/scripts/startup/bl_ui/space_view3d_toolbar.py b/release/scripts/startup/bl_ui/space_view3d_toolbar.py
index f93a6f3346b..257b9edf4e8 100644
--- a/release/scripts/startup/bl_ui/space_view3d_toolbar.py
+++ b/release/scripts/startup/bl_ui/space_view3d_toolbar.py
@@ -1693,58 +1693,6 @@ class VIEW3D_PT_tools_grease_pencil_brush_paint_falloff(GreasePencilBrushFalloff
return (settings and settings.brush and settings.brush.curve and tool == 'TINT')
-# Grease Pencil stroke interpolation tools
-class VIEW3D_PT_tools_grease_pencil_interpolate(Panel):
- bl_space_type = 'VIEW_3D'
- bl_region_type = 'HEADER'
- bl_label = "Interpolate"
-
- @classmethod
- def poll(cls, context):
- if context.gpencil_data is None:
- return False
-
- gpd = context.gpencil_data
- valid_mode = bool(gpd.use_stroke_edit_mode or gpd.is_stroke_paint_mode)
- return bool(context.editable_gpencil_strokes) and valid_mode
-
- def draw(self, context):
- layout = self.layout
- settings = context.tool_settings.gpencil_interpolate
-
- col = layout.column(align=True)
- col.label(text="Interpolate Strokes")
- col.operator("gpencil.interpolate", text="Interpolate")
- col.operator("gpencil.interpolate_sequence", text="Sequence")
- col.operator("gpencil.interpolate_reverse", text="Remove Breakdowns")
-
- col = layout.column(align=True)
- col.label(text="Options:")
- col.prop(settings, "interpolate_all_layers")
-
- gpd = context.gpencil_data
- if gpd.use_stroke_edit_mode:
- col.prop(settings, "interpolate_selected_only")
-
- col = layout.column(align=True)
- col.label(text="Sequence Options:")
- col.prop(settings, "step")
- col.prop(settings, "type")
- if settings.type == 'CUSTOM':
- # TODO: Options for loading/saving curve presets?
- col.template_curve_mapping(settings, "interpolation_curve", brush=True,
- use_negative_slope=True)
- elif settings.type != 'LINEAR':
- col.prop(settings, "easing")
-
- if settings.type == 'BACK':
- layout.prop(settings, "back")
- elif settings.type == 'ELASTIC':
- sub = layout.column(align=True)
- sub.prop(settings, "amplitude")
- sub.prop(settings, "period")
-
-
# Grease Pencil stroke sculpting tools
class GreasePencilSculptPanel:
bl_context = ".greasepencil_sculpt"
@@ -2266,7 +2214,6 @@ classes = (
VIEW3D_PT_tools_grease_pencil_vertex_paint_select,
VIEW3D_PT_tools_grease_pencil_vertex_paint_settings,
VIEW3D_PT_tools_grease_pencil_vertex_appearance,
- VIEW3D_PT_tools_grease_pencil_interpolate,
VIEW3D_PT_tools_grease_pencil_brush_mixcolor,
VIEW3D_PT_tools_grease_pencil_brush_mix_palette,
diff --git a/source/blender/blenkernel/BKE_gpencil.h b/source/blender/blenkernel/BKE_gpencil.h
index a8caf317467..b9dba991d64 100644
--- a/source/blender/blenkernel/BKE_gpencil.h
+++ b/source/blender/blenkernel/BKE_gpencil.h
@@ -101,8 +101,11 @@ void BKE_gpencil_tag(struct bGPdata *gpd);
void BKE_gpencil_batch_cache_dirty_tag(struct bGPdata *gpd);
void BKE_gpencil_batch_cache_free(struct bGPdata *gpd);
-void BKE_gpencil_stroke_sync_selection(struct bGPDstroke *gps);
-void BKE_gpencil_curve_sync_selection(struct bGPDstroke *gps);
+void BKE_gpencil_stroke_sync_selection(struct bGPdata *gpd, struct bGPDstroke *gps);
+void BKE_gpencil_curve_sync_selection(struct bGPdata *gpd, struct bGPDstroke *gps);
+void BKE_gpencil_stroke_select_index_set(struct bGPdata *gpd,
+ struct bGPDstroke *gps,
+ const bool reset);
struct bGPDframe *BKE_gpencil_frame_addnew(struct bGPDlayer *gpl, int cframe);
struct bGPDframe *BKE_gpencil_frame_addcopy(struct bGPDlayer *gpl, int cframe);
diff --git a/source/blender/blenkernel/BKE_gpencil_curve.h b/source/blender/blenkernel/BKE_gpencil_curve.h
index 2d42bb36949..9cbe67af9c1 100644
--- a/source/blender/blenkernel/BKE_gpencil_curve.h
+++ b/source/blender/blenkernel/BKE_gpencil_curve.h
@@ -50,8 +50,12 @@ struct bGPDcurve *BKE_gpencil_stroke_editcurve_generate(struct bGPDstroke *gps,
void BKE_gpencil_stroke_editcurve_update(struct bGPdata *gpd,
struct bGPDlayer *gpl,
struct bGPDstroke *gps);
-void BKE_gpencil_editcurve_stroke_sync_selection(struct bGPDstroke *gps, struct bGPDcurve *gpc);
-void BKE_gpencil_stroke_editcurve_sync_selection(struct bGPDstroke *gps, struct bGPDcurve *gpc);
+void BKE_gpencil_editcurve_stroke_sync_selection(struct bGPdata *gpd,
+ struct bGPDstroke *gps,
+ struct bGPDcurve *gpc);
+void BKE_gpencil_stroke_editcurve_sync_selection(struct bGPdata *gpd,
+ struct bGPDstroke *gps,
+ struct bGPDcurve *gpc);
void BKE_gpencil_strokes_selected_update_editcurve(struct bGPdata *gpd);
void BKE_gpencil_strokes_selected_sync_selection_editcurve(struct bGPdata *gpd);
void BKE_gpencil_stroke_update_geometry_from_editcurve(struct bGPDstroke *gps,
diff --git a/source/blender/blenkernel/intern/gpencil.c b/source/blender/blenkernel/intern/gpencil.c
index 1aec1ad296d..4167f60e880 100644
--- a/source/blender/blenkernel/intern/gpencil.c
+++ b/source/blender/blenkernel/intern/gpencil.c
@@ -1134,7 +1134,7 @@ bGPdata *BKE_gpencil_data_duplicate(Main *bmain, const bGPdata *gpd_src, bool in
* Ensure selection status of stroke is in sync with its points.
* \param gps: Grease pencil stroke
*/
-void BKE_gpencil_stroke_sync_selection(bGPDstroke *gps)
+void BKE_gpencil_stroke_sync_selection(bGPdata *gpd, bGPDstroke *gps)
{
bGPDspoint *pt;
int i;
@@ -1148,6 +1148,7 @@ void BKE_gpencil_stroke_sync_selection(bGPDstroke *gps)
* so initially, we must deselect
*/
gps->flag &= ~GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_set(NULL, gps, true);
for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
if (pt->flag & GP_SPOINT_SELECT) {
@@ -1155,9 +1156,13 @@ void BKE_gpencil_stroke_sync_selection(bGPDstroke *gps)
break;
}
}
+
+ if (gps->flag & GP_STROKE_SELECT) {
+ BKE_gpencil_stroke_select_index_set(gpd, gps, false);
+ }
}
-void BKE_gpencil_curve_sync_selection(bGPDstroke *gps)
+void BKE_gpencil_curve_sync_selection(bGPdata *gpd, bGPDstroke *gps)
{
bGPDcurve *gpc = gps->editcurve;
if (gpc == NULL) {
@@ -1165,6 +1170,7 @@ void BKE_gpencil_curve_sync_selection(bGPDstroke *gps)
}
gps->flag &= ~GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_set(NULL, gps, true);
gpc->flag &= ~GP_CURVE_SELECT;
bool is_selected = false;
@@ -1187,6 +1193,19 @@ void BKE_gpencil_curve_sync_selection(bGPDstroke *gps)
if (is_selected) {
gpc->flag |= GP_CURVE_SELECT;
gps->flag |= GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_set(gpd, gps, false);
+ }
+}
+
+/* Assign unique stroke ID for selection. */
+void BKE_gpencil_stroke_select_index_set(bGPdata *gpd, bGPDstroke *gps, const bool reset)
+{
+ if (!reset) {
+ gpd->select_last_index++;
+ gps->select_index = gpd->select_last_index;
+ }
+ else {
+ gps->select_index = 0;
}
}
@@ -2512,6 +2531,11 @@ bool BKE_gpencil_from_image(
pt->flag |= GP_SPOINT_SELECT;
}
}
+
+ if (gps->flag & GP_STROKE_SELECT) {
+ BKE_gpencil_stroke_select_index_set(gpd, gps, false);
+ }
+
BKE_gpencil_stroke_geometry_update(gpd, gps);
}
}
diff --git a/source/blender/blenkernel/intern/gpencil_curve.c b/source/blender/blenkernel/intern/gpencil_curve.c
index be14d74de7a..6707fb53aa3 100644
--- a/source/blender/blenkernel/intern/gpencil_curve.c
+++ b/source/blender/blenkernel/intern/gpencil_curve.c
@@ -781,7 +781,7 @@ void BKE_gpencil_stroke_editcurve_update(bGPdata *gpd, bGPDlayer *gpl, bGPDstrok
/**
* Sync the selection from stroke to editcurve
*/
-void BKE_gpencil_editcurve_stroke_sync_selection(bGPDstroke *gps, bGPDcurve *gpc)
+void BKE_gpencil_editcurve_stroke_sync_selection(bGPdata *gpd, bGPDstroke *gps, bGPDcurve *gpc)
{
if (gps->flag & GP_STROKE_SELECT) {
gpc->flag |= GP_CURVE_SELECT;
@@ -808,10 +808,11 @@ void BKE_gpencil_editcurve_stroke_sync_selection(bGPDstroke *gps, bGPDcurve *gpc
/**
* Sync the selection from editcurve to stroke
*/
-void BKE_gpencil_stroke_editcurve_sync_selection(bGPDstroke *gps, bGPDcurve *gpc)
+void BKE_gpencil_stroke_editcurve_sync_selection(bGPdata *gpd, bGPDstroke *gps, bGPDcurve *gpc)
{
if (gpc->flag & GP_CURVE_SELECT) {
gps->flag |= GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_set(gpd, gps, false);
for (int i = 0; i < gpc->tot_curve_points - 1; i++) {
bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
@@ -865,6 +866,7 @@ void BKE_gpencil_stroke_editcurve_sync_selection(bGPDstroke *gps, bGPDcurve *gpc
}
else {
gps->flag &= ~GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_set(NULL, gps, true);
for (int i = 0; i < gps->totpoints; i++) {
bGPDspoint *pt = &gps->points[i];
pt->flag &= ~GP_SPOINT_SELECT;
@@ -1089,6 +1091,7 @@ void BKE_gpencil_stroke_update_geometry_from_editcurve(bGPDstroke *gps,
/* deselect */
pt->flag &= ~GP_SPOINT_SELECT;
gps->flag &= ~GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_set(NULL, gps, true);
return;
}
@@ -1131,6 +1134,7 @@ void BKE_gpencil_stroke_update_geometry_from_editcurve(bGPDstroke *gps,
pt->flag &= ~GP_SPOINT_SELECT;
}
gps->flag &= ~GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_set(NULL, gps, true);
/* free temp data */
MEM_freeN(points);
@@ -1375,7 +1379,7 @@ void BKE_gpencil_strokes_selected_update_editcurve(bGPdata *gpd)
BKE_gpencil_stroke_editcurve_update(gpd, gpl, gps);
}
/* Update the selection from the stroke to the curve. */
- BKE_gpencil_editcurve_stroke_sync_selection(gps, gps->editcurve);
+ BKE_gpencil_editcurve_stroke_sync_selection(gpd, gps, gps->editcurve);
gps->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
BKE_gpencil_stroke_geometry_update(gpd, gps);
@@ -1400,7 +1404,7 @@ void BKE_gpencil_strokes_selected_sync_selection_editcurve(bGPdata *gpd)
bGPDcurve *gpc = gps->editcurve;
if (gpc != NULL) {
/* Update the selection of every stroke that has an editcurve */
- BKE_gpencil_stroke_editcurve_sync_selection(gps, gpc);
+ BKE_gpencil_stroke_editcurve_sync_selection(gpd, gps, gpc);
}
}
}
diff --git a/source/blender/blenkernel/intern/gpencil_geom.c b/source/blender/blenkernel/intern/gpencil_geom.c
index 2ef85439a46..2d10645a467 100644
--- a/source/blender/blenkernel/intern/gpencil_geom.c
+++ b/source/blender/blenkernel/intern/gpencil_geom.c
@@ -3444,6 +3444,7 @@ void BKE_gpencil_stroke_uniform_subdivide(bGPdata *gpd,
if (select) {
gps->flag |= GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_set(gpd, gps, false);
}
/* Free the sample points. Important to use the mutable loop here because we are erasing the list
diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c
index 25951fa3e6f..c7279dbc815 100644
--- a/source/blender/blenkernel/intern/scene.c
+++ b/source/blender/blenkernel/intern/scene.c
@@ -167,8 +167,6 @@ static void scene_init_data(ID *id)
&gp_primitive_curve->clipr,
CURVE_PRESET_BELL,
CURVEMAP_SLOPE_POSITIVE);
- /* Grease pencil interpolate. */
- scene->toolsettings->gp_interpolate.step = 1;
scene->unit.system = USER_UNIT_METRIC;
scene->unit.scale_length = 1.0f;
diff --git a/source/blender/blenloader/intern/versioning_290.c b/source/blender/blenloader/intern/versioning_290.c
index 728235e84bf..29041f4ae9d 100644
--- a/source/blender/blenloader/intern/versioning_290.c
+++ b/source/blender/blenloader/intern/versioning_290.c
@@ -1061,14 +1061,6 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain)
}
}
- /* Set the minimum sequence interpolate for grease pencil. */
- if (!DNA_struct_elem_find(fd->filesdna, "GP_Interpolate_Settings", "int", "step")) {
- LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
- ToolSettings *ts = scene->toolsettings;
- ts->gp_interpolate.step = 1;
- }
- }
-
/* Hair and PointCloud attributes. */
for (Hair *hair = bmain->hairs.first; hair != NULL; hair = hair->id.next) {
do_versions_point_attributes(&hair->pdata);
diff --git a/source/blender/editors/gpencil/gpencil_data.c b/source/blender/editors/gpencil/gpencil_data.c
index 4c9bde77103..0597de445f4 100644
--- a/source/blender/editors/gpencil/gpencil_data.c
+++ b/source/blender/editors/gpencil/gpencil_data.c
@@ -3418,9 +3418,11 @@ static int gpencil_material_select_exec(bContext *C, wmOperator *op)
if (!deselected) {
gps->flag |= GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_set(gpd, gps, false);
}
else {
gps->flag &= ~GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_set(NULL, gps, true);
}
for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
if (!deselected) {
diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c
index bf1aff5a34a..aec593b97ea 100644
--- a/source/blender/editors/gpencil/gpencil_edit.c
+++ b/source/blender/editors/gpencil/gpencil_edit.c
@@ -214,7 +214,7 @@ static int gpencil_editmode_toggle_exec(bContext *C, wmOperator *op)
if (gpc->flag & GP_CURVE_NEEDS_STROKE_UPDATE) {
BKE_gpencil_stroke_editcurve_update(gpd, gpl, gps);
/* Update the selection from the stroke to the curve. */
- BKE_gpencil_editcurve_stroke_sync_selection(gps, gps->editcurve);
+ BKE_gpencil_editcurve_stroke_sync_selection(gpd, gps, gps->editcurve);
gps->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
BKE_gpencil_stroke_geometry_update(gpd, gps);
@@ -992,6 +992,7 @@ static int gpencil_duplicate_exec(bContext *C, wmOperator *op)
pt->flag &= ~GP_SPOINT_SELECT;
}
gps->flag &= ~GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_set(NULL, gps, true);
changed = true;
}
@@ -1193,6 +1194,7 @@ static void gpencil_add_move_points(bGPdata *gpd, bGPDframe *gpf, bGPDstroke *gp
/* if the stroke is not reused, deselect */
if (!do_stroke) {
gps->flag &= ~GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_set(NULL, gps, true);
}
}
@@ -1709,6 +1711,7 @@ static int gpencil_strokes_paste_exec(bContext *C, wmOperator *op)
}
gps->flag &= ~GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_set(NULL, gps, true);
}
CTX_DATA_END;
@@ -2548,6 +2551,7 @@ static bool gpencil_dissolve_selected_stroke_points(bContext *C,
/* deselect the stroke, since none of its selected points will still be selected */
gps->flag &= ~GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_set(NULL, gps, true);
for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
pt->flag &= ~GP_SPOINT_SELECT;
}
@@ -2620,6 +2624,7 @@ static int gpencil_delete_selected_points(bContext *C)
if (gps->flag & GP_STROKE_SELECT) {
/* deselect old stroke, since it will be used as template for the new strokes */
gps->flag &= ~GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_set(NULL, gps, true);
if (is_curve_edit) {
bGPDcurve *gpc = gps->editcurve;
@@ -3790,7 +3795,7 @@ static int gpencil_strokes_reproject_exec(bContext *C, wmOperator *op)
if (is_curve_edit && gps->editcurve != NULL) {
BKE_gpencil_stroke_editcurve_update(gpd, gpl, gps);
/* Update the selection from the stroke to the curve. */
- BKE_gpencil_editcurve_stroke_sync_selection(gps, gps->editcurve);
+ BKE_gpencil_editcurve_stroke_sync_selection(gpd, gps, gps->editcurve);
gps->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
BKE_gpencil_stroke_geometry_update(gpd, gps);
@@ -4574,6 +4579,7 @@ static int gpencil_stroke_separate_exec(bContext *C, wmOperator *op)
else if (mode == GP_SEPARATE_STROKE) {
/* deselect old stroke */
gps->flag &= ~GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_set(NULL, gps, true);
/* unlink from source frame */
BLI_remlink(&gpf->strokes, gps);
gps->prev = gps->next = NULL;
@@ -4982,6 +4988,7 @@ static int gpencil_cutter_lasso_select(bContext *C,
}
gps->flag &= ~GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_set(NULL, gps, true);
}
CTX_DATA_END;
@@ -5022,6 +5029,7 @@ static int gpencil_cutter_lasso_select(bContext *C,
changed = true;
pt->flag |= GP_SPOINT_SELECT;
gps->flag |= GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_set(gpd, gps, false);
float r_hita[3], r_hitb[3];
if (gps->totpoints > 1) {
ED_gpencil_select_stroke_segment(
diff --git a/source/blender/editors/gpencil/gpencil_edit_curve.c b/source/blender/editors/gpencil/gpencil_edit_curve.c
index 031bbd61173..0f9a8c93df9 100644
--- a/source/blender/editors/gpencil/gpencil_edit_curve.c
+++ b/source/blender/editors/gpencil/gpencil_edit_curve.c
@@ -88,7 +88,7 @@ static int gpencil_stroke_enter_editcurve_mode_exec(bContext *C, wmOperator *op)
(gps->editcurve != NULL && gps->editcurve->flag & GP_CURVE_NEEDS_STROKE_UPDATE)) {
BKE_gpencil_stroke_editcurve_update(gpd, gpl, gps);
/* Update the selection from the stroke to the curve. */
- BKE_gpencil_editcurve_stroke_sync_selection(gps, gps->editcurve);
+ BKE_gpencil_editcurve_stroke_sync_selection(gpd, gps, gps->editcurve);
gps->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
BKE_gpencil_stroke_geometry_update(gpd, gps);
}
diff --git a/source/blender/editors/gpencil/gpencil_intern.h b/source/blender/editors/gpencil/gpencil_intern.h
index 454f8d21590..c6f74c39beb 100644
--- a/source/blender/editors/gpencil/gpencil_intern.h
+++ b/source/blender/editors/gpencil/gpencil_intern.h
@@ -103,55 +103,6 @@ typedef struct tGPDdraw {
float diff_mat[4][4]; /* matrix */
} tGPDdraw;
-/* Temporary interpolate operation data */
-typedef struct tGPDinterpolate_layer {
- struct tGPDinterpolate_layer *next, *prev;
-
- /** layer */
- struct bGPDlayer *gpl;
- /** frame before current frame (interpolate-from) */
- struct bGPDframe *prevFrame;
- /** frame after current frame (interpolate-to) */
- struct bGPDframe *nextFrame;
- /** interpolated frame */
- struct bGPDframe *interFrame;
- /** interpolate factor */
- float factor;
-
-} tGPDinterpolate_layer;
-
-typedef struct tGPDinterpolate {
- /** Current depsgraph from context */
- struct Depsgraph *depsgraph;
- /** current scene from context */
- struct Scene *scene;
- /** area where painting originated */
- struct ScrArea *area;
- /** region where painting originated */
- struct ARegion *region;
- /** current GP datablock */
- struct bGPdata *gpd;
- /** current material */
- struct Material *mat;
-
- /** current frame number */
- int cframe;
- /** (tGPDinterpolate_layer) layers to be interpolated */
- ListBase ilayers;
- /** value for determining the displacement influence */
- float shift;
- /** initial interpolation factor for active layer */
- float init_factor;
- /** shift low limit (-100%) */
- float low_limit;
- /** shift upper limit (200%) */
- float high_limit;
- /** flag from toolsettings */
- int flag;
-
- NumInput num; /* numeric input */
-} tGPDinterpolate;
-
/* Modal Operator Drawing Callbacks ------------------------ */
void ED_gpencil_draw_fill(struct tGPDdraw *tgpw);
diff --git a/source/blender/editors/gpencil/gpencil_interpolate.c b/source/blender/editors/gpencil/gpencil_interpolate.c
index ecd243ed595..6a2eae934a2 100644
--- a/source/blender/editors/gpencil/gpencil_interpolate.c
+++ b/source/blender/editors/gpencil/gpencil_interpolate.c
@@ -32,6 +32,7 @@
#include "BLI_blenlib.h"
#include "BLI_easing.h"
+#include "BLI_ghash.h"
#include "BLI_math.h"
#include "BLI_utildefines.h"
@@ -53,6 +54,7 @@
#include "BKE_report.h"
#include "UI_interface.h"
+#include "UI_resources.h"
#include "WM_api.h"
#include "WM_types.h"
@@ -67,6 +69,78 @@
#include "gpencil_intern.h"
+/* Temporary interpolate operation data */
+typedef struct tGPDinterpolate_layer {
+ struct tGPDinterpolate_layer *next, *prev;
+
+ /** layer */
+ struct bGPDlayer *gpl;
+ /** frame before current frame (interpolate-from) */
+ struct bGPDframe *prevFrame;
+ /** frame after current frame (interpolate-to) */
+ struct bGPDframe *nextFrame;
+ /** interpolated frame */
+ struct bGPDframe *interFrame;
+ /** interpolate factor */
+ float factor;
+
+ /* Hash tablets to create temp relationship between strokes. */
+ struct GHash *used_strokes;
+ struct GHash *pair_strokes;
+
+} tGPDinterpolate_layer;
+
+typedef struct tGPDinterpolate {
+ /** Current depsgraph from context */
+ struct Depsgraph *depsgraph;
+ /** current scene from context */
+ struct Scene *scene;
+ /** area where painting originated */
+ struct ScrArea *area;
+ /** region where painting originated */
+ struct ARegion *region;
+ /** current object */
+ struct Object *ob;
+ /** current GP datablock */
+ struct bGPdata *gpd;
+ /** current material */
+ struct Material *mat;
+ /* Space Conversion Data */
+ struct GP_SpaceConversion gsc;
+
+ /** current frame number */
+ int cframe;
+ /** (tGPDinterpolate_layer) layers to be interpolated */
+ ListBase ilayers;
+ /** value for determining the displacement influence */
+ float shift;
+ /** initial interpolation factor for active layer */
+ float init_factor;
+ /** shift low limit (-100%) */
+ float low_limit;
+ /** shift upper limit (200%) */
+ float high_limit;
+ /** flag from toolsettings */
+ int flag;
+ /** Flip mode. */
+ int flipmode;
+ /** smooth factor */
+ float smooth_factor;
+ /** smooth iterations */
+ int smooth_steps;
+
+ NumInput num; /* numeric input */
+} tGPDinterpolate;
+
+typedef enum eGP_InterpolateFlipMode {
+ /* No flip. */
+ GP_INTERPOLATE_NOFLIP = 0,
+ /* Flip always. */
+ GP_INTERPOLATE_FLIP = 1,
+ /* Flip if needed. */
+ GP_INTERPOLATE_FLIPAUTO = 2,
+} eGP_InterpolateFlipMode;
+
/* ************************************************ */
/* Core/Shared Utilities */
@@ -79,17 +153,150 @@ static bool gpencil_view3d_poll(bContext *C)
/* only 3D view */
ScrArea *area = CTX_wm_area(C);
if (area && area->spacetype != SPACE_VIEW3D) {
- return 0;
+ return false;
}
/* need data to interpolate */
if (ELEM(NULL, gpd, gpl)) {
- return 0;
+ return false;
}
- return 1;
+ return true;
+}
+
+/* Return if the stroke must be flipped or not. The logic of the calculation
+ * is to check if the lines from extremes crossed. All is done in 2D. */
+static bool gpencil_stroke_need_flip(Depsgraph *depsgraph,
+ Object *ob,
+ bGPDlayer *gpl,
+ GP_SpaceConversion *gsc,
+ bGPDstroke *gps_from,
+ bGPDstroke *gps_to)
+{
+ float diff_mat[4][4];
+ /* calculate parent matrix */
+ BKE_gpencil_layer_transform_matrix_get(depsgraph, ob, gpl, diff_mat);
+ bGPDspoint *pt, pt_dummy_ps;
+ float v1a[2], v1b[2], v2a[2], v2b[2];
+
+ /* Line from start of strokes. */
+ pt = &gps_from->points[0];
+ gpencil_point_to_parent_space(pt, diff_mat, &pt_dummy_ps);
+ gpencil_point_to_xy_fl(gsc, gps_from, &pt_dummy_ps, &v1a[0], &v1a[1]);
+
+ pt = &gps_to->points[0];
+ gpencil_point_to_parent_space(pt, diff_mat, &pt_dummy_ps);
+ gpencil_point_to_xy_fl(gsc, gps_from, &pt_dummy_ps, &v1b[0], &v1b[1]);
+
+ /* Line from end of strokes. */
+ pt = &gps_from->points[gps_from->totpoints - 1];
+ gpencil_point_to_parent_space(pt, diff_mat, &pt_dummy_ps);
+ gpencil_point_to_xy_fl(gsc, gps_from, &pt_dummy_ps, &v2a[0], &v2a[1]);
+
+ pt = &gps_to->points[gps_to->totpoints - 1];
+ gpencil_point_to_parent_space(pt, diff_mat, &pt_dummy_ps);
+ gpencil_point_to_xy_fl(gsc, gps_from, &pt_dummy_ps, &v2b[0], &v2b[1]);
+
+ if (isect_seg_seg_v2(v1a, v1b, v2a, v2b) == ISECT_LINE_LINE_CROSS) {
+ return true;
+ }
+
+ return false;
+}
+
+/* Return the stroke related to the selection index, returning the stroke with
+ * the smallest selection index greater than reference index. */
+static bGPDstroke *gpencil_stroke_get_related(GHash *used_strokes,
+ bGPDframe *gpf,
+ const int reference_index)
+{
+ bGPDstroke *gps_found = NULL;
+ int lower_index = INT_MAX;
+ LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
+ if (gps->select_index > reference_index) {
+ if (!BLI_ghash_haskey(used_strokes, gps)) {
+ if (gps->select_index < lower_index) {
+ lower_index = gps->select_index;
+ gps_found = gps;
+ }
+ }
+ }
+ }
+
+ /* Set as used. */
+ if (gps_found) {
+ BLI_ghash_insert(used_strokes, gps_found, gps_found);
+ }
+
+ return gps_found;
+}
+
+/* Load a Hash with the relationship between strokes. */
+static void gpencil_stroke_pair_table(bContext *C,
+ tGPDinterpolate *tgpi,
+ tGPDinterpolate_layer *tgpil)
+{
+ bGPdata *gpd = tgpi->gpd;
+ const bool only_selected = ((GPENCIL_EDIT_MODE(gpd)) &&
+ ((tgpi->flag & GP_TOOLFLAG_INTERPOLATE_ONLY_SELECTED) != 0));
+ const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
+
+ /* Create hash tablets with relationship between strokes. */
+ tgpil->used_strokes = BLI_ghash_ptr_new(__func__);
+ tgpil->pair_strokes = BLI_ghash_ptr_new(__func__);
+
+ /* Create a table with source and target pair of strokes. */
+ LISTBASE_FOREACH (bGPDstroke *, gps_from, &tgpil->prevFrame->strokes) {
+ bGPDstroke *gps_to = NULL;
+ /* only selected */
+ if ((GPENCIL_EDIT_MODE(gpd)) && (only_selected) &&
+ ((gps_from->flag & GP_STROKE_SELECT) == 0)) {
+ continue;
+ }
+ /* skip strokes that are invalid for current view */
+ if (ED_gpencil_stroke_can_use(C, gps_from) == false) {
+ continue;
+ }
+ /* Check if the material is editable. */
+ if (ED_gpencil_stroke_material_editable(tgpi->ob, tgpil->gpl, gps_from) == false) {
+ continue;
+ }
+ /* Try to get the related stroke. */
+ if ((is_multiedit) && (gps_from->select_index > 0)) {
+ gps_to = gpencil_stroke_get_related(
+ tgpil->used_strokes, tgpil->nextFrame, gps_from->select_index);
+ }
+ /* If not found, get final stroke to interpolate using position in the array. */
+ if (gps_to == NULL) {
+ int fFrame = BLI_findindex(&tgpil->prevFrame->strokes, gps_from);
+ gps_to = BLI_findlink(&tgpil->nextFrame->strokes, fFrame);
+ }
+
+ if (ELEM(NULL, gps_from, gps_to)) {
+ continue;
+ }
+ /* Insert the pair entry in the hash table. */
+ BLI_ghash_insert(tgpil->pair_strokes, gps_from, gps_to);
+ }
}
+static void gpencil_interpolate_smooth_stroke(bGPDstroke *gps,
+ float smooth_factor,
+ int smooth_steps)
+{
+ if (smooth_factor == 0.0f) {
+ return;
+ }
+
+ float reduce = 0.0f;
+ for (int r = 0; r < smooth_steps; r++) {
+ for (int i = 0; i < gps->totpoints - 1; i++) {
+ BKE_gpencil_stroke_smooth(gps, i, smooth_factor - reduce);
+ BKE_gpencil_stroke_smooth_strength(gps, i, smooth_factor);
+ }
+ reduce += 0.25f; /* reduce the factor */
+ }
+}
/* Perform interpolation */
static void gpencil_interpolate_update_points(const bGPDstroke *gps_from,
const bGPDstroke *gps_to,
@@ -112,7 +319,7 @@ static void gpencil_interpolate_update_points(const bGPDstroke *gps_from,
/* ****************** Interpolate Interactive *********************** */
/* Helper: free all temp strokes for display. */
-static void gpencil_interpolate_free_temp_strokes(bGPDframe *gpf)
+static void gpencil_interpolate_free_tagged_strokes(bGPDframe *gpf)
{
if (gpf == NULL) {
return;
@@ -152,33 +359,34 @@ static void gpencil_interpolate_update_strokes(bContext *C, tGPDinterpolate *tgp
const float factor = tgpil->factor + shift;
bGPDframe *gpf = tgpil->gpl->actframe;
- /* Free temp strokes. */
- gpencil_interpolate_free_temp_strokes(gpf);
+ /* Free temp strokes used for display. */
+ gpencil_interpolate_free_tagged_strokes(gpf);
- LISTBASE_FOREACH (bGPDstroke *, new_stroke, &tgpil->interFrame->strokes) {
- bGPDstroke *gps_from, *gps_to;
- int stroke_idx;
+ /* Clear previous interpolations. */
+ gpencil_interpolate_free_tagged_strokes(tgpil->interFrame);
- if (new_stroke->totpoints == 0) {
- continue;
- }
+ GHashIterator gh_iter;
+ GHASH_ITER (gh_iter, tgpil->pair_strokes) {
+ bGPDstroke *gps_from = (bGPDstroke *)BLI_ghashIterator_getKey(&gh_iter);
+ bGPDstroke *gps_to = (bGPDstroke *)BLI_ghashIterator_getValue(&gh_iter);
+ /* Create new stroke. */
+ bGPDstroke *new_stroke = BKE_gpencil_stroke_duplicate(gps_from, true, true);
+ new_stroke->flag |= GP_STROKE_TAG;
+ new_stroke->select_index = 0;
- /* get strokes to interpolate */
- stroke_idx = BLI_findindex(&tgpil->interFrame->strokes, new_stroke);
+ /* Update points position. */
+ gpencil_interpolate_update_points(gps_from, gps_to, new_stroke, factor);
- gps_from = BLI_findlink(&tgpil->prevFrame->strokes, stroke_idx);
- gps_to = BLI_findlink(&tgpil->nextFrame->strokes, stroke_idx);
-
- /* update points position */
- if ((gps_from) && (gps_to)) {
- gpencil_interpolate_update_points(gps_from, gps_to, new_stroke, factor);
+ /* Calc geometry data. */
+ BKE_gpencil_stroke_geometry_update(gpd, new_stroke);
+ /* Add to strokes. */
+ BLI_addtail(&tgpil->interFrame->strokes, new_stroke);
- /* Add temp strokes. */
- if (gpf) {
- bGPDstroke *gps_eval = BKE_gpencil_stroke_duplicate(new_stroke, true, true);
- gps_eval->flag |= GP_STROKE_TAG;
- BLI_addtail(&gpf->strokes, gps_eval);
- }
+ /* Add temp strokes to display. */
+ if (gpf) {
+ bGPDstroke *gps_eval = BKE_gpencil_stroke_duplicate(new_stroke, true, true);
+ gps_eval->flag |= GP_STROKE_TAG;
+ BLI_addtail(&gpf->strokes, gps_eval);
}
}
}
@@ -187,65 +395,50 @@ static void gpencil_interpolate_update_strokes(bContext *C, tGPDinterpolate *tgp
WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL);
}
-/* Helper: Verify valid strokes for interpolation */
-static bool gpencil_interpolate_check_todo(bContext *C, bGPdata *gpd)
+/* Helper: Get previous keyframe. */
+static bGPDframe *gpencil_get_previous_keyframe(bGPDlayer *gpl, int cfra)
{
- Object *ob = CTX_data_active_object(C);
- ToolSettings *ts = CTX_data_tool_settings(C);
- eGP_Interpolate_SettingsFlag flag = ts->gp_interpolate.flag;
+ if (gpl->actframe != NULL && gpl->actframe->framenum < cfra &&
+ gpl->actframe->key_type == BEZT_KEYTYPE_KEYFRAME) {
+ return gpl->actframe;
+ }
- /* get layers */
- LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
- /* all layers or only active */
- if (!(flag & GP_TOOLFLAG_INTERPOLATE_ALL_LAYERS) && !(gpl->flag & GP_LAYER_ACTIVE)) {
+ LISTBASE_FOREACH_BACKWARD (bGPDframe *, gpf, &gpl->frames) {
+ if (gpf->key_type != BEZT_KEYTYPE_KEYFRAME) {
continue;
}
- /* only editable and visible layers are considered */
- if (!BKE_gpencil_layer_is_editable(gpl) || (gpl->actframe == NULL)) {
+ if (gpf->framenum >= cfra) {
continue;
}
+ return gpf;
+ }
- /* read strokes */
- LISTBASE_FOREACH (bGPDstroke *, gps_from, &gpl->actframe->strokes) {
- bGPDstroke *gps_to;
- int fFrame;
-
- /* only selected */
- if ((GPENCIL_EDIT_MODE(gpd)) && (flag & GP_TOOLFLAG_INTERPOLATE_ONLY_SELECTED) &&
- ((gps_from->flag & GP_STROKE_SELECT) == 0)) {
- continue;
- }
- /* skip strokes that are invalid for current view */
- if (ED_gpencil_stroke_can_use(C, gps_from) == false) {
- continue;
- }
- /* check if the color is editable */
- if (ED_gpencil_stroke_material_editable(ob, gpl, gps_from) == false) {
- continue;
- }
-
- /* get final stroke to interpolate */
- fFrame = BLI_findindex(&gpl->actframe->strokes, gps_from);
- gps_to = (gpl->actframe->next != NULL) ?
- BLI_findlink(&gpl->actframe->next->strokes, fFrame) :
- NULL;
- if (gps_to == NULL) {
- continue;
- }
+ return NULL;
+}
- return true;
+/* Helper: Get next keyframe. */
+static bGPDframe *gpencil_get_next_keyframe(bGPDlayer *gpl, int cfra)
+{
+ LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
+ if (gpf->key_type != BEZT_KEYTYPE_KEYFRAME) {
+ continue;
+ }
+ if (gpf->framenum <= cfra) {
+ continue;
}
+ return gpf;
}
- return false;
+
+ return NULL;
}
/* Helper: Create internal strokes interpolated */
static void gpencil_interpolate_set_points(bContext *C, tGPDinterpolate *tgpi)
{
+ Scene *scene = tgpi->scene;
bGPdata *gpd = tgpi->gpd;
bGPDlayer *active_gpl = CTX_data_active_gpencil_layer(C);
bGPDframe *actframe = active_gpl->actframe;
- Object *ob = CTX_data_active_object(C);
/* save initial factor for active layer to define shift limits */
tgpi->init_factor = (float)(tgpi->cframe - actframe->framenum) /
@@ -274,12 +467,15 @@ static void gpencil_interpolate_set_points(bContext *C, tGPDinterpolate *tgpi)
tgpil = MEM_callocN(sizeof(tGPDinterpolate_layer), "GPencil Interpolate Layer");
tgpil->gpl = gpl;
- tgpil->prevFrame = BKE_gpencil_frame_duplicate(gpl->actframe, true);
- tgpil->nextFrame = BKE_gpencil_frame_duplicate(gpl->actframe->next, true);
+ bGPDframe *gpf = gpencil_get_previous_keyframe(gpl, CFRA);
+ tgpil->prevFrame = BKE_gpencil_frame_duplicate(gpf, true);
+
+ gpf = gpencil_get_next_keyframe(gpl, CFRA);
+ tgpil->nextFrame = BKE_gpencil_frame_duplicate(gpf, true);
BLI_addtail(&tgpi->ilayers, tgpil);
- /* create a new temporary frame */
+ /* Create a new temporary frame. */
tgpil->interFrame = MEM_callocN(sizeof(bGPDframe), "bGPDframe");
tgpil->interFrame->framenum = tgpi->cframe;
@@ -287,62 +483,42 @@ static void gpencil_interpolate_set_points(bContext *C, tGPDinterpolate *tgpi)
tgpil->factor = (float)(tgpi->cframe - tgpil->prevFrame->framenum) /
(tgpil->nextFrame->framenum - tgpil->prevFrame->framenum + 1);
- /* create new strokes data with interpolated points reading original stroke */
- LISTBASE_FOREACH (bGPDstroke *, gps_from, &tgpil->prevFrame->strokes) {
- bGPDstroke *gps_to;
- int fFrame;
+ /* Load the relationship between frames. */
+ gpencil_stroke_pair_table(C, tgpi, tgpil);
- bGPDstroke *new_stroke = NULL;
- bool valid = true;
+ /* Create new strokes data with interpolated points reading original stroke. */
+ GHashIterator gh_iter;
+ GHASH_ITER (gh_iter, tgpil->pair_strokes) {
+ bGPDstroke *gps_from = (bGPDstroke *)BLI_ghashIterator_getKey(&gh_iter);
+ bGPDstroke *gps_to = (bGPDstroke *)BLI_ghashIterator_getValue(&gh_iter);
- /* only selected */
- if ((GPENCIL_EDIT_MODE(gpd)) && (tgpi->flag & GP_TOOLFLAG_INTERPOLATE_ONLY_SELECTED) &&
- ((gps_from->flag & GP_STROKE_SELECT) == 0)) {
- valid = false;
+ /* If destination stroke is smaller, resize new_stroke to size of gps_to stroke. */
+ if (gps_from->totpoints > gps_to->totpoints) {
+ BKE_gpencil_stroke_uniform_subdivide(gpd, gps_to, gps_from->totpoints, true);
}
- /* skip strokes that are invalid for current view */
- if (ED_gpencil_stroke_can_use(C, gps_from) == false) {
- valid = false;
+ if (gps_to->totpoints > gps_from->totpoints) {
+ BKE_gpencil_stroke_uniform_subdivide(gpd, gps_from, gps_to->totpoints, true);
}
- /* check if the color is editable */
- if (ED_gpencil_stroke_material_editable(ob, tgpil->gpl, gps_from) == false) {
- valid = false;
+ /* Flip stroke. */
+ if (tgpi->flipmode == GP_INTERPOLATE_FLIP) {
+ BKE_gpencil_stroke_flip(gps_to);
}
-
- /* get final stroke to interpolate */
- fFrame = BLI_findindex(&tgpil->prevFrame->strokes, gps_from);
- gps_to = BLI_findlink(&tgpil->nextFrame->strokes, fFrame);
- if (gps_to == NULL) {
- valid = false;
- }
-
- if (valid) {
- /* if destination stroke is smaller, resize new_stroke to size of gps_to stroke */
- if (gps_from->totpoints > gps_to->totpoints) {
- BKE_gpencil_stroke_uniform_subdivide(gpd, gps_to, gps_from->totpoints, true);
+ else if (tgpi->flipmode == GP_INTERPOLATE_FLIPAUTO) {
+ if (gpencil_stroke_need_flip(
+ tgpi->depsgraph, tgpi->ob, gpl, &tgpi->gsc, gps_from, gps_to)) {
+ BKE_gpencil_stroke_flip(gps_to);
}
- if (gps_to->totpoints > gps_from->totpoints) {
- BKE_gpencil_stroke_uniform_subdivide(gpd, gps_from, gps_to->totpoints, true);
- }
-
- /* Create new stroke. */
- new_stroke = BKE_gpencil_stroke_duplicate(gps_from, true, true);
-
- /* Update points position. */
- gpencil_interpolate_update_points(gps_from, gps_to, new_stroke, tgpil->factor);
}
- else {
- /* Create new stroke. */
- new_stroke = BKE_gpencil_stroke_duplicate(gps_from, true, true);
- /* need an empty stroke to keep index correct for lookup, but resize to smallest size */
- new_stroke->totpoints = 0;
- new_stroke->points = MEM_recallocN(new_stroke->points, sizeof(*new_stroke->points));
- if (new_stroke->dvert != NULL) {
- new_stroke->dvert = MEM_recallocN(new_stroke->dvert, sizeof(*new_stroke->dvert));
- }
- }
+ /* Create new stroke. */
+ bGPDstroke *new_stroke = BKE_gpencil_stroke_duplicate(gps_from, true, true);
+ new_stroke->flag |= GP_STROKE_TAG;
+ new_stroke->select_index = 0;
+
+ /* Update points position. */
+ gpencil_interpolate_update_points(gps_from, gps_to, new_stroke, tgpil->factor);
+ gpencil_interpolate_smooth_stroke(new_stroke, tgpi->smooth_factor, tgpi->smooth_steps);
/* Calc geometry data. */
BKE_gpencil_stroke_geometry_update(gpd, new_stroke);
@@ -429,7 +605,7 @@ static void gpencil_interpolate_exit(bContext *C, wmOperator *op)
/* Clear any temp stroke. */
LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
- gpencil_interpolate_free_temp_strokes(gpf);
+ gpencil_interpolate_free_tagged_strokes(gpf);
}
}
@@ -441,9 +617,18 @@ static void gpencil_interpolate_exit(bContext *C, wmOperator *op)
MEM_SAFE_FREE(tgpil->prevFrame);
MEM_SAFE_FREE(tgpil->nextFrame);
MEM_SAFE_FREE(tgpil->interFrame);
+
+ /* Free Hash tablets. */
+ if (tgpil->used_strokes != NULL) {
+ BLI_ghash_free(tgpil->used_strokes, NULL, NULL);
+ }
+ if (tgpil->pair_strokes != NULL) {
+ BLI_ghash_free(tgpil->pair_strokes, NULL, NULL);
+ }
}
BLI_freelistN(&tgpi->ilayers);
+
MEM_SAFE_FREE(tgpi);
}
DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
@@ -456,24 +641,33 @@ static void gpencil_interpolate_exit(bContext *C, wmOperator *op)
/* Init new temporary interpolation data */
static bool gpencil_interpolate_set_init_values(bContext *C, wmOperator *op, tGPDinterpolate *tgpi)
{
- ToolSettings *ts = CTX_data_tool_settings(C);
- bGPdata *gpd = CTX_data_gpencil_data(C);
-
/* set current scene and window */
tgpi->depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
tgpi->scene = CTX_data_scene(C);
tgpi->area = CTX_wm_area(C);
tgpi->region = CTX_wm_region(C);
- tgpi->flag = ts->gp_interpolate.flag;
+ tgpi->ob = CTX_data_active_object(C);
+ /* Setup space conversions. */
+ gpencil_point_conversion_init(C, &tgpi->gsc);
/* set current frame number */
tgpi->cframe = tgpi->scene->r.cfra;
/* set GP datablock */
- tgpi->gpd = gpd;
-
+ tgpi->gpd = tgpi->ob->data;
/* set interpolation weight */
tgpi->shift = RNA_float_get(op->ptr, "shift");
+ SET_FLAG_FROM_TEST(
+ tgpi->flag, (RNA_enum_get(op->ptr, "layers") == 1), GP_TOOLFLAG_INTERPOLATE_ALL_LAYERS);
+ SET_FLAG_FROM_TEST(
+ tgpi->flag,
+ ((GPENCIL_EDIT_MODE(tgpi->gpd)) && (RNA_boolean_get(op->ptr, "interpolate_selected_only"))),
+ GP_TOOLFLAG_INTERPOLATE_ONLY_SELECTED);
+
+ tgpi->flipmode = RNA_enum_get(op->ptr, "flip");
+
+ tgpi->smooth_factor = RNA_float_get(op->ptr, "smooth_factor");
+ tgpi->smooth_steps = RNA_int_get(op->ptr, "smooth_steps");
/* Untag strokes to be sure nothing is pending due any canceled process. */
LISTBASE_FOREACH (bGPDlayer *, gpl, &tgpi->gpd->layers) {
@@ -524,12 +718,13 @@ static int gpencil_interpolate_invoke(bContext *C, wmOperator *op, const wmEvent
bGPdata *gpd = CTX_data_gpencil_data(C);
bGPDlayer *gpl = CTX_data_active_gpencil_layer(C);
Scene *scene = CTX_data_scene(C);
- int cfra = CFRA;
- bGPDframe *actframe = gpl->actframe;
tGPDinterpolate *tgpi = NULL;
/* cannot interpolate if not between 2 frames */
- if (ELEM(NULL, actframe, actframe->next)) {
+ int cfra = CFRA;
+ bGPDframe *gpf_prv = gpencil_get_previous_keyframe(gpl, cfra);
+ bGPDframe *gpf_next = gpencil_get_next_keyframe(gpl, cfra);
+ if (ELEM(NULL, gpf_prv, gpf_next)) {
BKE_report(
op->reports,
RPT_ERROR,
@@ -537,25 +732,10 @@ static int gpencil_interpolate_invoke(bContext *C, wmOperator *op, const wmEvent
return OPERATOR_CANCELLED;
}
- /* cannot interpolate in extremes */
- if (ELEM(cfra, actframe->framenum, actframe->next->framenum)) {
- BKE_report(op->reports,
- RPT_ERROR,
- "Cannot interpolate as current frame already has existing grease pencil frames");
- return OPERATOR_CANCELLED;
- }
-
if (GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd)) {
BKE_report(op->reports, RPT_ERROR, "Cannot interpolate in curve edit mode");
return OPERATOR_CANCELLED;
}
-
- /* need editable strokes */
- if (!gpencil_interpolate_check_todo(C, gpd)) {
- BKE_report(op->reports, RPT_ERROR, "Interpolation requires some editable strokes");
- return OPERATOR_CANCELLED;
- }
-
/* try to initialize context data needed */
if (!gpencil_interpolate_init(C, op)) {
if (op->customdata) {
@@ -602,8 +782,7 @@ static int gpencil_interpolate_modal(bContext *C, wmOperator *op, const wmEvent
gpf_dst = BKE_gpencil_layer_frame_get(tgpil->gpl, tgpi->cframe, GP_GETFRAME_ADD_NEW);
gpf_dst->key_type = BEZT_KEYTYPE_BREAKDOWN;
- /* copy strokes */
- BLI_listbase_clear(&gpf_dst->strokes);
+ /* Copy strokes. */
LISTBASE_FOREACH (bGPDstroke *, gps_src, &tgpil->interFrame->strokes) {
if (gps_src->totpoints == 0) {
continue;
@@ -710,6 +889,15 @@ static void gpencil_interpolate_cancel(bContext *C, wmOperator *op)
void GPENCIL_OT_interpolate(wmOperatorType *ot)
{
+ static const EnumPropertyItem flip_modes[] = {
+ {GP_INTERPOLATE_NOFLIP, "NOFLIP", 0, "No Flip", ""},
+ {GP_INTERPOLATE_FLIP, "FLIP", 0, "Flip", ""},
+ {GP_INTERPOLATE_FLIPAUTO, "AUTO", 0, "Automatic", ""},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ PropertyRNA *prop;
+
/* identifiers */
ot->name = "Grease Pencil Interpolation";
ot->idname = "GPENCIL_OT_interpolate";
@@ -724,6 +912,12 @@ void GPENCIL_OT_interpolate(wmOperatorType *ot)
/* flags */
ot->flag = OPTYPE_UNDO | OPTYPE_BLOCKING;
+ static const EnumPropertyItem gpencil_interpolation_layer_items[] = {
+ {0, "ACTIVE", 0, "Active", ""},
+ {1, "ALL", 0, "All Layers", ""},
+ {0, NULL, 0, NULL, NULL},
+ };
+
/* properties */
RNA_def_float_factor(
ot->srna,
@@ -735,25 +929,68 @@ void GPENCIL_OT_interpolate(wmOperatorType *ot)
"Bias factor for which frame has more influence on the interpolated strokes",
-0.9f,
0.9f);
+
+ RNA_def_enum(ot->srna,
+ "layers",
+ gpencil_interpolation_layer_items,
+ 0,
+ "Layer",
+ "Layers included in the interpolation");
+
+ RNA_def_boolean(ot->srna,
+ "interpolate_selected_only",
+ 0,
+ "Only Selected",
+ "Interpolate only selected strokes");
+
+ RNA_def_enum(ot->srna,
+ "flip",
+ flip_modes,
+ GP_INTERPOLATE_FLIPAUTO,
+ "Flip Mode",
+ "Invert destination stroke to match start and end with source stroke");
+
+ RNA_def_int(ot->srna,
+ "smooth_steps",
+ 1,
+ 1,
+ 3,
+ "Iterations",
+ "Number of times to smooth newly created strokes",
+ 1,
+ 3);
+
+ RNA_def_float(ot->srna,
+ "smooth_factor",
+ 0.0f,
+ 0.0f,
+ 2.0f,
+ "Smooth",
+ "Amount of smoothing to apply to interpolated strokes, to reduce jitter/noise",
+ 0.0f,
+ 2.0f);
+
+ prop = RNA_def_boolean(ot->srna, "release_confirm", 0, "Confirm on Release", "");
+ RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
}
/* ****************** Interpolate Sequence *********************** */
/* Helper: Perform easing equation calculations for GP interpolation operator */
-static float gpencil_interpolate_seq_easing_calc(GP_Interpolate_Settings *ipo_settings, float time)
+static float gpencil_interpolate_seq_easing_calc(wmOperator *op, float time)
{
const float begin = 0.0f;
const float change = 1.0f;
const float duration = 1.0f;
- const float back = ipo_settings->back;
- const float amplitude = ipo_settings->amplitude;
- const float period = ipo_settings->period;
-
- eBezTriple_Easing easing = ipo_settings->easing;
+ const float back = RNA_float_get(op->ptr, "back");
+ const float amplitude = RNA_float_get(op->ptr, "amplitude");
+ const float period = RNA_float_get(op->ptr, "period");
+ const eBezTriple_Easing easing = RNA_enum_get(op->ptr, "easing");
+ const eGP_Interpolate_Type type = RNA_enum_get(op->ptr, "type");
float result = time;
- switch (ipo_settings->type) {
+ switch (type) {
case GP_IPO_BACK:
switch (easing) {
case BEZT_IPO_EASE_IN:
@@ -936,7 +1173,7 @@ static float gpencil_interpolate_seq_easing_calc(GP_Interpolate_Settings *ipo_se
break;
default:
- printf("%s: Unknown interpolation type - %d\n", __func__, ipo_settings->type);
+ printf("%s: Unknown interpolation type\n", __func__);
break;
}
@@ -945,34 +1182,47 @@ static float gpencil_interpolate_seq_easing_calc(GP_Interpolate_Settings *ipo_se
static int gpencil_interpolate_seq_exec(bContext *C, wmOperator *op)
{
- bGPdata *gpd = CTX_data_gpencil_data(C);
+ Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
+ Scene *scene = CTX_data_scene(C);
+ ToolSettings *ts = CTX_data_tool_settings(C);
+ Object *ob = CTX_data_active_object(C);
+ bGPdata *gpd = ob->data;
bGPDlayer *active_gpl = CTX_data_active_gpencil_layer(C);
- bGPDframe *actframe = active_gpl->actframe;
+ /* Setup space conversions. */
+ GP_SpaceConversion gsc;
+ gpencil_point_conversion_init(C, &gsc);
- Object *ob = CTX_data_active_object(C);
- ToolSettings *ts = CTX_data_tool_settings(C);
- Scene *scene = CTX_data_scene(C);
int cfra = CFRA;
GP_Interpolate_Settings *ipo_settings = &ts->gp_interpolate;
- eGP_Interpolate_SettingsFlag flag = ipo_settings->flag;
- const int step = ipo_settings->step;
+ const int step = RNA_int_get(op->ptr, "step");
+ const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
+ const bool all_layers = (bool)(RNA_enum_get(op->ptr, "layers") == 1);
+ const bool only_selected = ((GPENCIL_EDIT_MODE(gpd)) &&
+ (RNA_boolean_get(op->ptr, "interpolate_selected_only") != 0));
- /* cannot interpolate if not between 2 frames */
- if (ELEM(NULL, actframe, actframe->next)) {
+ eGP_InterpolateFlipMode flipmode = RNA_enum_get(op->ptr, "flip");
+
+ const float smooth_factor = RNA_float_get(op->ptr, "smooth_factor");
+ const int smooth_steps = RNA_int_get(op->ptr, "smooth_steps");
+
+ const eGP_Interpolate_Type type = RNA_enum_get(op->ptr, "type");
+
+ if (ipo_settings->custom_ipo == NULL) {
+ ipo_settings->custom_ipo = BKE_curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f);
+ }
+ BKE_curvemapping_init(ipo_settings->custom_ipo);
+
+ /* Cannot interpolate if not between 2 frames. */
+ bGPDframe *gpf_prv = gpencil_get_previous_keyframe(active_gpl, cfra);
+ bGPDframe *gpf_next = gpencil_get_next_keyframe(active_gpl, cfra);
+ if (ELEM(NULL, gpf_prv, gpf_next)) {
BKE_report(
op->reports,
RPT_ERROR,
"Cannot find a pair of grease pencil frames to interpolate between in active layer");
return OPERATOR_CANCELLED;
}
- /* cannot interpolate in extremes */
- if (ELEM(cfra, actframe->framenum, actframe->next->framenum)) {
- BKE_report(op->reports,
- RPT_ERROR,
- "Cannot interpolate as current frame already has existing grease pencil frames");
- return OPERATOR_CANCELLED;
- }
if (GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd)) {
BKE_report(op->reports, RPT_ERROR, "Cannot interpolate in curve edit mode");
@@ -981,103 +1231,137 @@ static int gpencil_interpolate_seq_exec(bContext *C, wmOperator *op)
/* loop all layer to check if need interpolation */
LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
- bGPDframe *prevFrame, *nextFrame;
- bGPDstroke *gps_from, *gps_to;
- int cframe, fFrame;
-
- /* Need a set of frames to interpolate. */
- if ((gpl->actframe == NULL) || (gpl->actframe->next == NULL)) {
- continue;
- }
/* all layers or only active */
- if (((flag & GP_TOOLFLAG_INTERPOLATE_ALL_LAYERS) == 0) && (gpl != active_gpl)) {
+ if ((!all_layers) && (gpl != active_gpl)) {
continue;
}
/* only editable and visible layers are considered */
if (!BKE_gpencil_layer_is_editable(gpl)) {
continue;
}
+ gpf_prv = gpencil_get_previous_keyframe(gpl, cfra);
+ gpf_next = gpencil_get_next_keyframe(gpl, cfra);
+
+ /* Need a set of frames to interpolate. */
+ if ((gpf_prv == NULL) || (gpf_next == NULL)) {
+ continue;
+ }
+
+ /* Store extremes. */
+ bGPDframe *prevFrame = BKE_gpencil_frame_duplicate(gpf_prv, true);
+ bGPDframe *nextFrame = BKE_gpencil_frame_duplicate(gpf_next, true);
+
+ /* Create a table with source and target pair of strokes. */
+ GHash *used_strokes = BLI_ghash_ptr_new(__func__);
+ GHash *pair_strokes = BLI_ghash_ptr_new(__func__);
+
+ LISTBASE_FOREACH (bGPDstroke *, gps_from, &prevFrame->strokes) {
+ bGPDstroke *gps_to = NULL;
+ /* Only selected. */
+ if ((GPENCIL_EDIT_MODE(gpd)) && (only_selected) &&
+ ((gps_from->flag & GP_STROKE_SELECT) == 0)) {
+ continue;
+ }
+ /* Skip strokes that are invalid for current view. */
+ if (ED_gpencil_stroke_can_use(C, gps_from) == false) {
+ continue;
+ }
+ /* Check if the material is editable. */
+ if (ED_gpencil_stroke_material_editable(ob, gpl, gps_from) == false) {
+ continue;
+ }
+ /* Try to get the related stroke. */
+ if ((is_multiedit) && (gps_from->select_index > 0)) {
+ gps_to = gpencil_stroke_get_related(used_strokes, nextFrame, gps_from->select_index);
+ }
+ /* If not found, get final stroke to interpolate using position in the array. */
+ if (gps_to == NULL) {
+ int fFrame = BLI_findindex(&prevFrame->strokes, gps_from);
+ gps_to = BLI_findlink(&nextFrame->strokes, fFrame);
+ }
+
+ if (ELEM(NULL, gps_from, gps_to)) {
+ continue;
+ }
+
+ /* if destination stroke is smaller, resize new_stroke to size of gps_to stroke */
+ if (gps_from->totpoints > gps_to->totpoints) {
+ BKE_gpencil_stroke_uniform_subdivide(gpd, gps_to, gps_from->totpoints, true);
+ }
+ if (gps_to->totpoints > gps_from->totpoints) {
+ BKE_gpencil_stroke_uniform_subdivide(gpd, gps_from, gps_to->totpoints, true);
+ }
- /* store extremes */
- prevFrame = BKE_gpencil_frame_duplicate(gpl->actframe, true);
- nextFrame = BKE_gpencil_frame_duplicate(gpl->actframe->next, true);
+ /* Flip stroke. */
+ if (flipmode == GP_INTERPOLATE_FLIP) {
+ BKE_gpencil_stroke_flip(gps_to);
+ }
+ else if (flipmode == GP_INTERPOLATE_FLIPAUTO) {
+ if (gpencil_stroke_need_flip(depsgraph, ob, gpl, &gsc, gps_from, gps_to)) {
+ BKE_gpencil_stroke_flip(gps_to);
+ }
+ }
- /* Loop over intermediary frames and create the interpolation */
- for (cframe = prevFrame->framenum + step; cframe < nextFrame->framenum; cframe += step) {
- bGPDframe *interFrame = NULL;
- float factor;
+ /* Insert the pair entry in the hash table. */
+ BLI_ghash_insert(pair_strokes, gps_from, gps_to);
+ }
- /* get interpolation factor */
+ /* Loop over intermediary frames and create the interpolation. */
+ for (int cframe = prevFrame->framenum + step; cframe < nextFrame->framenum; cframe += step) {
+ /* Get interpolation factor. */
float framerange = nextFrame->framenum - prevFrame->framenum;
CLAMP_MIN(framerange, 1.0f);
- factor = (float)(cframe - prevFrame->framenum) / framerange;
+ float factor = (float)(cframe - prevFrame->framenum) / framerange;
- if (ipo_settings->type == GP_IPO_CURVEMAP) {
+ if (type == GP_IPO_CURVEMAP) {
/* custom curvemap */
if (ipo_settings->custom_ipo) {
factor = BKE_curvemapping_evaluateF(ipo_settings->custom_ipo, 0, factor);
}
else {
BKE_report(op->reports, RPT_ERROR, "Custom interpolation curve does not exist");
+ continue;
}
}
- else if (ipo_settings->type >= GP_IPO_BACK) {
+ else if (type >= GP_IPO_BACK) {
/* easing equation... */
- factor = gpencil_interpolate_seq_easing_calc(ipo_settings, factor);
+ factor = gpencil_interpolate_seq_easing_calc(op, factor);
}
- /* create new strokes data with interpolated points reading original stroke */
- for (gps_from = prevFrame->strokes.first; gps_from; gps_from = gps_from->next) {
-
- /* only selected */
- if ((GPENCIL_EDIT_MODE(gpd)) && (flag & GP_TOOLFLAG_INTERPOLATE_ONLY_SELECTED) &&
- ((gps_from->flag & GP_STROKE_SELECT) == 0)) {
- continue;
- }
- /* skip strokes that are invalid for current view */
- if (ED_gpencil_stroke_can_use(C, gps_from) == false) {
- continue;
- }
- /* check if the color is editable */
- if (ED_gpencil_stroke_material_editable(ob, gpl, gps_from) == false) {
- continue;
- }
-
- /* get final stroke to interpolate */
- fFrame = BLI_findindex(&prevFrame->strokes, gps_from);
- gps_to = BLI_findlink(&nextFrame->strokes, fFrame);
- if (gps_to == NULL) {
- continue;
- }
-
- /* create a new frame if needed */
- if (interFrame == NULL) {
- interFrame = BKE_gpencil_layer_frame_get(gpl, cframe, GP_GETFRAME_ADD_NEW);
- interFrame->key_type = BEZT_KEYTYPE_BREAKDOWN;
- }
-
- /* if destination stroke is smaller, resize new_stroke to size of gps_to stroke */
- if (gps_from->totpoints > gps_to->totpoints) {
- BKE_gpencil_stroke_uniform_subdivide(gpd, gps_to, gps_from->totpoints, true);
- }
- if (gps_to->totpoints > gps_from->totpoints) {
- BKE_gpencil_stroke_uniform_subdivide(gpd, gps_from, gps_to->totpoints, true);
- }
+ /* Apply the factor to all pair of strokes. */
+ GHashIterator gh_iter;
+ GHASH_ITER (gh_iter, pair_strokes) {
+ bGPDstroke *gps_from = (bGPDstroke *)BLI_ghashIterator_getKey(&gh_iter);
+ bGPDstroke *gps_to = (bGPDstroke *)BLI_ghashIterator_getValue(&gh_iter);
- /* create new stroke */
+ /* Create new stroke. */
bGPDstroke *new_stroke = BKE_gpencil_stroke_duplicate(gps_from, true, true);
+ new_stroke->flag |= GP_STROKE_TAG;
+ new_stroke->select_index = 0;
- /* update points position */
+ /* Update points position. */
gpencil_interpolate_update_points(gps_from, gps_to, new_stroke, factor);
+ gpencil_interpolate_smooth_stroke(new_stroke, smooth_factor, smooth_steps);
/* Calc geometry data. */
BKE_gpencil_stroke_geometry_update(gpd, new_stroke);
- /* add to strokes */
+ /* Add strokes to frame. */
+ bGPDframe *interFrame = BKE_gpencil_layer_frame_get(gpl, cframe, GP_GETFRAME_ADD_NEW);
+ interFrame->key_type = BEZT_KEYTYPE_BREAKDOWN;
+
BLI_addtail(&interFrame->strokes, new_stroke);
}
}
+ /* Free Hash tablets. */
+ if (used_strokes != NULL) {
+ BLI_ghash_free(used_strokes, NULL, NULL);
+ }
+ if (pair_strokes != NULL) {
+ BLI_ghash_free(pair_strokes, NULL, NULL);
+ }
+
BKE_gpencil_free_strokes(prevFrame);
BKE_gpencil_free_strokes(nextFrame);
MEM_SAFE_FREE(prevFrame);
@@ -1091,8 +1375,148 @@ static int gpencil_interpolate_seq_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
+static void gpencil_interpolate_seq_ui(bContext *C, wmOperator *op)
+{
+ uiLayout *layout = op->layout;
+ uiLayout *col, *row;
+ PointerRNA ptr;
+
+ RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr);
+
+ const eGP_Interpolate_Type type = RNA_enum_get(op->ptr, "type");
+
+ uiLayoutSetPropSep(layout, true);
+ uiLayoutSetPropDecorate(layout, false);
+
+ col = uiLayoutColumn(layout, true);
+ uiItemR(col, &ptr, "step", 0, NULL, ICON_NONE);
+ uiItemR(col, &ptr, "layers", 0, NULL, ICON_NONE);
+ uiItemR(col, &ptr, "interpolate_selected_only", 0, NULL, ICON_NONE);
+ uiItemR(col, &ptr, "flip", 0, NULL, ICON_NONE);
+ uiItemR(col, &ptr, "smooth_factor", 0, NULL, ICON_NONE);
+ uiItemR(col, &ptr, "smooth_steps", 0, NULL, ICON_NONE);
+ uiItemR(col, &ptr, "type", 0, NULL, ICON_NONE);
+
+ if (type == GP_IPO_CURVEMAP) {
+ /* Get an RNA pointer to ToolSettings to give to the custom curve. */
+ Scene *scene = CTX_data_scene(C);
+ ToolSettings *ts = scene->toolsettings;
+ PointerRNA gpsettings_ptr;
+ RNA_pointer_create(
+ &scene->id, &RNA_GPencilInterpolateSettings, &ts->gp_interpolate, &gpsettings_ptr);
+ uiTemplateCurveMapping(
+ layout, &gpsettings_ptr, "interpolation_curve", 0, false, true, true, false);
+ }
+ else if (type != GP_IPO_LINEAR) {
+ row = uiLayoutRow(layout, false);
+ uiItemR(row, &ptr, "easing", 0, NULL, ICON_NONE);
+ if (type == GP_IPO_BACK) {
+ row = uiLayoutRow(layout, false);
+ uiItemR(row, &ptr, "back", 0, NULL, ICON_NONE);
+ }
+ else if (type == GP_IPO_ELASTIC) {
+ row = uiLayoutRow(layout, false);
+ uiItemR(row, &ptr, "amplitude", 0, NULL, ICON_NONE);
+ row = uiLayoutRow(layout, false);
+ uiItemR(row, &ptr, "period", 0, NULL, ICON_NONE);
+ }
+ }
+}
+
void GPENCIL_OT_interpolate_sequence(wmOperatorType *ot)
{
+ static const EnumPropertyItem gpencil_interpolation_layer_items[] = {
+ {0, "ACTIVE", 0, "Active", ""},
+ {1, "ALL", 0, "All Layers", ""},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ static const EnumPropertyItem gpencil_interpolation_type_items[] = {
+ /* interpolation */
+ {0, "", 0, N_("Interpolation"), "Standard transitions between keyframes"},
+ {GP_IPO_LINEAR,
+ "LINEAR",
+ ICON_IPO_LINEAR,
+ "Linear",
+ "Straight-line interpolation between A and B (i.e. no ease in/out)"},
+ {GP_IPO_CURVEMAP,
+ "CUSTOM",
+ ICON_IPO_BEZIER,
+ "Custom",
+ "Custom interpolation defined using a curve map"},
+
+ /* easing */
+ {0,
+ "",
+ 0,
+ N_("Easing (by strength)"),
+ "Predefined inertial transitions, useful for motion graphics (from least to most "
+ "''dramatic'')"},
+ {GP_IPO_SINE,
+ "SINE",
+ ICON_IPO_SINE,
+ "Sinusoidal",
+ "Sinusoidal easing (weakest, almost linear but with a slight curvature)"},
+ {GP_IPO_QUAD, "QUAD", ICON_IPO_QUAD, "Quadratic", "Quadratic easing"},
+ {GP_IPO_CUBIC, "CUBIC", ICON_IPO_CUBIC, "Cubic", "Cubic easing"},
+ {GP_IPO_QUART, "QUART", ICON_IPO_QUART, "Quartic", "Quartic easing"},
+ {GP_IPO_QUINT, "QUINT", ICON_IPO_QUINT, "Quintic", "Quintic easing"},
+ {GP_IPO_EXPO, "EXPO", ICON_IPO_EXPO, "Exponential", "Exponential easing (dramatic)"},
+ {GP_IPO_CIRC,
+ "CIRC",
+ ICON_IPO_CIRC,
+ "Circular",
+ "Circular easing (strongest and most dynamic)"},
+
+ {0, "", 0, N_("Dynamic Effects"), "Simple physics-inspired easing effects"},
+ {GP_IPO_BACK, "BACK", ICON_IPO_BACK, "Back", "Cubic easing with overshoot and settle"},
+ {GP_IPO_BOUNCE,
+ "BOUNCE",
+ ICON_IPO_BOUNCE,
+ "Bounce",
+ "Exponentially decaying parabolic bounce, like when objects collide"},
+ {GP_IPO_ELASTIC,
+ "ELASTIC",
+ ICON_IPO_ELASTIC,
+ "Elastic",
+ "Exponentially decaying sine wave, like an elastic band"},
+
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ static const EnumPropertyItem gpencil_interpolation_easing_items[] = {
+ {BEZT_IPO_EASE_AUTO,
+ "AUTO",
+ ICON_IPO_EASE_IN_OUT,
+ "Automatic Easing",
+ "Easing type is chosen automatically based on what the type of interpolation used "
+ "(e.g. 'Ease In' for transitional types, and 'Ease Out' for dynamic effects)"},
+
+ {BEZT_IPO_EASE_IN,
+ "EASE_IN",
+ ICON_IPO_EASE_IN,
+ "Ease In",
+ "Only on the end closest to the next keyframe"},
+ {BEZT_IPO_EASE_OUT,
+ "EASE_OUT",
+ ICON_IPO_EASE_OUT,
+ "Ease Out",
+ "Only on the end closest to the first keyframe"},
+ {BEZT_IPO_EASE_IN_OUT,
+ "EASE_IN_OUT",
+ ICON_IPO_EASE_IN_OUT,
+ "Ease In and Out",
+ "Segment between both keyframes"},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ static const EnumPropertyItem flip_modes[] = {
+ {GP_INTERPOLATE_NOFLIP, "NOFLIP", 0, "No Flip", ""},
+ {GP_INTERPOLATE_FLIP, "FLIP", 0, "Flip", ""},
+ {GP_INTERPOLATE_FLIPAUTO, "AUTO", 0, "Automatic", ""},
+ {0, NULL, 0, NULL, NULL},
+ };
+
/* identifiers */
ot->name = "Interpolate Sequence";
ot->idname = "GPENCIL_OT_interpolate_sequence";
@@ -1101,6 +1525,103 @@ void GPENCIL_OT_interpolate_sequence(wmOperatorType *ot)
/* api callbacks */
ot->exec = gpencil_interpolate_seq_exec;
ot->poll = gpencil_view3d_poll;
+ ot->ui = gpencil_interpolate_seq_ui;
+
+ RNA_def_int(ot->srna,
+ "step",
+ 1,
+ 1,
+ MAXFRAME,
+ "Step",
+ "Number of frames between generated interpolated frames",
+ 1,
+ MAXFRAME);
+
+ RNA_def_enum(ot->srna,
+ "layers",
+ gpencil_interpolation_layer_items,
+ 0,
+ "Layer",
+ "Layers included in the interpolation");
+
+ RNA_def_boolean(ot->srna,
+ "interpolate_selected_only",
+ 0,
+ "Only Selected",
+ "Interpolate only selected strokes");
+
+ RNA_def_enum(ot->srna,
+ "flip",
+ flip_modes,
+ GP_INTERPOLATE_FLIPAUTO,
+ "Flip Mode",
+ "Invert destination stroke to match start and end with source stroke");
+
+ RNA_def_int(ot->srna,
+ "smooth_steps",
+ 1,
+ 1,
+ 3,
+ "Iterations",
+ "Number of times to smooth newly created strokes",
+ 1,
+ 3);
+
+ RNA_def_float(ot->srna,
+ "smooth_factor",
+ 0.0f,
+ 0.0f,
+ 2.0f,
+ "Smooth",
+ "Amount of smoothing to apply to interpolated strokes, to reduce jitter/noise",
+ 0.0f,
+ 2.0f);
+
+ RNA_def_enum(ot->srna,
+ "type",
+ gpencil_interpolation_type_items,
+ 0,
+ "Type",
+ "Interpolation method to use the next time 'Interpolate Sequence' is run");
+
+ RNA_def_enum(
+ ot->srna,
+ "easing",
+ gpencil_interpolation_easing_items,
+ 0,
+ "Easing",
+ "Which ends of the segment between the preceding and following grease pencil frames "
+ "easing interpolation is applied to");
+
+ RNA_def_float(ot->srna,
+ "back",
+ 1.702f,
+ 0.0f,
+ FLT_MAX,
+ "Back",
+ "Amount of overshoot for 'back' easing",
+ 0.0f,
+ FLT_MAX);
+
+ RNA_def_float(ot->srna,
+ "amplitude",
+ 0.15f,
+ 0.0f,
+ FLT_MAX,
+ "Amplitude",
+ "Amount to boost elastic bounces for 'elastic' easing",
+ 0.0f,
+ FLT_MAX);
+
+ RNA_def_float(ot->srna,
+ "period",
+ 0.15f,
+ -FLT_MAX,
+ FLT_MAX,
+ "Period",
+ "Time between bounces for elastic easing",
+ -FLT_MAX,
+ FLT_MAX);
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -1110,19 +1631,30 @@ void GPENCIL_OT_interpolate_sequence(wmOperatorType *ot)
static bool gpencil_interpolate_reverse_poll(bContext *C)
{
- if (!gpencil_view3d_poll(C)) {
- return 0;
+ ScrArea *area = CTX_wm_area(C);
+ if (area == NULL) {
+ return false;
+ }
+ if ((area->spacetype != SPACE_VIEW3D) && (area->spacetype != SPACE_ACTION)) {
+ return false;
}
- bGPDlayer *gpl = CTX_data_active_gpencil_layer(C);
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
+ if (gpd == NULL) {
+ return false;
+ }
+ bGPDlayer *gpl = BKE_gpencil_layer_active_get(gpd);
+ if (gpl == NULL) {
+ return false;
+ }
/* need to be on a breakdown frame */
if ((gpl->actframe == NULL) || (gpl->actframe->key_type != BEZT_KEYTYPE_BREAKDOWN)) {
CTX_wm_operator_poll_msg_set(C, "Expected current frame to be a breakdown");
- return 0;
+ return false;
}
- return 1;
+ return true;
}
static int gpencil_interpolate_reverse_exec(bContext *C, wmOperator *UNUSED(op))
@@ -1132,7 +1664,11 @@ static int gpencil_interpolate_reverse_exec(bContext *C, wmOperator *UNUSED(op))
/* Go through each layer, deleting the breakdowns around the current frame,
* but only if there is a keyframe nearby to stop at
*/
- CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
+ LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
+ /* only editable and visible layers are considered */
+ if (!BKE_gpencil_layer_is_editable(gpl) || (gpl->actframe == NULL)) {
+ continue;
+ }
bGPDframe *start_key = NULL;
bGPDframe *end_key = NULL;
bGPDframe *gpf, *gpfn;
@@ -1193,7 +1729,6 @@ static int gpencil_interpolate_reverse_exec(bContext *C, wmOperator *UNUSED(op))
BLI_freelinkN(&gpl->frames, end_key);
}
}
- CTX_DATA_END;
/* notifiers */
DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
@@ -1205,7 +1740,7 @@ static int gpencil_interpolate_reverse_exec(bContext *C, wmOperator *UNUSED(op))
void GPENCIL_OT_interpolate_reverse(wmOperatorType *ot)
{
/* identifiers */
- ot->name = "Remove Breakdowns";
+ ot->name = "Delete Breakdowns";
ot->idname = "GPENCIL_OT_interpolate_reverse";
ot->description =
"Remove breakdown frames generated by interpolating between two Grease Pencil frames";
@@ -1217,5 +1752,3 @@ void GPENCIL_OT_interpolate_reverse(wmOperatorType *ot)
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
-
-/* *************************************************************** */
diff --git a/source/blender/editors/gpencil/gpencil_merge.c b/source/blender/editors/gpencil/gpencil_merge.c
index 435bff34998..8d8734dfd1f 100644
--- a/source/blender/editors/gpencil/gpencil_merge.c
+++ b/source/blender/editors/gpencil/gpencil_merge.c
@@ -104,6 +104,7 @@ static bGPDstroke *gpencil_prepare_stroke(bContext *C, wmOperator *op, int totpo
Main *bmain = CTX_data_main(C);
ToolSettings *ts = CTX_data_tool_settings(C);
Object *ob = CTX_data_active_object(C);
+ bGPdata *gpd = ob->data;
bGPDlayer *gpl = CTX_data_active_gpencil_layer(C);
Scene *scene = CTX_data_scene(C);
@@ -133,6 +134,7 @@ static bGPDstroke *gpencil_prepare_stroke(bContext *C, wmOperator *op, int totpo
/* stroke */
bGPDstroke *gps = BKE_gpencil_stroke_new(MAX2(ob->actcol - 1, 0), totpoints, brush->size);
gps->flag |= GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_set(gpd, gps, false);
if (cyclic) {
gps->flag |= GP_STROKE_CYCLIC;
@@ -241,6 +243,7 @@ static void gpencil_calc_points_factor(bContext *C,
}
}
gps->flag &= ~GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_set(NULL, gps, true);
}
}
}
diff --git a/source/blender/editors/gpencil/gpencil_primitive.c b/source/blender/editors/gpencil/gpencil_primitive.c
index f347a4fe6b9..4eec4c7d00e 100644
--- a/source/blender/editors/gpencil/gpencil_primitive.c
+++ b/source/blender/editors/gpencil/gpencil_primitive.c
@@ -338,6 +338,7 @@ static void gpencil_primitive_set_initdata(bContext *C, tGPDprimitive *tgpi)
ED_gpencil_fill_vertex_color_set(ts, brush, gps);
gps->flag &= ~GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_set(NULL, gps, true);
/* the polygon must be closed, so enabled cyclic */
if (ELEM(tgpi->type, GP_STROKE_BOX, GP_STROKE_CIRCLE)) {
gps->flag |= GP_STROKE_CYCLIC;
diff --git a/source/blender/editors/gpencil/gpencil_select.c b/source/blender/editors/gpencil/gpencil_select.c
index d1d8abd6775..fd5c5bb5346 100644
--- a/source/blender/editors/gpencil/gpencil_select.c
+++ b/source/blender/editors/gpencil/gpencil_select.c
@@ -156,6 +156,11 @@ static bool gpencil_3d_point_to_screen_space(ARegion *region,
/* helper to deselect all selected strokes/points */
static void deselect_all_selected(bContext *C)
{
+ /* Set selection index to 0. */
+ Object *ob = CTX_data_active_object(C);
+ bGPdata *gpd = ob->data;
+ gpd->select_last_index = 0;
+
CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) {
/* deselect stroke and its points if selected */
if (gps->flag & GP_STROKE_SELECT) {
@@ -169,6 +174,7 @@ static void deselect_all_selected(bContext *C)
/* deselect stroke itself too */
gps->flag &= ~GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_set(NULL, gps, true);
}
/* deselect curve and curve points */
@@ -187,7 +193,7 @@ static void deselect_all_selected(bContext *C)
CTX_DATA_END;
}
-static void select_all_curve_points(bGPDstroke *gps, bGPDcurve *gpc, bool deselect)
+static void select_all_curve_points(bGPdata *gpd, bGPDstroke *gps, bGPDcurve *gpc, bool deselect)
{
for (int i = 0; i < gpc->tot_curve_points; i++) {
bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
@@ -205,10 +211,12 @@ static void select_all_curve_points(bGPDstroke *gps, bGPDcurve *gpc, bool desele
if (deselect == false) {
gpc->flag |= GP_CURVE_SELECT;
gps->flag |= GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_set(gpd, gps, false);
}
else {
gpc->flag &= ~GP_CURVE_SELECT;
gps->flag &= ~GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_set(NULL, gps, true);
}
}
@@ -423,7 +431,7 @@ static int gpencil_select_alternate_exec(bContext *C, wmOperator *op)
BEZT_DESEL_ALL(&gpc_pt->bezt);
}
- BKE_gpencil_curve_sync_selection(gps);
+ BKE_gpencil_curve_sync_selection(gpd, gps);
changed = true;
}
}
@@ -562,6 +570,7 @@ static bool gpencil_select_same_layer(bContext *C)
}
gpc->flag |= GP_CURVE_SELECT;
gps->flag |= GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_set(gpd, gps, false);
changed = true;
}
@@ -578,6 +587,7 @@ static bool gpencil_select_same_layer(bContext *C)
}
gps->flag |= GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_set(gpd, gps, false);
changed = true;
}
@@ -622,6 +632,7 @@ static bool gpencil_select_same_material(bContext *C)
}
gpc->flag |= GP_CURVE_SELECT;
gps->flag |= GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_set(gpd, gps, false);
changed = true;
}
@@ -640,6 +651,7 @@ static bool gpencil_select_same_material(bContext *C)
}
gps->flag |= GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_set(gpd, gps, false);
changed = true;
}
@@ -756,6 +768,8 @@ static int gpencil_select_first_exec(bContext *C, wmOperator *op)
BEZT_SEL_ALL(&gpc->curve_points[0].bezt);
gpc->flag |= GP_CURVE_SELECT;
gps->flag |= GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_set(gpd, gps, false);
+
if ((extend == false) && (gps->totpoints > 1)) {
for (int i = 1; i < gpc->tot_curve_points; i++) {
bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
@@ -769,6 +783,7 @@ static int gpencil_select_first_exec(bContext *C, wmOperator *op)
else {
gps->points->flag |= GP_SPOINT_SELECT;
gps->flag |= GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_set(gpd, gps, false);
/* deselect rest? */
if ((extend == false) && (gps->totpoints > 1)) {
@@ -863,6 +878,7 @@ static int gpencil_select_last_exec(bContext *C, wmOperator *op)
BEZT_SEL_ALL(&gpc->curve_points[gpc->tot_curve_points - 1].bezt);
gpc->flag |= GP_CURVE_SELECT;
gps->flag |= GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_set(gpd, gps, false);
if ((extend == false) && (gps->totpoints > 1)) {
for (int i = 0; i < gpc->tot_curve_points - 1; i++) {
bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
@@ -876,6 +892,7 @@ static int gpencil_select_last_exec(bContext *C, wmOperator *op)
else {
gps->points[gps->totpoints - 1].flag |= GP_SPOINT_SELECT;
gps->flag |= GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_set(gpd, gps, false);
/* deselect rest? */
if ((extend == false) && (gps->totpoints > 1)) {
@@ -1272,10 +1289,12 @@ static bool gpencil_stroke_do_circle_sel(bGPdata *gpd,
if (select) {
pt_active->flag |= GP_SPOINT_SELECT;
gps_active->flag |= GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_set(gpd, gps_active, false);
}
else {
pt_active->flag &= ~GP_SPOINT_SELECT;
gps_active->flag &= ~GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_set(NULL, gps_active, true);
}
changed = true;
/* if stroke mode, don't check more points */
@@ -1314,13 +1333,13 @@ static bool gpencil_stroke_do_circle_sel(bGPdata *gpd,
BKE_gpencil_stroke_editcurve_update(gpd, gpl, gps_active);
gps_active->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
/* Select all curve points. */
- select_all_curve_points(gps_active, gps_active->editcurve, false);
+ select_all_curve_points(gpd, gps_active, gps_active->editcurve, false);
BKE_gpencil_stroke_geometry_update(gpd, gps_active);
changed = true;
}
/* Ensure that stroke selection is in sync with its points. */
- BKE_gpencil_stroke_sync_selection(gps_active);
+ BKE_gpencil_stroke_sync_selection(gpd, gps_active);
return changed;
}
@@ -1338,6 +1357,9 @@ static bool gpencil_do_curve_circle_sel(bContext *C,
{
ARegion *region = CTX_wm_region(C);
View3D *v3d = CTX_wm_view3d(C);
+ Object *ob = CTX_data_active_object(C);
+ bGPdata *gpd = ob->data;
+
const bool only_selected = (v3d->overlay.handle_display == CURVE_HANDLE_SELECTED);
bool hit = false;
@@ -1411,7 +1433,7 @@ static bool gpencil_do_curve_circle_sel(bContext *C,
}
}
- BKE_gpencil_curve_sync_selection(gps);
+ BKE_gpencil_curve_sync_selection(gpd, gps);
return hit;
}
@@ -1631,7 +1653,7 @@ static bool gpencil_stroke_fill_isect_rect(ARegion *region,
#endif
static bool gpencil_generic_curve_select(bContext *C,
- Object *UNUSED(ob),
+ Object *ob,
GPencilTestFn is_inside_fn,
rcti UNUSED(box),
GP_SelectUserData *user_data,
@@ -1640,6 +1662,7 @@ static bool gpencil_generic_curve_select(bContext *C,
{
ARegion *region = CTX_wm_region(C);
View3D *v3d = CTX_wm_view3d(C);
+ bGPdata *gpd = ob->data;
const bool handle_only_selected = (v3d->overlay.handle_display == CURVE_HANDLE_SELECTED);
const bool handle_all = (v3d->overlay.handle_display == CURVE_HANDLE_ALL);
@@ -1771,7 +1794,7 @@ static bool gpencil_generic_curve_select(bContext *C,
}
}
- BKE_gpencil_curve_sync_selection(gps);
+ BKE_gpencil_curve_sync_selection(gpd, gps);
}
GP_EDITABLE_CURVES_END(gps_iter);
@@ -1797,6 +1820,8 @@ static bool gpencil_generic_stroke_select(bContext *C,
/* deselect all strokes first? */
if (SEL_OP_USE_PRE_DESELECT(sel_op) || (GPENCIL_PAINT_MODE(gpd))) {
+ /* Set selection index to 0. */
+ gpd->select_last_index = 0;
CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) {
bGPDspoint *pt;
@@ -1807,6 +1832,7 @@ static bool gpencil_generic_stroke_select(bContext *C,
}
gps->flag &= ~GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_set(NULL, gps, true);
}
CTX_DATA_END;
@@ -1891,13 +1917,13 @@ static bool gpencil_generic_stroke_select(bContext *C,
BKE_gpencil_stroke_editcurve_update(gpd, gpl, gps_active);
gps_active->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
/* Select all curve points. */
- select_all_curve_points(gps_active, gps_active->editcurve, false);
+ select_all_curve_points(gpd, gps_active, gps_active->editcurve, false);
BKE_gpencil_stroke_geometry_update(gpd, gps_active);
changed = true;
}
/* Ensure that stroke selection is in sync with its points */
- BKE_gpencil_stroke_sync_selection(gps_active);
+ BKE_gpencil_stroke_sync_selection(gpd, gps_active);
}
GP_EVALUATED_STROKES_END(gpstroke_iter);
@@ -2313,7 +2339,7 @@ static int gpencil_select_exec(bContext *C, wmOperator *op)
}
/* select all curve points */
if (hit_curve != NULL) {
- select_all_curve_points(hit_stroke, hit_curve, deselect);
+ select_all_curve_points(gpd, hit_stroke, hit_curve, deselect);
}
else {
bGPDspoint *pt;
@@ -2332,9 +2358,11 @@ static int gpencil_select_exec(bContext *C, wmOperator *op)
/* stroke too... */
if (deselect == false) {
hit_stroke->flag |= GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_set(gpd, hit_stroke, false);
}
else {
hit_stroke->flag &= ~GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_set(NULL, hit_stroke, true);
}
}
}
@@ -2346,11 +2374,13 @@ static int gpencil_select_exec(bContext *C, wmOperator *op)
BEZT_SEL_IDX(&hit_curve_point->bezt, hit_curve_handle);
hit_curve->flag |= GP_CURVE_SELECT;
hit_stroke->flag |= GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_set(gpd, hit_stroke, false);
}
else {
/* we're adding selection, so selection must be true */
hit_point->flag |= GP_SPOINT_SELECT;
hit_stroke->flag |= GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_set(gpd, hit_stroke, false);
/* expand selection to segment */
int selectmode;
@@ -2378,14 +2408,14 @@ static int gpencil_select_exec(bContext *C, wmOperator *op)
if (!BEZT_ISSEL_ANY(&hit_curve_point->bezt)) {
hit_curve_point->flag &= ~GP_CURVE_POINT_SELECT;
}
- BKE_gpencil_curve_sync_selection(hit_stroke);
+ BKE_gpencil_curve_sync_selection(gpd, hit_stroke);
}
else {
/* deselect point */
hit_point->flag &= ~GP_SPOINT_SELECT;
/* ensure that stroke is selected correctly */
- BKE_gpencil_stroke_sync_selection(hit_stroke);
+ BKE_gpencil_stroke_sync_selection(gpd, hit_stroke);
}
}
}
@@ -2572,6 +2602,7 @@ static int gpencil_select_vertex_color_exec(bContext *C, wmOperator *op)
if (gps_selected) {
gps->flag |= GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_set(gpd, gps, false);
/* Extend stroke selection. */
if (selectmode == GP_SELECTMODE_STROKE) {
diff --git a/source/blender/editors/gpencil/gpencil_utils.c b/source/blender/editors/gpencil/gpencil_utils.c
index 9b12772bc9b..f9242c5a1a8 100644
--- a/source/blender/editors/gpencil/gpencil_utils.c
+++ b/source/blender/editors/gpencil/gpencil_utils.c
@@ -1167,6 +1167,7 @@ void ED_gpencil_stroke_reproject(Depsgraph *depsgraph,
if (keep_original) {
gps_active = BKE_gpencil_stroke_duplicate(gps, true, true);
gps_active->flag &= ~GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_set(NULL, gps_active, true);
for (i = 0, pt = gps_active->points; i < gps_active->totpoints; i++, pt++) {
pt->flag &= ~GP_SPOINT_SELECT;
}
@@ -1689,6 +1690,10 @@ void ED_gpencil_vgroup_select(bContext *C, Object *ob)
gps->flag |= GP_STROKE_SELECT;
}
}
+
+ if (gps->flag & GP_STROKE_SELECT) {
+ BKE_gpencil_stroke_select_index_set(gpd, gps, false);
+ }
}
}
@@ -2564,6 +2569,9 @@ int ED_gpencil_select_stroke_segment(bGPdata *gpd,
void ED_gpencil_select_toggle_all(bContext *C, int action)
{
+ Object *ob = CTX_data_active_object(C);
+ bGPdata *gpd = ob->data;
+
/* for "toggle", test for existing selected strokes */
if (action == SEL_TOGGLE) {
action = SEL_SELECT;
@@ -2588,6 +2596,9 @@ void ED_gpencil_select_toggle_all(bContext *C, int action)
* NOTE: we limit ourselves to editable layers, since once a layer is "locked/hidden
* nothing should be able to touch it
*/
+ /* Set selection index to 0. */
+ gpd->select_last_index = 0;
+
CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
/* deselect all strokes on all frames */
@@ -2605,6 +2616,7 @@ void ED_gpencil_select_toggle_all(bContext *C, int action)
}
gps->flag &= ~GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_set(NULL, gps, true);
}
}
}
@@ -2642,9 +2654,11 @@ void ED_gpencil_select_toggle_all(bContext *C, int action)
/* Change status of stroke */
if (selected) {
gps->flag |= GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_set(gpd, gps, false);
}
else {
gps->flag &= ~GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_set(NULL, gps, true);
}
}
CTX_DATA_END;
@@ -2666,6 +2680,11 @@ void ED_gpencil_select_curve_toggle_all(bContext *C, int action)
}
if (action == SEL_DESELECT) {
+ /* Set selection index to 0. */
+ Object *ob = CTX_data_active_object(C);
+ bGPdata *gpd = ob->data;
+ gpd->select_last_index = 0;
+
GP_EDITABLE_CURVES_BEGIN(gps_iter, C, gpl, gps, gpc)
{
for (int i = 0; i < gpc->tot_curve_points; i++) {
@@ -2676,6 +2695,7 @@ void ED_gpencil_select_curve_toggle_all(bContext *C, int action)
}
gpc->flag &= ~GP_CURVE_SELECT;
gps->flag &= ~GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_set(NULL, gps, true);
}
GP_EDITABLE_CURVES_END(gps_iter);
}
@@ -2717,10 +2737,12 @@ void ED_gpencil_select_curve_toggle_all(bContext *C, int action)
if (selected) {
gpc->flag |= GP_CURVE_SELECT;
gps->flag |= GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_set(gpd, gps, false);
}
else {
gpc->flag &= ~GP_CURVE_SELECT;
gps->flag &= ~GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_set(NULL, gps, true);
}
}
GP_EDITABLE_STROKES_END(gps_iter);
diff --git a/source/blender/makesdna/DNA_gpencil_types.h b/source/blender/makesdna/DNA_gpencil_types.h
index 835761bf6e9..9d969a29add 100644
--- a/source/blender/makesdna/DNA_gpencil_types.h
+++ b/source/blender/makesdna/DNA_gpencil_types.h
@@ -306,6 +306,10 @@ typedef struct bGPDstroke {
float uv_translation[2];
float uv_scale;
+ /** Stroke selection index.*/
+ int select_index;
+ char _pad4[4];
+
/** Vertex weight data. */
struct MDeformVert *dvert;
void *_pad3;
@@ -701,6 +705,10 @@ typedef struct bGPdata {
/** Keyframe type for onion filter (eBezTriple_KeyframeType plus All option) */
short onion_keytype;
+ /** Stroke selection last index. Used to generate a unique selection index. */
+ int select_last_index;
+ char _pad3[4];
+
bGPgrid grid;
bGPdata_Runtime runtime;
diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h
index 2362686cfce..c7f7e610a1a 100644
--- a/source/blender/makesdna/DNA_scene_types.h
+++ b/source/blender/makesdna/DNA_scene_types.h
@@ -1132,22 +1132,6 @@ typedef enum eGP_vertex_SelectMaskFlag {
/* Settings for GP Interpolation Operators */
typedef struct GP_Interpolate_Settings {
- /** #eGP_Interpolate_SettingsFlag. */
- short flag;
-
- /** #eGP_Interpolate_Type - Interpolation Mode. */
- char type;
- /** #eBezTriple_Easing - Easing mode (if easing equation used). */
- char easing;
-
- /** BEZT_IPO_BACK. */
- float back;
- /** BEZT_IPO_ELASTIC. */
- float amplitude, period;
- /* Step between sequence interpolated frames. */
- int step;
- char _pad[4];
-
/** Custom interpolation curve (for use with GP_IPO_CURVEMAP). */
struct CurveMapping *custom_ipo;
} GP_Interpolate_Settings;
diff --git a/source/blender/makesrna/intern/rna_gpencil.c b/source/blender/makesrna/intern/rna_gpencil.c
index 5437c93ef61..631ab1c4671 100644
--- a/source/blender/makesrna/intern/rna_gpencil.c
+++ b/source/blender/makesrna/intern/rna_gpencil.c
@@ -717,7 +717,7 @@ static void rna_GPencil_stroke_point_select_set(PointerRNA *ptr, const bool valu
}
/* Check if the stroke should be selected or not... */
- BKE_gpencil_stroke_sync_selection(gps);
+ BKE_gpencil_stroke_sync_selection(gpd, gps);
}
}
@@ -933,6 +933,7 @@ static void rna_GPencil_stroke_close(ID *id,
static void rna_GPencil_stroke_select_set(PointerRNA *ptr, const bool value)
{
+ bGPdata *gpd = (bGPdata *)ptr->owner_id;
bGPDstroke *gps = ptr->data;
bGPDspoint *pt;
int i;
@@ -940,9 +941,11 @@ static void rna_GPencil_stroke_select_set(PointerRNA *ptr, const bool value)
/* set new value */
if (value) {
gps->flag |= GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_set(gpd, gps, false);
}
else {
gps->flag &= ~GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_set(NULL, gps, true);
}
/* ensure that the stroke's points are selected in the same way */
@@ -1744,6 +1747,11 @@ static void rna_def_gpencil_stroke(BlenderRNA *brna)
RNA_def_property_ui_text(
prop, "Vertex Fill Color", "Color used to mix with fill color to get final color");
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update");
+
+ /* Selection Index */
+ prop = RNA_def_property(srna, "select_index", PROP_INT, PROP_NONE);
+ RNA_def_property_int_sdna(prop, NULL, "select_index");
+ RNA_def_property_ui_text(prop, "Select Index", "Index of selection used for interpolation");
}
static void rna_def_gpencil_strokes_api(BlenderRNA *brna, PropertyRNA *cprop)
diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c
index 9479d15c2cc..8c01b28df5a 100644
--- a/source/blender/makesrna/intern/rna_scene.c
+++ b/source/blender/makesrna/intern/rna_scene.c
@@ -735,27 +735,6 @@ static void rna_GPencil_update(Main *UNUSED(bmain), Scene *scene, PointerRNA *UN
ED_gpencil_tag_scene_gpencil(scene);
}
-/* Grease Pencil Interpolation settings */
-static char *rna_GPencilInterpolateSettings_path(PointerRNA *UNUSED(ptr))
-{
- return BLI_strdup("tool_settings.gpencil_interpolate");
-}
-
-static void rna_GPencilInterpolateSettings_type_set(PointerRNA *ptr, int value)
-{
- GP_Interpolate_Settings *settings = (GP_Interpolate_Settings *)ptr->data;
-
- /* NOTE: This cast should be fine, as we have a small + finite set of values
- * (#eGP_Interpolate_Type) that should fit well within a char.
- */
- settings->type = (char)value;
-
- /* init custom interpolation curve here now the first time it's used */
- if ((settings->type == GP_IPO_CURVEMAP) && (settings->custom_ipo == NULL)) {
- settings->custom_ipo = BKE_curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f);
- }
-}
-
static void rna_Gpencil_extend_selection(bContext *C, PointerRNA *UNUSED(ptr))
{
/* Extend selection to all points in all selected strokes. */
@@ -2688,69 +2667,10 @@ static void rna_def_gpencil_interpolate(BlenderRNA *brna)
srna = RNA_def_struct(brna, "GPencilInterpolateSettings", NULL);
RNA_def_struct_sdna(srna, "GP_Interpolate_Settings");
- RNA_def_struct_path_func(srna, "rna_GPencilInterpolateSettings_path");
RNA_def_struct_ui_text(srna,
"Grease Pencil Interpolate Settings",
"Settings for Grease Pencil interpolation tools");
- /* flags */
- prop = RNA_def_property(srna, "interpolate_all_layers", PROP_BOOLEAN, PROP_NONE);
- RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_TOOLFLAG_INTERPOLATE_ALL_LAYERS);
- RNA_def_property_ui_text(
- prop, "Interpolate All Layers", "Interpolate all layers, not only active");
- RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL);
-
- prop = RNA_def_property(srna, "interpolate_selected_only", PROP_BOOLEAN, PROP_NONE);
- RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_TOOLFLAG_INTERPOLATE_ONLY_SELECTED);
- RNA_def_property_ui_text(prop,
- "Interpolate Selected Strokes",
- "Interpolate only selected strokes in the original frame");
- RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL);
-
- /* interpolation type */
- prop = RNA_def_property(srna, "type", PROP_ENUM, PROP_NONE);
- RNA_def_property_enum_sdna(prop, NULL, "type");
- RNA_def_property_enum_items(prop, rna_enum_gpencil_interpolation_mode_items);
- RNA_def_property_enum_funcs(prop, NULL, "rna_GPencilInterpolateSettings_type_set", NULL);
- RNA_def_property_ui_text(
- prop, "Type", "Interpolation method to use the next time 'Interpolate Sequence' is run");
- RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL);
-
- prop = RNA_def_property(srna, "step", PROP_INT, PROP_NONE);
- RNA_def_property_range(prop, 1, MAXFRAME);
- RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
- RNA_def_property_ui_text(prop, "Step", "Number of frames between generated interpolated frames");
- RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL);
-
- /* easing */
- prop = RNA_def_property(srna, "easing", PROP_ENUM, PROP_NONE);
- RNA_def_property_enum_sdna(prop, NULL, "easing");
- RNA_def_property_enum_items(prop, rna_enum_beztriple_interpolation_easing_items);
- RNA_def_property_ui_text(
- prop,
- "Easing",
- "Which ends of the segment between the preceding and following grease pencil frames "
- "easing interpolation is applied to");
- RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL);
-
- /* easing options */
- prop = RNA_def_property(srna, "back", PROP_FLOAT, PROP_NONE);
- RNA_def_property_float_sdna(prop, NULL, "back");
- RNA_def_property_ui_text(prop, "Back", "Amount of overshoot for 'back' easing");
- RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL);
-
- prop = RNA_def_property(srna, "amplitude", PROP_FLOAT, PROP_NONE);
- RNA_def_property_float_sdna(prop, NULL, "amplitude");
- RNA_def_property_range(prop, 0.0f, FLT_MAX); /* only positive values... */
- RNA_def_property_ui_text(
- prop, "Amplitude", "Amount to boost elastic bounces for 'elastic' easing");
- RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL);
-
- prop = RNA_def_property(srna, "period", PROP_FLOAT, PROP_NONE);
- RNA_def_property_float_sdna(prop, NULL, "period");
- RNA_def_property_ui_text(prop, "Period", "Time between bounces for elastic easing");
- RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL);
-
/* custom curvemap */
prop = RNA_def_property(srna, "interpolation_curve", PROP_POINTER, PROP_NONE);
RNA_def_property_pointer_sdna(prop, NULL, "custom_ipo");
@@ -2759,7 +2679,6 @@ static void rna_def_gpencil_interpolate(BlenderRNA *brna)
prop,
"Interpolation Curve",
"Custom curve to control 'sequence' interpolation between Grease Pencil frames");
- RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL);
}
static void rna_def_transform_orientation(BlenderRNA *brna)