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.py62
-rw-r--r--release/scripts/startup/bl_app_templates_system/2D_Animation/__init__.py2
-rw-r--r--release/scripts/startup/bl_ui/properties_data_gpencil.py5
-rw-r--r--release/scripts/startup/bl_ui/properties_grease_pencil_common.py9
-rw-r--r--release/scripts/startup/bl_ui/space_toolsystem_toolbar.py19
-rw-r--r--release/scripts/startup/bl_ui/space_view3d.py50
-rw-r--r--release/scripts/startup/bl_ui/space_view3d_toolbar.py3
-rw-r--r--source/blender/blenkernel/BKE_gpencil.h1
-rw-r--r--source/blender/blenkernel/BKE_gpencil_curve.h61
-rw-r--r--source/blender/blenkernel/BKE_gpencil_geom.h70
-rw-r--r--source/blender/blenkernel/BKE_gpencil_modifier.h15
-rw-r--r--source/blender/blenkernel/intern/armature_deform.c41
-rw-r--r--source/blender/blenkernel/intern/brush.c29
-rw-r--r--source/blender/blenkernel/intern/gpencil.c147
-rw-r--r--source/blender/blenkernel/intern/gpencil_curve.c1619
-rw-r--r--source/blender/blenkernel/intern/gpencil_geom.c273
-rw-r--r--source/blender/blenkernel/intern/gpencil_modifier.c24
-rw-r--r--source/blender/blenloader/intern/versioning_280.c2
-rw-r--r--source/blender/blenloader/intern/versioning_290.c7
-rw-r--r--source/blender/blenloader/intern/versioning_300.c13
-rw-r--r--source/blender/draw/engines/overlay/overlay_gpencil.c41
-rw-r--r--source/blender/draw/engines/overlay/shaders/edit_gpencil_vert.glsl15
-rw-r--r--source/blender/draw/intern/draw_cache_impl_gpencil.c21
-rw-r--r--source/blender/editors/gpencil/CMakeLists.txt1
-rw-r--r--source/blender/editors/gpencil/gpencil_add_monkey.c56
-rw-r--r--source/blender/editors/gpencil/gpencil_add_stroke.c2
-rw-r--r--source/blender/editors/gpencil/gpencil_bake_animation.c2
-rw-r--r--source/blender/editors/gpencil/gpencil_convert.c1
-rw-r--r--source/blender/editors/gpencil/gpencil_curve_draw.c845
-rw-r--r--source/blender/editors/gpencil/gpencil_data.c44
-rw-r--r--source/blender/editors/gpencil/gpencil_edit.c2140
-rw-r--r--source/blender/editors/gpencil/gpencil_edit_curve.c241
-rw-r--r--source/blender/editors/gpencil/gpencil_fill.c2
-rw-r--r--source/blender/editors/gpencil/gpencil_intern.h15
-rw-r--r--source/blender/editors/gpencil/gpencil_interpolate.c8
-rw-r--r--source/blender/editors/gpencil/gpencil_merge.c2
-rw-r--r--source/blender/editors/gpencil/gpencil_ops.c43
-rw-r--r--source/blender/editors/gpencil/gpencil_paint.c53
-rw-r--r--source/blender/editors/gpencil/gpencil_primitive.c6
-rw-r--r--source/blender/editors/gpencil/gpencil_sculpt_paint.c28
-rw-r--r--source/blender/editors/gpencil/gpencil_select.c1487
-rw-r--r--source/blender/editors/gpencil/gpencil_trace_utils.c2
-rw-r--r--source/blender/editors/gpencil/gpencil_utils.c315
-rw-r--r--source/blender/editors/gpencil/gpencil_uv.c8
-rw-r--r--source/blender/editors/gpencil/gpencil_vertex_ops.c33
-rw-r--r--source/blender/editors/gpencil/gpencil_vertex_paint.c130
-rw-r--r--source/blender/editors/gpencil/gpencil_weight_paint.c90
-rw-r--r--source/blender/editors/include/ED_gpencil.h6
-rw-r--r--source/blender/editors/screen/area.c4
-rw-r--r--source/blender/editors/transform/transform_convert_gpencil.c718
-rw-r--r--source/blender/editors/transform/transform_gizmo_3d.c7
-rw-r--r--source/blender/editors/transform/transform_mode_gpopacity.c12
-rw-r--r--source/blender/editors/transform/transform_mode_gpshrinkfatten.c12
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c12
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencilarmature.c86
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencilarray.c57
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencilbuild.c5
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencilcolor.c29
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencilhook.c165
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencillattice.c96
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencillength.c17
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c3
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencilmirror.c42
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencilmultiply.c5
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencilnoise.c17
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpenciloffset.c166
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencilopacity.c29
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencilsimplify.c17
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencilsmooth.c72
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencilsubdiv.c17
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpenciltexture.c31
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencilthick.c105
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpenciltime.c3
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpenciltint.c29
-rw-r--r--source/blender/gpencil_modifiers/intern/MOD_gpencilweight.c29
-rw-r--r--source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c2
-rw-r--r--source/blender/io/gpencil/intern/gpencil_io_import_svg.cc2
-rw-r--r--source/blender/makesdna/DNA_brush_enums.h4
-rw-r--r--source/blender/makesdna/DNA_gpencil_types.h42
-rw-r--r--source/blender/makesdna/DNA_scene_defaults.h3
-rw-r--r--source/blender/makesdna/DNA_scene_types.h21
-rw-r--r--source/blender/makesdna/intern/dna_defaults.c1
-rw-r--r--source/blender/makesrna/intern/rna_brush.c12
-rw-r--r--source/blender/makesrna/intern/rna_gpencil.c99
-rw-r--r--source/blender/makesrna/intern/rna_scene.c21
85 files changed, 6801 insertions, 3280 deletions
diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py
index 0af7493ed47..b76afc6f35f 100644
--- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py
+++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py
@@ -3316,7 +3316,7 @@ def km_grease_pencil_stroke_edit_mode(params):
# Separate
("gpencil.stroke_separate", {"type": 'P', "value": 'PRESS'}, None),
# Split and joint strokes
- ("gpencil.stroke_split", {"type": 'V', "value": 'PRESS'}, None),
+ ("gpencil.stroke_split", {"type": 'V', "value": 'PRESS', "shift": True}, None),
("gpencil.stroke_join", {"type": 'J', "value": 'PRESS', "ctrl": True}, None),
("gpencil.stroke_join", {"type": 'J', "value": 'PRESS', "shift": True, "ctrl": True},
{"properties": [("type", 'JOINCOPY')]}),
@@ -3361,9 +3361,6 @@ def km_grease_pencil_stroke_edit_mode(params):
# Proportional editing.
*_template_items_proportional_editing(
params, connected=True, toggle_data_path='tool_settings.use_proportional_edit'),
- # Curve edit mode toggle.
- ("wm.context_toggle", {"type": 'U', "value": 'PRESS'},
- {"properties": [("data_path", 'gpencil_data.use_curve_edit')]}),
# Add menu
("object.gpencil_add", {"type": 'A', "value": 'PRESS', "shift": True}, None),
# Vertex group menu
@@ -3381,6 +3378,8 @@ def km_grease_pencil_stroke_edit_mode(params):
op_menu("VIEW3D_MT_gpencil_animation", {"type": 'I', "value": 'PRESS'}),
# Context menu
*_template_items_context_menu("VIEW3D_MT_gpencil_edit_context_menu", params.context_menu_event),
+ # Set handle type
+ ("gpencil.stroke_editcurve_set_handle_type", {"type": 'V', "value": 'PRESS'}, None),
])
if params.legacy:
@@ -3391,21 +3390,6 @@ def km_grease_pencil_stroke_edit_mode(params):
return keymap
-def km_grease_pencil_stroke_curve_edit_mode(_params):
- items = []
- keymap = (
- "Grease Pencil Stroke Curve Edit Mode",
- {"space_type": 'EMPTY', "region_type": 'WINDOW'},
- {"items": items},
- )
-
- items.extend([
- # Set handle type
- ("gpencil.stroke_editcurve_set_handle_type", {"type": 'V', "value": 'PRESS'}, None),
- ])
-
- return keymap
-
def km_grease_pencil_stroke_paint_mode(params):
items = []
keymap = (
@@ -5747,6 +5731,33 @@ def km_generic_gizmo_tweak_modal_map(_params):
return keymap
+def km_gpencil_curve_draw_modal_map(_params):
+ items = []
+ keymap = (
+ "Curve Draw Tool Modal Map",
+ {"space_type": 'EMPTY', "region_type": 'WINDOW', "modal": True},
+ {"items": items},
+ )
+
+ items.extend([
+ ("CANCEL", {"type": 'ESC', "value": 'PRESS', "any": True}, None),
+ ("CANCEL", {"type": 'RIGHTMOUSE', "value": 'PRESS', "any": True}, None),
+ ("CONFIRM", {"type": 'SPACE', "value": 'PRESS', "any": True}, None),
+ ("CONFIRM", {"type": 'RET', "value": 'PRESS', "any": True}, None),
+ ("CONFIRM", {"type": 'NUMPAD_ENTER', "value": 'PRESS', "any": True}, None),
+ ("CONFIRM", {"type": 'MIDDLEMOUSE', "value": 'PRESS', "any": True}, None),
+ ("FREE_HANDLE_ON", {"type": 'RIGHT_ALT', "value": 'PRESS', "any": True}, None),
+ ("FREE_HANDLE_OFF", {"type": 'RIGHT_ALT', "value": 'RELEASE', "any": True}, None),
+ ("FREE_HANDLE_ON", {"type": 'LEFT_ALT', "value": 'PRESS', "any": True}, None),
+ ("FREE_HANDLE_OFF", {"type": 'LEFT_ALT', "value": 'RELEASE', "any": True}, None),
+ ("CYCLIC_TOGGLE", {"type": 'C', "value": 'PRESS', "any": True}, None),
+ ("DELETE_LAST", {"type": 'X', "value": 'PRESS', "any": True}, None),
+ ("SET_THICKNESS", {"type": 'F', "value": 'PRESS', "any": True}, None),
+ ])
+
+ return keymap
+
+
# ------------------------------------------------------------------------------
# Popup Keymaps
@@ -6854,6 +6865,15 @@ def km_3d_view_tool_paint_gpencil_eyedropper(params):
]},
)
+def km_3d_view_tool_paint_gpencil_curve_pen(params):
+ return (
+ "3D View Tool: Paint Gpencil, Bézier Pen",
+ {"space_type": 'VIEW_3D', "region_type": 'WINDOW'},
+ {"items": [
+ ("gpencil.draw_curve", {"type": params.tool_mouse, "value": 'PRESS'}, {"properties":[]}),
+ ]},
+ )
+
def km_3d_view_tool_paint_gpencil_interpolate(params):
return (
"3D View Tool: Paint Gpencil, Interpolate",
@@ -6864,6 +6884,7 @@ def km_3d_view_tool_paint_gpencil_interpolate(params):
]},
)
+
def km_3d_view_tool_edit_gpencil_select(params):
return (
"3D View Tool: Edit Gpencil, Tweak",
@@ -7119,7 +7140,6 @@ def generate_keymaps(params=None):
# Modes.
km_grease_pencil(params),
- km_grease_pencil_stroke_curve_edit_mode(params),
km_grease_pencil_stroke_edit_mode(params),
km_grease_pencil_stroke_paint_mode(params),
km_grease_pencil_stroke_paint_draw_brush(params),
@@ -7184,6 +7204,7 @@ def generate_keymaps(params=None):
km_view3d_dolly_modal(params),
km_paint_stroke_modal(params),
km_sculpt_expand_modal(params),
+ km_gpencil_curve_draw_modal_map(params),
# Gizmos.
km_generic_gizmo(params),
@@ -7292,6 +7313,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_curve_pen(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),
diff --git a/release/scripts/startup/bl_app_templates_system/2D_Animation/__init__.py b/release/scripts/startup/bl_app_templates_system/2D_Animation/__init__.py
index 40dd0729fec..bcaea40e99c 100644
--- a/release/scripts/startup/bl_app_templates_system/2D_Animation/__init__.py
+++ b/release/scripts/startup/bl_app_templates_system/2D_Animation/__init__.py
@@ -59,6 +59,8 @@ def load_handler(_):
scene = bpy.data.scenes[0]
if scene:
scene.tool_settings.use_keyframe_insert_auto = True
+ scene.tool_settings.gpencil_curve_fit_threshold = 0.1
+ scene.tool_settings.gpencil_curve_fit_corner_angle = 1.57079632679489661923
for ob in scene.objects:
if ob.type == 'GPENCIL':
gpd = ob.data
diff --git a/release/scripts/startup/bl_ui/properties_data_gpencil.py b/release/scripts/startup/bl_ui/properties_data_gpencil.py
index b273eee4e19..549f50f1300 100644
--- a/release/scripts/startup/bl_ui/properties_data_gpencil.py
+++ b/release/scripts/startup/bl_ui/properties_data_gpencil.py
@@ -380,7 +380,10 @@ class DATA_PT_gpencil_strokes(DataButtonsPanel, Panel):
sub.active = gpd.stroke_thickness_space == 'WORLDSPACE'
sub.prop(gpd, "pixel_factor", text="Thickness Scale")
- col.prop(gpd, "edit_curve_resolution")
+ col.separator()
+
+ col.prop(gpd, "edit_curve_resolution", text="Bézier Stroke Resolution")
+ col.prop(gpd, "use_adaptive_curve_resolution", text="Adaptive")
class DATA_PT_gpencil_display(DataButtonsPanel, Panel):
diff --git a/release/scripts/startup/bl_ui/properties_grease_pencil_common.py b/release/scripts/startup/bl_ui/properties_grease_pencil_common.py
index de743033036..3e85bf2adfd 100644
--- a/release/scripts/startup/bl_ui/properties_grease_pencil_common.py
+++ b/release/scripts/startup/bl_ui/properties_grease_pencil_common.py
@@ -365,6 +365,14 @@ class GPENCIL_MT_cleanup(Menu):
if ob.mode != 'PAINT_GPENCIL':
layout.operator("gpencil.reproject")
+class GPENCIL_MT_stroke_type(Menu):
+ bl_label = "Set Stroke Type"
+
+ def draw(self, contect):
+ layout = self.layout
+ layout.operator("gpencil.stroke_set_type", text="Poly", icon='OUTLINER_DATA_GREASEPENCIL').type = 'POLY'
+ layout.operator("gpencil.stroke_set_type", text="Bézier", icon='HANDLE_ALIGNED').type = 'BEZIER'
+
class GPENCIL_UL_annotation_layer(UIList):
def draw_item(self, _context, layout, _data, item, icon, _active_data, _active_propname, _index):
@@ -923,6 +931,7 @@ classes = (
GPENCIL_MT_move_to_layer,
GPENCIL_MT_layer_active,
GPENCIL_MT_material_active,
+ GPENCIL_MT_stroke_type,
GPENCIL_MT_gpencil_draw_delete,
GPENCIL_MT_layer_mask_menu,
diff --git a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py
index c55f637f8b2..dc6ce05371a 100644
--- a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py
+++ b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py
@@ -2042,6 +2042,21 @@ class _defs_gpencil_paint:
keymap=(),
draw_settings=draw_settings,
)
+
+ @ToolDef.from_fn
+ def curve_pen():
+ def draw_settings(context, layout, tool):
+ pass
+
+ return dict(
+ idname="builtin.curve_pen",
+ label="Bézier Pen",
+ icon="ops.curve.draw",
+ cursor='DOT',
+ widget=None,
+ keymap=(),
+ draw_settings=draw_settings,
+ )
@ToolDef.from_fn
def interpolate():
@@ -2057,9 +2072,6 @@ class _defs_gpencil_paint:
label="Interpolate",
icon="ops.pose.breakdowner",
cursor='DEFAULT',
- widget=None,
- keymap=(),
- draw_settings=draw_settings,
)
@@ -2910,6 +2922,7 @@ class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel):
_defs_view3d_generic.cursor,
None,
_defs_gpencil_paint.generate_from_brushes,
+ _defs_gpencil_paint.curve_pen,
_defs_gpencil_paint.cutter,
None,
_defs_gpencil_paint.eyedropper,
diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py
index df41445ee6f..98033cc2608 100644
--- a/release/scripts/startup/bl_ui/space_view3d.py
+++ b/release/scripts/startup/bl_ui/space_view3d.py
@@ -660,26 +660,24 @@ class VIEW3D_HT_header(Header):
sub.separator(factor=0.4)
sub.prop(tool_settings, "use_gpencil_draw_additive", text="", icon='FREEZE')
+ # Curve fit
+ row = layout.row(align=True)
+ row.prop(tool_settings, "use_gpencil_bezier_mode", text="",
+ icon='IPO_BEZIER')
+ sub = row.row(align=True)
+ sub.active = tool_settings.use_gpencil_bezier_mode
+ sub.popover(
+ panel="VIEW3D_PT_gpencil_curve_fit",
+ text="Curve Fit",
+ )
+
# Select mode for Editing
if gpd.use_stroke_edit_mode:
row = layout.row(align=True)
row.prop_enum(tool_settings, "gpencil_selectmode_edit", text="", value='POINT')
row.prop_enum(tool_settings, "gpencil_selectmode_edit", text="", value='STROKE')
+ row.prop_enum(tool_settings, "gpencil_selectmode_edit", text="", value='SEGMENT')
- subrow = row.row(align=True)
- subrow.enabled = not gpd.use_curve_edit
- subrow.prop_enum(tool_settings, "gpencil_selectmode_edit", text="", value='SEGMENT')
-
- # Curve edit submode
- row = layout.row(align=True)
- row.prop(gpd, "use_curve_edit", text="",
- icon='IPO_BEZIER')
- sub = row.row(align=True)
- sub.active = gpd.use_curve_edit
- sub.popover(
- panel="VIEW3D_PT_gpencil_curve_edit",
- text="Curve Editing",
- )
# Select mode for Sculpt
if gpd.is_stroke_sculpt_mode:
@@ -5009,6 +5007,7 @@ class VIEW3D_MT_edit_gpencil_stroke(Menu):
layout.operator_menu_enum("gpencil.stroke_caps_set", text="Toggle Caps", property="type")
layout.operator("gpencil.stroke_flip", text="Switch Direction")
layout.prop(settings, "use_scale_thickness", text="Scale Thickness")
+ layout.menu("GPENCIL_MT_stroke_type")
layout.separator()
layout.operator("gpencil.stroke_normalize", text="Normalize Thickness").mode = 'THICKNESS'
@@ -6830,8 +6829,7 @@ class VIEW3D_PT_overlay_gpencil_options(Panel):
# Handles for Curve Edit
if context.object.mode == 'EDIT_GPENCIL':
gpd = context.object.data
- if gpd.use_curve_edit:
- layout.prop(overlay, "display_handle", text="Handles")
+ layout.prop(overlay, "display_handle", text="Handles")
if context.object.mode in {'PAINT_GPENCIL', 'VERTEX_GPENCIL'}:
layout.label(text="Vertex Paint")
@@ -6996,21 +6994,19 @@ class VIEW3D_PT_gpencil_multi_frame(Panel):
layout.template_curve_mapping(settings, "multiframe_falloff_curve", brush=True)
-# Grease Pencil Object - Curve Editing tools
-class VIEW3D_PT_gpencil_curve_edit(Panel):
+# Grease Pencil Object - Curve Editing settings
+class VIEW3D_PT_gpencil_curve_fit(Panel):
bl_space_type = 'VIEW_3D'
bl_region_type = 'HEADER'
- bl_label = "Curve Editing"
+ bl_label = "Curve Fit"
def draw(self, context):
layout = self.layout
- gpd = context.gpencil_data
+ tool_settings = context.tool_settings
col = layout.column(align=True)
- col.prop(gpd, "edit_curve_resolution")
- col.prop(gpd, "curve_edit_threshold")
- col.prop(gpd, "curve_edit_corner_angle")
- col.prop(gpd, "use_adaptive_curve_resolution")
+ col.prop(tool_settings, "gpencil_curve_fit_threshold")
+ col.prop(tool_settings, "gpencil_curve_fit_corner_angle")
class VIEW3D_MT_gpencil_edit_context_menu(Menu):
@@ -7109,6 +7105,10 @@ class VIEW3D_MT_gpencil_edit_context_menu(Menu):
col.separator()
+ col.menu("GPENCIL_MT_stroke_type")
+
+ col.separator()
+
# Duplicate operators
col.operator("gpencil.duplicate_move", text="Duplicate")
col.operator("gpencil.copy", text="Copy", icon='COPYDOWN')
@@ -7606,6 +7606,7 @@ classes = (
VIEW3D_MT_gpencil_animation,
VIEW3D_MT_gpencil_simplify,
VIEW3D_MT_gpencil_autoweights,
+ VIEW3D_PT_gpencil_curve_fit,
VIEW3D_MT_gpencil_edit_context_menu,
VIEW3D_MT_edit_curve,
VIEW3D_MT_edit_curve_ctrlpoints,
@@ -7653,7 +7654,6 @@ classes = (
VIEW3D_PT_grease_pencil,
VIEW3D_PT_annotation_onion,
VIEW3D_PT_gpencil_multi_frame,
- VIEW3D_PT_gpencil_curve_edit,
VIEW3D_PT_quad_view,
VIEW3D_PT_view3d_stereo,
VIEW3D_PT_shading,
diff --git a/release/scripts/startup/bl_ui/space_view3d_toolbar.py b/release/scripts/startup/bl_ui/space_view3d_toolbar.py
index 46fed79332d..c8f81940cfe 100644
--- a/release/scripts/startup/bl_ui/space_view3d_toolbar.py
+++ b/release/scripts/startup/bl_ui/space_view3d_toolbar.py
@@ -1464,6 +1464,9 @@ class VIEW3D_PT_tools_grease_pencil_brush_advanced(View3DPanel, Panel):
subcol.enabled = False
subcol.prop(gp_settings, "aspect")
+ col.separator()
+ col.prop(gp_settings, "use_curve_data")
+
elif brush.gpencil_tool == 'FILL':
row = col.row(align=True)
row.prop(gp_settings, "fill_draw_mode", text="Boundary")
diff --git a/source/blender/blenkernel/BKE_gpencil.h b/source/blender/blenkernel/BKE_gpencil.h
index c11c34cb312..449750e2944 100644
--- a/source/blender/blenkernel/BKE_gpencil.h
+++ b/source/blender/blenkernel/BKE_gpencil.h
@@ -266,6 +266,7 @@ bool BKE_gpencil_stroke_select_check(const struct bGPDstroke *gps);
void BKE_gpencil_dvert_ensure(struct bGPDstroke *gps);
void BKE_gpencil_vgroup_remove(struct Object *ob, struct bDeformGroup *defgroup);
void BKE_gpencil_stroke_weights_duplicate(struct bGPDstroke *gps_src, struct bGPDstroke *gps_dst);
+void BKE_gpencil_editcurve_weights_duplicate(struct bGPDcurve *gpc_src, struct bGPDcurve *gpc_dst);
/* Set active frame by layer. */
void BKE_gpencil_frame_active_set(struct Depsgraph *depsgraph, struct bGPdata *gpd);
diff --git a/source/blender/blenkernel/BKE_gpencil_curve.h b/source/blender/blenkernel/BKE_gpencil_curve.h
index 9cbe67af9c1..5672185086f 100644
--- a/source/blender/blenkernel/BKE_gpencil_curve.h
+++ b/source/blender/blenkernel/BKE_gpencil_curve.h
@@ -33,7 +33,10 @@ struct Scene;
struct bGPDcurve;
struct bGPDlayer;
struct bGPDstroke;
+struct bGPDcurve;
+struct bGPDcurve_point;
struct bGPdata;
+enum eGPStrokeGeoUpdateFlag;
void BKE_gpencil_convert_curve(struct Main *bmain,
struct Scene *scene,
@@ -45,25 +48,63 @@ void BKE_gpencil_convert_curve(struct Main *bmain,
struct bGPDcurve *BKE_gpencil_stroke_editcurve_generate(struct bGPDstroke *gps,
const float error_threshold,
- const float corner_angle,
- const float stroke_radius);
-void BKE_gpencil_stroke_editcurve_update(struct bGPdata *gpd,
- struct bGPDlayer *gpl,
- struct bGPDstroke *gps);
+ const float corner_angle);
+struct bGPDcurve *BKE_gpencil_stroke_editcurve_tagged_segments_update(struct bGPDstroke *gps,
+ const float error_threshold,
+ const float corner_angle);
+void BKE_gpencil_stroke_editcurve_regenerate_single(struct bGPDstroke *gps,
+ uint32_t start_idx,
+ uint32_t end_idx,
+ const float error_threshold);
+void BKE_gpencil_stroke_refit_curve(struct bGPDstroke *gps,
+ const float threshold,
+ const float corner_angle,
+ const enum eGPStrokeGeoUpdateFlag flag);
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,
const uint resolution,
- const bool is_adaptive);
-void BKE_gpencil_editcurve_recalculate_handles(struct bGPDstroke *gps);
+ const bool is_adaptive,
+ const enum eGPStrokeGeoUpdateFlag flag);
+bool BKE_gpencil_editcurve_recalculate_handles(struct bGPDstroke *gps);
void BKE_gpencil_editcurve_subdivide(struct bGPDstroke *gps, const int cuts);
-
+int BKE_gpencil_editcurve_dissolve(struct bGPDstroke *gps,
+ const uint flag,
+ const bool refit_segments,
+ const float error_threshold);
+void BKE_gpencil_editcurve_simplify_adaptive(struct bGPDstroke *gps, const float threshold);
+void BKE_gpencil_editcurve_simplify_fixed(struct bGPDstroke *gps, const int count);
+void BKE_gpencil_editcurve_smooth_ex(struct bGPDstroke *gps,
+ const float factor,
+ const uint step_size,
+ const uint repeat,
+ const bool only_selected,
+ const bool affect_endpoints,
+ const bool use_vertex_groups,
+ const bool invert_weights,
+ const int deform_group,
+ const CurveMapping *curve_mapping,
+ const bool do_positions,
+ const bool do_pressure,
+ const bool do_strength);
+void BKE_gpencil_editcurve_smooth(struct bGPDstroke *gps,
+ const float factor,
+ const uint step_size,
+ const uint repeat,
+ const bool only_selected,
+ const bool affect_endpoints,
+ const bool do_positions,
+ const bool do_pressure,
+ const bool do_strength);
+bool BKE_gpencil_editcurve_merge_distance(struct bGPDstroke *gps,
+ const float threshold,
+ const bool use_unselected,
+ const bool refit_segments,
+ const float error_threshold);
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/blenkernel/BKE_gpencil_geom.h b/source/blender/blenkernel/BKE_gpencil_geom.h
index c1ccae7a437..1e9c9d8ba01 100644
--- a/source/blender/blenkernel/BKE_gpencil_geom.h
+++ b/source/blender/blenkernel/BKE_gpencil_geom.h
@@ -38,6 +38,71 @@ struct bGPDspoint;
struct bGPDstroke;
struct bGPdata;
+typedef enum eGPStrokeGeoUpdateFlag {
+ /* Default geometry update. Triangulate the stroke, update UVs and bounding box. If the stroke
+ type is bezier, regenerate the polyline first (GP_GEO_UPDATE_POLYLINE_REGENERATE_ALL). */
+ GP_GEO_UPDATE_DEFAULT = 0,
+
+ /* == Curve refitting flags == */
+ /* On a stroke geometry update, if the stroke is of type bézier, there is the option to use the
+ points in the polyline to do a curve fitting. This is useful when an operation writes to the
+ polyline and the shape of the curve is out of sync and needs to be refitted. These flags
+ control what attributes the curve should be fitted to. */
+
+ /* Refit the curve point positions. */
+ GP_GEO_UPDATE_CURVE_REFIT_POSITION = (1 << 1),
+ /* Refit the curve point pressures. */
+ GP_GEO_UPDATE_CURVE_REFIT_PRESSURE = (1 << 2),
+ /* Refit the curve point strengths. */
+ GP_GEO_UPDATE_CURVE_REFIT_STRENGTH = (1 << 3),
+ /* Refit the curve point vertex colors. */
+ GP_GEO_UPDATE_CURVE_REFIT_COLOR = (1 << 4),
+ /* Refit the curve point weights. */
+ GP_GEO_UPDATE_CURVE_REFIT_WEIGHT = (1 << 5),
+ /* Do a partial refit. Uses the `GP_SPOINT_TAG` point flag to determin what curve segments need
+ to be refitted. Only affected curve segments will be updated. */
+ GP_GEO_UPDATE_CURVE_PARTIAL_REFIT = (1 << 6),
+
+ /* == Polyline regeneration flags == */
+ /* The polyline is regenerated when the curve geometry is updated. This is because the polyline
+ is used for rendering instead of the actual curve data. These flag control what attributes
+ should be regenerated when the curve was updated. */
+
+ /* Regenerate the polyline positions from the curve data. */
+ GP_GEO_UPDATE_POLYLINE_POSITION = (1 << 7),
+ /* Regenerate the polyline point pressure from the curve data. */
+ GP_GEO_UPDATE_POLYLINE_PRESSURE = (1 << 8),
+ /* Regenerate the polyline point strength from the curve data. */
+ GP_GEO_UPDATE_POLYLINE_STRENGTH = (1 << 9),
+ /* Regenerate the polyline vertex colors from the curve data. */
+ GP_GEO_UPDATE_POLYLINE_COLOR = (1 << 10),
+ /* Regenerate the polyline weights from the curve data. */
+ GP_GEO_UPDATE_POLYLINE_WEIGHT = (1 << 11),
+
+ /* Add additional flags here: (1 << 12), (2 << 12), ... */
+ /* GP_GEO_UPDATE_XXX = (1 << 12), */
+} eGPStrokeGeoUpdateFlag;
+
+/* Refit all attributes. */
+#define GP_GEO_UPDATE_CURVE_REFIT_ALL \
+ (GP_GEO_UPDATE_CURVE_REFIT_POSITION | GP_GEO_UPDATE_CURVE_REFIT_PRESSURE | \
+ GP_GEO_UPDATE_CURVE_REFIT_STRENGTH | GP_GEO_UPDATE_CURVE_REFIT_COLOR | \
+ GP_GEO_UPDATE_CURVE_REFIT_WEIGHT)
+
+/* Check if any curve refitting is done. */
+#define GP_GEO_UPDATE_CURVE_REFIT_ANY(flag) (flag & GP_GEO_UPDATE_CURVE_REFIT_ALL)
+
+/* Regenerate all attributes of the polyline from the curve data. */
+#define GP_GEO_UPDATE_POLYLINE_REGENERATE_ALL \
+ (GP_GEO_UPDATE_POLYLINE_POSITION | GP_GEO_UPDATE_POLYLINE_PRESSURE | \
+ GP_GEO_UPDATE_POLYLINE_STRENGTH | GP_GEO_UPDATE_POLYLINE_COLOR | \
+ GP_GEO_UPDATE_POLYLINE_WEIGHT)
+
+/* Check if any atttributes of the polyline need to be regenerated. Note that we update all
+ * attributes by default (GP_GEO_UPDATE_DEFAULT). */
+#define GP_GEO_UPDATE_POLYLINE_REGENERATE_ANY(flag) \
+ ((flag & GP_GEO_UPDATE_POLYLINE_REGENERATE_ALL) || flag == GP_GEO_UPDATE_DEFAULT)
+
/* Object boundbox. */
bool BKE_gpencil_data_minmax(const struct bGPdata *gpd, float r_min[3], float r_max[3]);
bool BKE_gpencil_stroke_minmax(const struct bGPDstroke *gps,
@@ -78,7 +143,10 @@ void BKE_gpencil_stroke_2d_flat_ref(const struct bGPDspoint *ref_points,
const float scale,
int *r_direction);
void BKE_gpencil_stroke_fill_triangulate(struct bGPDstroke *gps);
-void BKE_gpencil_stroke_geometry_update(struct bGPdata *gpd, struct bGPDstroke *gps);
+
+void BKE_gpencil_stroke_geometry_update(struct bGPdata *gpd,
+ struct bGPDstroke *gps,
+ const eGPStrokeGeoUpdateFlag flag);
void BKE_gpencil_stroke_uv_update(struct bGPDstroke *gps);
void BKE_gpencil_transform(struct bGPdata *gpd, const float mat[4][4]);
diff --git a/source/blender/blenkernel/BKE_gpencil_modifier.h b/source/blender/blenkernel/BKE_gpencil_modifier.h
index 8fbc2112c77..ac8766374bb 100644
--- a/source/blender/blenkernel/BKE_gpencil_modifier.h
+++ b/source/blender/blenkernel/BKE_gpencil_modifier.h
@@ -119,7 +119,7 @@ typedef struct GpencilModifierTypeInfo {
void (*copyData)(const struct GpencilModifierData *md, struct GpencilModifierData *target);
/**
- * Callback for GP "stroke" modifiers that operate on the
+ * Callback for GP "polyline stroke" modifiers that operate on the
* shape and parameters of the provided strokes (e.g. Thickness, Noise, etc.)
*
* The gpl parameter contains the GP layer that the strokes come from.
@@ -130,7 +130,18 @@ typedef struct GpencilModifierTypeInfo {
* The gps parameter contains the GP stroke to operate on. This is usually a copy
* of the original (unmodified and saved to files) stroke data.
*/
- void (*deformStroke)(struct GpencilModifierData *md,
+ void (*deformPolyline)(struct GpencilModifierData *md,
+ struct Depsgraph *depsgraph,
+ struct Object *ob,
+ struct bGPDlayer *gpl,
+ struct bGPDframe *gpf,
+ struct bGPDstroke *gps);
+
+ /**
+ * Callback for GP "bezier stroke" modifiers that operate on the
+ * shape and parameters of the provided strokes (e.g. Thickness, Noise, etc.)
+ */
+ void (*deformBezier)(struct GpencilModifierData *md,
struct Depsgraph *depsgraph,
struct Object *ob,
struct bGPDlayer *gpl,
diff --git a/source/blender/blenkernel/intern/armature_deform.c b/source/blender/blenkernel/intern/armature_deform.c
index bca5503c8d2..770a90b7190 100644
--- a/source/blender/blenkernel/intern/armature_deform.c
+++ b/source/blender/blenkernel/intern/armature_deform.c
@@ -49,6 +49,7 @@
#include "BKE_armature.h"
#include "BKE_deform.h"
#include "BKE_editmesh.h"
+#include "BKE_gpencil.h"
#include "BKE_lattice.h"
#include "DEG_depsgraph_build.h"
@@ -487,6 +488,7 @@ static void armature_deform_coords_impl(const Object *ob_arm,
bool use_dverts = false;
int armature_def_nr;
int cd_dvert_offset = -1;
+ MDeformVert *temp_dverts = NULL;
/* in editmode, or not an armature */
if (arm->edbo || (ob_arm->pose == NULL)) {
@@ -523,9 +525,33 @@ static void armature_deform_coords_impl(const Object *ob_arm,
}
}
else if (ob_target->type == OB_GPENCIL) {
- dverts = gps_target->dvert;
- if (dverts) {
- dverts_len = gps_target->totpoints;
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps_target)) {
+ bGPDcurve *gpc = gps_target->editcurve;
+ dverts = gpc->dvert;
+ if (dverts != NULL) {
+ dverts_len = gpc->tot_curve_points * 3;
+ temp_dverts = MEM_mallocN(sizeof(MDeformVert) * dverts_len, __func__);
+ for (i = 0; i < gpc->tot_curve_points; i++) {
+ MDeformVert *dvert_src = &gpc->dvert[i];
+ int idx = i * 3;
+ for (int w = 0; w < 3; w++) {
+ MDeformVert *dvert_dst = &temp_dverts[idx + w];
+ memcpy(dvert_dst, dvert_src, sizeof(MDeformVert));
+ if (dvert_src->dw) {
+ dvert_dst->dw = MEM_mallocN(sizeof(MDeformWeight) * dvert_src->totweight,
+ __func__);
+ memcpy(dvert_dst->dw, dvert_src->dw, sizeof(MDeformWeight) * dvert_src->totweight);
+ }
+ }
+ }
+ dverts = temp_dverts;
+ }
+ }
+ else {
+ dverts = gps_target->dvert;
+ if (dverts) {
+ dverts_len = gps_target->totpoints;
+ }
}
}
}
@@ -619,6 +645,15 @@ static void armature_deform_coords_impl(const Object *ob_arm,
if (pchan_from_defbase) {
MEM_freeN(pchan_from_defbase);
}
+
+ if (temp_dverts != NULL) {
+ bGPDcurve *gpc = gps_target->editcurve;
+ for (i = 0; i < gpc->tot_curve_points * 3; i++) {
+ MDeformVert *dvert = &temp_dverts[i];
+ MEM_SAFE_FREE(dvert->dw);
+ }
+ MEM_freeN(temp_dverts);
+ }
}
void BKE_armature_deform_coords_with_gpencil_stroke(const Object *ob_arm,
diff --git a/source/blender/blenkernel/intern/brush.c b/source/blender/blenkernel/intern/brush.c
index abf7bab7612..4a7049e10db 100644
--- a/source/blender/blenkernel/intern/brush.c
+++ b/source/blender/blenkernel/intern/brush.c
@@ -1057,6 +1057,27 @@ void BKE_gpencil_brush_preset_set(Main *bmain, Brush *brush, const short type)
zero_v3(brush->secondary_rgb);
break;
}
+#if 0
+ case GP_BRUSH_PRESET_INK_CURVE: {
+ brush->size = 25.0f;
+ brush->gpencil_settings->flag &= ~GP_BRUSH_USE_PRESSURE;
+
+ brush->gpencil_settings->draw_strength = 1.0f;
+ brush->gpencil_settings->flag &= ~GP_BRUSH_USE_STRENGTH_PRESSURE;
+
+ brush->gpencil_settings->input_samples = 10;
+ brush->gpencil_settings->draw_angle = 0.0f;
+ brush->gpencil_settings->draw_angle_factor = 0.0f;
+ brush->gpencil_settings->hardeness = 1.0f;
+ copy_v2_fl(brush->gpencil_settings->aspect_ratio, 1.0f);
+
+ brush->gpencil_settings->icon_id = GP_BRUSH_ICON_PEN;
+ brush->gpencil_tool = GPAINT_TOOL_CURVE;
+
+ zero_v3(brush->secondary_rgb);
+ break;
+ }
+#endif
case GP_BRUSH_PRESET_VERTEX_DRAW: {
brush->gpencil_settings->icon_id = GP_BRUSH_ICON_VERTEX_DRAW;
brush->gpencil_vertex_tool = GPVERTEX_TOOL_DRAW;
@@ -1399,6 +1420,14 @@ void BKE_brush_gpencil_paint_presets(Main *bmain, ToolSettings *ts, const bool r
BKE_gpencil_brush_preset_set(bmain, brush, GP_BRUSH_PRESET_TINT);
}
+#if 0
+ /* Curve pen. */
+ brush = gpencil_brush_ensure(bmain, ts, "Curve pen", OB_MODE_PAINT_GPENCIL, &r_new);
+ if ((reset) || (r_new)) {
+ BKE_gpencil_brush_preset_set(bmain, brush, GPAINT_TOOL_CURVE);
+ }
+#endif
+
/* Set default Draw brush. */
if ((reset == false) && (brush_prev != NULL)) {
BKE_paint_brush_set(paint, brush_prev);
diff --git a/source/blender/blenkernel/intern/gpencil.c b/source/blender/blenkernel/intern/gpencil.c
index 647db970d09..d1a7c95fe8d 100644
--- a/source/blender/blenkernel/intern/gpencil.c
+++ b/source/blender/blenkernel/intern/gpencil.c
@@ -186,6 +186,7 @@ static void greasepencil_blend_write(BlendWriter *writer, ID *id, const void *id
BLO_write_struct(writer, bGPDcurve, gpc);
BLO_write_struct_array(
writer, bGPDcurve_point, gpc->tot_curve_points, gpc->curve_points);
+ BKE_defvert_blend_write(writer, gpc->tot_curve_points, gpc->dvert);
}
}
}
@@ -259,6 +260,11 @@ void BKE_gpencil_blend_read_data(BlendDataReader *reader, bGPdata *gpd)
if (gps->editcurve != NULL) {
/* Relink curve point array. */
BLO_read_data_address(reader, &gps->editcurve->curve_points);
+ if (gps->editcurve->dvert != NULL) {
+ BLO_read_data_address(reader, &gps->editcurve->dvert);
+ BKE_defvert_blend_read(
+ reader, gps->editcurve->tot_curve_points, gps->editcurve->dvert);
+ }
}
/* Relink weight data. */
@@ -388,12 +394,19 @@ void BKE_gpencil_free_stroke_editcurve(bGPDstroke *gps)
if (gps == NULL) {
return;
}
- bGPDcurve *editcurve = gps->editcurve;
- if (editcurve == NULL) {
+ bGPDcurve *gpc = gps->editcurve;
+ if (gpc == NULL) {
return;
}
- MEM_freeN(editcurve->curve_points);
- MEM_freeN(editcurve);
+ MEM_freeN(gpc->curve_points);
+ if (gpc->dvert != NULL) {
+ for (int i = 0; i < gpc->tot_curve_points; i++) {
+ MDeformVert *dvert = &gpc->dvert[i];
+ BKE_gpencil_free_point_weights(dvert);
+ }
+ MEM_freeN(gpc->dvert);
+ }
+ MEM_freeN(gpc);
gps->editcurve = NULL;
}
@@ -407,8 +420,9 @@ void BKE_gpencil_free_stroke(bGPDstroke *gps)
if (gps->points) {
MEM_freeN(gps->points);
}
+
+ BKE_gpencil_free_stroke_weights(gps);
if (gps->dvert) {
- BKE_gpencil_free_stroke_weights(gps);
MEM_freeN(gps->dvert);
}
if (gps->triangles) {
@@ -766,8 +780,6 @@ bGPdata *BKE_gpencil_data_addnew(Main *bmain, const char name[])
gpd->pixfactor = GP_DEFAULT_PIX_FACTOR;
gpd->curve_edit_resolution = GP_DEFAULT_CURVE_RESOLUTION;
- gpd->curve_edit_threshold = GP_DEFAULT_CURVE_ERROR;
- gpd->curve_edit_corner_angle = GP_DEFAULT_CURVE_EDIT_CORNER_ANGLE;
/* use adaptive curve resolution by default */
gpd->flag |= GP_DATA_CURVE_ADAPTIVE_RESOLUTION;
@@ -942,6 +954,16 @@ void BKE_gpencil_stroke_weights_duplicate(bGPDstroke *gps_src, bGPDstroke *gps_d
BKE_defvert_array_copy(gps_dst->dvert, gps_src->dvert, gps_src->totpoints);
}
+void BKE_gpencil_editcurve_weights_duplicate(bGPDcurve *gpc_src, bGPDcurve *gpc_dst)
+{
+ if (gpc_src == NULL) {
+ return;
+ }
+ BLI_assert(gpc_src->tot_curve_points == gpc_dst->tot_curve_points);
+
+ BKE_defvert_array_copy(gpc_dst->dvert, gpc_src->dvert, gpc_src->tot_curve_points);
+}
+
/* Make a copy of a given gpencil stroke editcurve */
bGPDcurve *BKE_gpencil_stroke_curve_duplicate(bGPDcurve *gpc_src)
{
@@ -951,6 +973,14 @@ bGPDcurve *BKE_gpencil_stroke_curve_duplicate(bGPDcurve *gpc_src)
gpc_dst->curve_points = MEM_dupallocN(gpc_src->curve_points);
}
+ if (gpc_src->dvert != NULL) {
+ gpc_dst->dvert = MEM_dupallocN(gpc_src->dvert);
+ BKE_gpencil_editcurve_weights_duplicate(gpc_src, gpc_dst);
+ }
+ else {
+ gpc_dst->dvert = NULL;
+ }
+
return gpc_dst;
}
@@ -1197,7 +1227,6 @@ void BKE_gpencil_curve_sync_selection(bGPdata *gpd, bGPDstroke *gps)
return;
}
- gps->flag &= ~GP_STROKE_SELECT;
BKE_gpencil_stroke_select_index_reset(gps);
gpc->flag &= ~GP_CURVE_SELECT;
@@ -1220,7 +1249,6 @@ void BKE_gpencil_curve_sync_selection(bGPdata *gpd, bGPDstroke *gps)
if (is_selected) {
gpc->flag |= GP_CURVE_SELECT;
- gps->flag |= GP_STROKE_SELECT;
BKE_gpencil_stroke_select_index_set(gpd, gps);
}
}
@@ -1257,14 +1285,7 @@ void BKE_gpencil_frame_delete_laststroke(bGPDlayer *gpl, bGPDframe *gpf)
}
/* free the stroke and its data */
- if (gps->points) {
- MEM_freeN(gps->points);
- }
- if (gps->dvert) {
- BKE_gpencil_free_stroke_weights(gps);
- MEM_freeN(gps->dvert);
- }
- MEM_freeN(gps->triangles);
+ BKE_gpencil_free_stroke(gps);
BLI_freelinkN(&gpf->strokes, gps);
/* if frame has no strokes after this, delete it */
@@ -2078,6 +2099,26 @@ bool BKE_gpencil_stroke_select_check(const bGPDstroke *gps)
/* ************************************************** */
/* GP Object - Vertex Groups */
+/* Helper to remove a dvert from a group. */
+static void gpencil_remove_dvert_ex(MDeformVert *dvert, const int def_nr, const int tot_groups)
+{
+ if (dvert == NULL) {
+ return;
+ }
+
+ MDeformWeight *dw = BKE_defvert_find_index(dvert, def_nr);
+ if (dw != NULL) {
+ BKE_defvert_remove_group(dvert, dw);
+ }
+ /* Reorganize weights for other groups after deleted one. */
+ for (int g = 0; g < tot_groups; g++) {
+ dw = BKE_defvert_find_index(dvert, g);
+ if ((dw != NULL) && (dw->def_nr > def_nr)) {
+ dw->def_nr--;
+ }
+ }
+}
+
/**
* Remove a vertex group.
* \param ob: Grease pencil object
@@ -2086,7 +2127,6 @@ bool BKE_gpencil_stroke_select_check(const bGPDstroke *gps)
void BKE_gpencil_vgroup_remove(Object *ob, bDeformGroup *defgroup)
{
bGPdata *gpd = ob->data;
- MDeformVert *dvert = NULL;
const int def_nr = BLI_findindex(&ob->defbase, defgroup);
const int totgrp = BLI_listbase_count(&ob->defbase);
@@ -2095,20 +2135,17 @@ void BKE_gpencil_vgroup_remove(Object *ob, bDeformGroup *defgroup)
LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
- if (gps->dvert != NULL) {
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps)) {
+ bGPDcurve *gpc = gps->editcurve;
+ for (int i = 0; i < gpc->tot_curve_points; i++) {
+ MDeformVert *dvert = &gpc->dvert[i];
+ gpencil_remove_dvert_ex(dvert, def_nr, totgrp);
+ }
+ }
+ else if (gps->dvert != NULL) {
for (int i = 0; i < gps->totpoints; i++) {
- dvert = &gps->dvert[i];
- MDeformWeight *dw = BKE_defvert_find_index(dvert, def_nr);
- if (dw != NULL) {
- BKE_defvert_remove_group(dvert, dw);
- }
- /* Reorganize weights for other groups after deleted one. */
- for (int g = 0; g < totgrp; g++) {
- dw = BKE_defvert_find_index(dvert, g);
- if ((dw != NULL) && (dw->def_nr > def_nr)) {
- dw->def_nr--;
- }
- }
+ MDeformVert *dvert = &gps->dvert[i];
+ gpencil_remove_dvert_ex(dvert, def_nr, totgrp);
}
}
}
@@ -2127,8 +2164,12 @@ void BKE_gpencil_vgroup_remove(Object *ob, bDeformGroup *defgroup)
*/
void BKE_gpencil_dvert_ensure(bGPDstroke *gps)
{
- if (gps->dvert == NULL) {
- gps->dvert = MEM_callocN(sizeof(MDeformVert) * gps->totpoints, "gp_stroke_weights");
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps) && gps->editcurve->dvert == NULL) {
+ bGPDcurve *gpc = gps->editcurve;
+ gpc->dvert = MEM_callocN(sizeof(MDeformVert) * gpc->tot_curve_points, __func__);
+ }
+ else if (gps->dvert == NULL) {
+ gps->dvert = MEM_callocN(sizeof(MDeformVert) * gps->totpoints, __func__);
}
}
@@ -2654,7 +2695,7 @@ bool BKE_gpencil_from_image(
BKE_gpencil_stroke_select_index_set(gpd, gps);
}
- BKE_gpencil_stroke_geometry_update(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
}
}
@@ -2949,19 +2990,37 @@ void BKE_gpencil_frame_original_pointers_update(const struct bGPDframe *gpf_orig
/* Assign original stroke pointer. */
if (gps_eval != NULL) {
gps_eval->runtime.gps_orig = gps_orig;
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps_orig)) {
+ bGPDcurve *gpc_orig = gps_orig->editcurve;
+ bGPDcurve *gpc_eval = gps_eval->editcurve;
- /* Assign original point pointer. */
- for (int i = 0; i < gps_orig->totpoints; i++) {
- if (i > gps_eval->totpoints - 1) {
- break;
+ for (int i = 0; i < gpc_orig->tot_curve_points; i++) {
+ if (i > gpc_eval->tot_curve_points - 1) {
+ break;
+ }
+ bGPDcurve_point *gpc_pt_orig = &gpc_orig->curve_points[i];
+ bGPDcurve_point *gpc_pt_eval = &gpc_eval->curve_points[i];
+ gpc_pt_orig->runtime.gpc_pt_orig = NULL;
+ gpc_pt_orig->runtime.idx_orig = i;
+ gpc_pt_eval->runtime.gpc_pt_orig = gpc_pt_orig;
+ gpc_pt_eval->runtime.idx_orig = i;
}
- bGPDspoint *pt_orig = &gps_orig->points[i];
- bGPDspoint *pt_eval = &gps_eval->points[i];
- pt_orig->runtime.pt_orig = NULL;
- pt_orig->runtime.idx_orig = i;
- pt_eval->runtime.pt_orig = pt_orig;
- pt_eval->runtime.idx_orig = i;
}
+ else {
+ /* Assign original point pointer. */
+ for (int i = 0; i < gps_orig->totpoints; i++) {
+ if (i > gps_eval->totpoints - 1) {
+ break;
+ }
+ bGPDspoint *pt_orig = &gps_orig->points[i];
+ bGPDspoint *pt_eval = &gps_eval->points[i];
+ pt_orig->runtime.pt_orig = NULL;
+ pt_orig->runtime.idx_orig = i;
+ pt_eval->runtime.pt_orig = pt_orig;
+ pt_eval->runtime.idx_orig = i;
+ }
+ }
+
/* Increase pointer. */
gps_eval = gps_eval->next;
}
diff --git a/source/blender/blenkernel/intern/gpencil_curve.c b/source/blender/blenkernel/intern/gpencil_curve.c
index 344be7bc0f5..01a57b01f4a 100644
--- a/source/blender/blenkernel/intern/gpencil_curve.c
+++ b/source/blender/blenkernel/intern/gpencil_curve.c
@@ -37,14 +37,17 @@
#include "BLT_translation.h"
#include "DNA_collection_types.h"
+#include "DNA_color_types.h"
#include "DNA_gpencil_types.h"
#include "DNA_material_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_scene_types.h"
#include "BKE_collection.h"
+#include "BKE_colortools.h"
#include "BKE_context.h"
#include "BKE_curve.h"
+#include "BKE_deform.h"
#include "BKE_gpencil.h"
#include "BKE_gpencil_curve.h"
#include "BKE_gpencil_geom.h"
@@ -56,8 +59,6 @@
#include "DEG_depsgraph_query.h"
-#define COORD_FITTING_INFLUENCE 20.0f
-
/* -------------------------------------------------------------------- */
/** \name Convert to curve object
* \{ */
@@ -330,7 +331,6 @@ static void gpencil_convert_spline(Main *bmain,
* The total of points must consider that last point of each segment is equal to the first
* point of next segment.
*/
- int totpoints = 0;
int segments = 0;
int resolu = nu->resolu + 1;
segments = nu->pntsu;
@@ -338,7 +338,6 @@ static void gpencil_convert_spline(Main *bmain,
segments--;
cyclic = false;
}
- totpoints = (resolu * segments) - (segments - 1);
/* Materials
* Notice: The color of the material is the color of viewport and not the final shader color.
@@ -358,69 +357,43 @@ static void gpencil_convert_spline(Main *bmain,
switch (nu->type) {
case CU_POLY: {
- /* Allocate memory for storage points. */
- gps->totpoints = nu->pntsu;
- gps->points = MEM_callocN(sizeof(bGPDspoint) * gps->totpoints, "gp_stroke_points");
+ bGPDcurve *gpc = BKE_gpencil_stroke_editcurve_new(nu->pntsu);
+
/* Increase thickness for this type. */
gps->thickness = 10.0f;
- /* Get all curve points */
- for (int s = 0; s < gps->totpoints; s++) {
- BPoint *bp = &nu->bp[s];
- bGPDspoint *pt = &gps->points[s];
- copy_v3_v3(&pt->x, bp->vec);
- pt->pressure = bp->radius;
- pt->strength = 1.0f;
+ for (int i = 0; i < nu->pntsu; i++) {
+ bGPDcurve_point *cpt = &gpc->curve_points[i];
+ BPoint *bp = &nu->bp[i];
+ copy_v3_v3(cpt->bezt.vec[1], bp->vec);
+ cpt->pressure = bp->radius;
+ cpt->strength = 1.0f;
+ cpt->bezt.h1 = cpt->bezt.h2 = HD_VECT;
}
+
+ gps->editcurve = gpc;
+ BKE_gpencil_editcurve_recalculate_handles(gps);
break;
}
case CU_BEZIER: {
- /* Allocate memory for storage points. */
- gps->totpoints = totpoints;
- gps->points = MEM_callocN(sizeof(bGPDspoint) * gps->totpoints, "gp_stroke_points");
-
- int init = 0;
- resolu = nu->resolu + 1;
- segments = nu->pntsu;
- if ((nu->flagu & CU_NURB_CYCLIC) == 0) {
- segments--;
- }
- /* Get all interpolated curve points of Beziert */
- for (int s = 0; s < segments; s++) {
- int inext = (s + 1) % nu->pntsu;
- BezTriple *prevbezt = &nu->bezt[s];
- BezTriple *bezt = &nu->bezt[inext];
- bool last = (bool)(s == segments - 1);
-
- coord_array = MEM_callocN((size_t)3 * resolu * sizeof(float), __func__);
+ /* Create new grease pencil editcurve. */
+ bGPDcurve *gpc = BKE_gpencil_stroke_editcurve_new(nu->pntsu);
+ for (int i = 0; i < nu->pntsu; i++) {
+ bGPDcurve_point *cpt = &gpc->curve_points[i];
+ BezTriple *bezt = &nu->bezt[i];
for (int j = 0; j < 3; j++) {
- BKE_curve_forward_diff_bezier(prevbezt->vec[1][j],
- prevbezt->vec[2][j],
- bezt->vec[0][j],
- bezt->vec[1][j],
- coord_array + j,
- resolu - 1,
- sizeof(float[3]));
- }
- /* Save first point coordinates. */
- if (s == 0) {
- copy_v3_v3(init_co, &coord_array[0]);
+ copy_v3_v3(cpt->bezt.vec[j], bezt->vec[j]);
}
- /* Add points to the stroke */
- float radius_start = prevbezt->radius * scale_thickness;
- float radius_end = bezt->radius * scale_thickness;
- gpencil_add_new_points(
- gps, coord_array, radius_start, radius_end, init, resolu, init_co, last);
- /* Free memory. */
- MEM_SAFE_FREE(coord_array);
-
- /* As the last point of segment is the first point of next segment, back one array
- * element to avoid duplicated points on the same location.
- */
- init += resolu - 1;
+ cpt->pressure = bezt->radius * scale_thickness;
+ cpt->strength = 1.0f;
+ cpt->bezt.h1 = bezt->h1;
+ cpt->bezt.h2 = bezt->h2;
}
+
+ gps->editcurve = gpc;
+ BKE_gpencil_editcurve_recalculate_handles(gps);
break;
}
case CU_NURBS: {
@@ -453,9 +426,10 @@ static void gpencil_convert_spline(Main *bmain,
break;
}
}
- /* Cyclic curve, close stroke. */
+
+ /* Cyclic curve. */
if (cyclic) {
- BKE_gpencil_stroke_close(gps);
+ gps->flag |= GP_STROKE_CYCLIC;
}
if (sample > 0.0f) {
@@ -463,7 +437,7 @@ static void gpencil_convert_spline(Main *bmain,
}
/* Recalc fill geometry. */
- BKE_gpencil_stroke_geometry_update(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
}
static void gpencil_editstroke_deselect_all(bGPDcurve *gpc)
@@ -566,8 +540,182 @@ void BKE_gpencil_convert_curve(Main *bmain,
/** \name Edit-Curve Kernel Functions
* \{ */
-static bGPDcurve *gpencil_stroke_editcurve_generate_edgecases(bGPDstroke *gps,
- const float stroke_radius)
+typedef struct tCurveFitPoint {
+ float x, y, z;
+ float pressure;
+ float strength;
+ float color[4];
+} tCurveFitPoint;
+
+#define CURVE_FIT_POINT_DIM 9
+
+/* We treat pressure, strength and vertex colors as dimensions in the curve fitting like the
+ * position. But this means that e.g. a high pressure will change the shape of the
+ * higher-dimensional fitted curve which will in turn change the shape of the projected
+ * 3-dimensional curve. But we don't really want the pressure or something else than the position
+ * to influence the shape. So this COORD_FITTING_INFLUENCE will "dampen" the effect of the other
+ * attributes affecting the shape. Ideally, we fit the other attributes separate from the position.
+ */
+#define COORD_FITTING_INFLUENCE 20.0f
+
+typedef struct tGPCurveSegment {
+ struct tGPCurveSegment *next, *prev;
+
+ float *point_array;
+ uint32_t point_array_len;
+
+ bGPDcurve_point *curve_points;
+ float *cubic_array;
+ uint32_t cubic_array_len;
+
+ uint32_t *cubic_orig_index;
+ uint32_t *corners_index_array;
+ uint32_t corners_index_len;
+
+} tGPCurveSegment;
+
+/**
+ * Find the stationary points of a cubic bezier curve.
+ * Calcualtes the t-values (factor along curve) of the stationary points of a cubic bezier curve by
+ * finding the roots of the first derivative. If no roots where found the function returns false.
+ * Otherwise if one of the roots was found, one of r_t1 or r_t2 will be NaN and the other will
+ * contain the t-value of the root found. If both roots were found, both r_t1 and r_t2 will contain
+ * a value.
+ * \param p1, p2, p3, p4: Points of the cubic bezier curve.
+ * \param r_t1, r_t2: Return t-values (factor along curve).
+ */
+static bool find_cubic_bezier_stationary_points(
+ const float p1, const float p2, const float p3, const float p4, float *r_t1, float *r_t2)
+{
+ float a = 6.0f * (-p1 + 3.0f * p2 - 3.0f * p3 + p4);
+ if (IS_EQF(a, 0.0f)) {
+ /* Special edge-case that we have to handle seperately.*/
+ if ((p1 == p4) && (p2 == p3)) {
+ if (p1 == p2) {
+ *r_t1 = NAN;
+ *r_t2 = NAN;
+ return false;
+ }
+ *r_t1 = 0.5f;
+ *r_t2 = NAN;
+ return true;
+ }
+ /* Denominator is zero. No roots. */
+ return false;
+ }
+
+ float x = p4 * (p1 - p2) - p3 * (p1 + p2) + p2 * p2 + p3 * p3;
+ if (x < 0.0f) {
+ /* Negative number under square root. No real roots. */
+ return false;
+ }
+
+ float s = 6.0f * sqrtf(x);
+ float b = 6.0f * (p1 - 2.0f * p2 + p3);
+
+ float t1 = (s - b) / a;
+ float t2 = -(s + b) / a;
+
+ /* Discard root outside of limits. */
+ *r_t1 = IN_RANGE_INCL(t1, 0.0f, 1.0f) ? t1 : NAN;
+ *r_t2 = IN_RANGE_INCL(t2, 0.0f, 1.0f) ? t2 : NAN;
+
+ return !(*r_t1 == NAN) || !(*r_t2 == NAN);
+}
+
+static void gpencil_free_curve_segment(tGPCurveSegment *tcs)
+{
+ if (tcs == NULL) {
+ return;
+ }
+
+ if (tcs->point_array != NULL && tcs->point_array_len > 0) {
+ MEM_freeN(tcs->point_array);
+ }
+ if (tcs->curve_points != NULL && tcs->cubic_array_len > 0) {
+ MEM_freeN(tcs->curve_points);
+ }
+ if (tcs->cubic_array != NULL && tcs->cubic_array_len > 0) {
+ free(tcs->cubic_array);
+ }
+ if (tcs->cubic_orig_index != NULL && tcs->cubic_array_len > 0) {
+ free(tcs->cubic_orig_index);
+ }
+ if (tcs->corners_index_array != NULL && tcs->corners_index_len > 0) {
+ free(tcs->corners_index_array);
+ }
+
+ MEM_freeN(tcs);
+}
+
+static bool gpencil_is_segment_tagged(bGPDstroke *gps, const uint32_t cpt_start)
+{
+ bGPDcurve *gpc = gps->editcurve;
+ const bool is_cyclic = (gps->flag & GP_STROKE_CYCLIC);
+ const uint32_t cpt_end = (cpt_start + 1) % gpc->tot_curve_points;
+
+ const uint32_t start_pt_idx = gpc->curve_points[cpt_start].point_index;
+ const uint32_t end_pt_idx = gpc->curve_points[cpt_end].point_index;
+
+ if (!is_cyclic && (cpt_start == gpc->tot_curve_points - 1)) {
+ return false;
+ }
+
+ /* Check endpoints of segment frist. */
+ if ((gps->points[start_pt_idx].flag & GP_SPOINT_TAG) ||
+ (gps->points[end_pt_idx].flag & GP_SPOINT_TAG)) {
+ return true;
+ }
+
+ /* Iterate over all stroke points in between and check if any point is tagged. */
+ if (is_cyclic && (cpt_start == gpc->tot_curve_points - 1)) {
+ for (int i = start_pt_idx + 1; i < gps->totpoints; i++) {
+ bGPDspoint *pt = &gps->points[i];
+ if ((pt->flag & GP_SPOINT_TAG)) {
+ return true;
+ }
+ }
+ }
+ else {
+ for (int i = start_pt_idx + 1; i < end_pt_idx - 1; i++) {
+ bGPDspoint *pt = &gps->points[i];
+ if ((pt->flag & GP_SPOINT_TAG)) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+static int gpencil_count_tagged_curve_segments(bGPDstroke *gps)
+{
+ bGPDcurve *gpc = gps->editcurve;
+ /* Handle single point edgecase. */
+ if (gpc->tot_curve_points == 1) {
+ return (gps->points[0].flag & GP_SPOINT_TAG) ? 1 : 0;
+ }
+
+ /* Iterate over points and check if segment is tagged. */
+ int count = 0;
+ for (int i = 0; i < gpc->tot_curve_points; i++) {
+ if (gpencil_is_segment_tagged(gps, i)) {
+ count++;
+ }
+ }
+
+ return count;
+}
+
+static void gpencil_clear_point_tag(bGPDstroke *gps)
+{
+ for (int i = 0; i < gps->totpoints; i++) {
+ bGPDspoint *pt = &gps->points[i];
+ pt->flag &= ~GP_SPOINT_TAG;
+ }
+}
+
+static bGPDcurve *gpencil_stroke_editcurve_generate_edgecases(bGPDstroke *gps)
{
BLI_assert(gps->totpoints < 3);
@@ -577,6 +725,8 @@ static bGPDcurve *gpencil_stroke_editcurve_generate_edgecases(bGPDstroke *gps,
bGPDcurve_point *cpt = &editcurve->curve_points[0];
BezTriple *bezt = &cpt->bezt;
+ /* This is not the actual radius, but it is good enough for what we need. */
+ float stroke_radius = gps->thickness / 1000.0f;
/* Handles are twice as long as the radius of the point. */
float offset = (pt->pressure * stroke_radius) * 2.0f;
@@ -639,154 +789,465 @@ static bGPDcurve *gpencil_stroke_editcurve_generate_edgecases(bGPDstroke *gps,
return NULL;
}
-/**
- * Creates a bGPDcurve by doing a cubic curve fitting on the grease pencil stroke points.
- */
-bGPDcurve *BKE_gpencil_stroke_editcurve_generate(bGPDstroke *gps,
- const float error_threshold,
- const float corner_angle,
- const float stroke_radius)
+/* Helper: Generates a tGPCurveSegment using the stroke points in gps from the start index up to
+ * the end index. */
+static tGPCurveSegment *gpencil_fit_curve_to_points_ex(bGPDstroke *gps,
+ const float diag_length,
+ const float error_threshold,
+ const float corner_angle,
+ const uint32_t start_idx,
+ const uint32_t end_idx,
+ const bool is_cyclic)
{
- if (gps->totpoints < 3) {
- return gpencil_stroke_editcurve_generate_edgecases(gps, stroke_radius);
- }
-#define POINT_DIM 9
+ const uint32_t length = end_idx - start_idx + 1;
- float *points = MEM_callocN(sizeof(float) * gps->totpoints * POINT_DIM, __func__);
- float diag_length = len_v3v3(gps->boundbox_min, gps->boundbox_max);
- float tmp_vec[3];
-
- for (int i = 0; i < gps->totpoints; i++) {
- bGPDspoint *pt = &gps->points[i];
- int row = i * POINT_DIM;
+ tGPCurveSegment *tcs = MEM_callocN(sizeof(tGPCurveSegment), __func__);
+ tcs->point_array_len = length * CURVE_FIT_POINT_DIM;
+ tcs->point_array = MEM_callocN(sizeof(float) * tcs->point_array_len, __func__);
+ float tmp_vec[3];
+ for (int i = 0; i < length; i++) {
+ bGPDspoint *pt = &gps->points[i + start_idx];
+ int row = i * CURVE_FIT_POINT_DIM;
+ tCurveFitPoint *cfpt = (tCurveFitPoint *)&tcs->point_array[row];
/* normalize coordinate to 0..1 */
sub_v3_v3v3(tmp_vec, &pt->x, gps->boundbox_min);
- mul_v3_v3fl(&points[row], tmp_vec, COORD_FITTING_INFLUENCE / diag_length);
- points[row + 3] = pt->pressure / diag_length;
+ mul_v3_v3fl(&cfpt->x, tmp_vec, COORD_FITTING_INFLUENCE / diag_length);
+ cfpt->pressure = pt->pressure / diag_length;
/* strength and color are already normalized */
- points[row + 4] = pt->strength / diag_length;
- mul_v4_v4fl(&points[row + 5], pt->vert_color, 1.0f / diag_length);
+ cfpt->strength = pt->strength / diag_length;
+ mul_v4_v4fl(cfpt->color, pt->vert_color, 1.0f / diag_length);
}
- uint calc_flag = CURVE_FIT_CALC_HIGH_QUALIY;
- if (gps->totpoints > 2 && gps->flag & GP_STROKE_CYCLIC) {
+ int calc_flag = CURVE_FIT_CALC_HIGH_QUALIY;
+ if (is_cyclic) {
calc_flag |= CURVE_FIT_CALC_CYCLIC;
}
-
- float *r_cubic_array = NULL;
- unsigned int r_cubic_array_len = 0;
- unsigned int *r_cubic_orig_index = NULL;
- unsigned int *r_corners_index_array = NULL;
- unsigned int r_corners_index_len = 0;
- int r = curve_fit_cubic_to_points_refit_fl(points,
- gps->totpoints,
- POINT_DIM,
+ int r = curve_fit_cubic_to_points_refit_fl(tcs->point_array,
+ length,
+ CURVE_FIT_POINT_DIM,
error_threshold,
calc_flag,
NULL,
0,
corner_angle,
- &r_cubic_array,
- &r_cubic_array_len,
- &r_cubic_orig_index,
- &r_corners_index_array,
- &r_corners_index_len);
-
- if (r != 0 || r_cubic_array_len < 1) {
+ &tcs->cubic_array,
+ &tcs->cubic_array_len,
+ &tcs->cubic_orig_index,
+ &tcs->corners_index_array,
+ &tcs->corners_index_len);
+
+ if (r != 0 || tcs->cubic_array_len < 1) {
+ gpencil_free_curve_segment(tcs);
return NULL;
}
- uint curve_point_size = 3 * POINT_DIM;
+ tcs->curve_points = MEM_callocN(sizeof(bGPDcurve_point) * tcs->cubic_array_len, __func__);
- bGPDcurve *editcurve = BKE_gpencil_stroke_editcurve_new(r_cubic_array_len);
-
- for (int i = 0; i < r_cubic_array_len; i++) {
- bGPDcurve_point *cpt = &editcurve->curve_points[i];
+ for (int i = 0; i < tcs->cubic_array_len; i++) {
+ bGPDcurve_point *cpt = &tcs->curve_points[i];
BezTriple *bezt = &cpt->bezt;
- float *curve_point = &r_cubic_array[i * curve_point_size];
+ tCurveFitPoint *curve_point = (tCurveFitPoint *)&tcs->cubic_array[i * 3 * CURVE_FIT_POINT_DIM];
+ /* Loop over bezier triple and fill coordinates. */
for (int j = 0; j < 3; j++) {
- float *bez = &curve_point[j * POINT_DIM];
+ float *bez = &curve_point[j].x;
madd_v3_v3v3fl(bezt->vec[j], gps->boundbox_min, bez, diag_length / COORD_FITTING_INFLUENCE);
}
- float *ctrl_point = &curve_point[1 * POINT_DIM];
- cpt->pressure = ctrl_point[3] * diag_length;
- cpt->strength = ctrl_point[4] * diag_length;
- mul_v4_v4fl(cpt->vert_color, &ctrl_point[5], diag_length);
+ tCurveFitPoint *ctrl_point = &curve_point[1];
+ cpt->pressure = ctrl_point->pressure * diag_length;
+ cpt->strength = ctrl_point->strength * diag_length;
+ mul_v4_v4fl(cpt->vert_color, ctrl_point->color, diag_length);
- /* default handle type */
+ /* Default handle type */
bezt->h1 = HD_ALIGN;
bezt->h2 = HD_ALIGN;
- cpt->point_index = r_cubic_orig_index[i];
+ /* Make sure to add the start index. */
+ cpt->point_index = tcs->cubic_orig_index[i] + start_idx;
+
+ /* Select the curve point if the original stroke point was selected. */
+ if (gps->points[cpt->point_index].flag & GP_SPOINT_SELECT) {
+ cpt->flag |= GP_CURVE_POINT_SELECT;
+ BEZT_SEL_ALL(bezt);
+ }
}
- if (r_corners_index_len > 0 && r_corners_index_array != NULL) {
- int start = 0, end = r_corners_index_len;
- if ((r_corners_index_len > 1) && (calc_flag & CURVE_FIT_CALC_CYCLIC) == 0) {
- start = 1;
- end = r_corners_index_len - 1;
+ /* Set handle type to HD_FREE for corner handles. Ignore first and last. */
+ if (tcs->corners_index_array != NULL) {
+ uint i_start = 0, i_end = tcs->corners_index_len;
+
+ if ((tcs->corners_index_len >= 2) && (calc_flag & CURVE_FIT_CALC_CYCLIC) == 0) {
+ i_start += 1;
+ i_end -= 1;
}
- for (int i = start; i < end; i++) {
- bGPDcurve_point *cpt = &editcurve->curve_points[r_corners_index_array[i]];
+
+ for (uint i = i_start; i < i_end; i++) {
+ bGPDcurve_point *cpt = &tcs->curve_points[tcs->corners_index_array[i]];
BezTriple *bezt = &cpt->bezt;
bezt->h1 = HD_FREE;
bezt->h2 = HD_FREE;
}
}
- MEM_freeN(points);
- if (r_cubic_array) {
- free(r_cubic_array);
+ return tcs;
+}
+
+static float get_point_weight(MDeformVert *dvert, bool inverse, int def_nr)
+{
+ float weight = 1.0f;
+
+ if ((dvert != NULL) && (def_nr != -1)) {
+ MDeformWeight *dw = BKE_defvert_find_index(dvert, def_nr);
+ weight = dw ? dw->weight : -1.0f;
+ if ((weight >= 0.0f) && (inverse == 1)) {
+ return -1.0f;
+ }
+
+ if ((weight < 0.0f) && (inverse == 0)) {
+ return -1.0f;
+ }
+
+ /* if inverse, weight is always 1 */
+ if ((weight < 0.0f) && (inverse == 1)) {
+ return 1.0f;
+ }
}
- if (r_corners_index_array) {
- free(r_corners_index_array);
+
+ /* handle special empty groups */
+ if ((dvert == NULL) && (def_nr != -1)) {
+ if (inverse == 1) {
+ return 1.0f;
+ }
+
+ return -1.0f;
}
- if (r_cubic_orig_index) {
- free(r_cubic_orig_index);
+
+ return weight;
+}
+
+/**
+ * Creates a bGPDcurve by doing a cubic curve fitting on the grease pencil stroke points.
+ */
+bGPDcurve *BKE_gpencil_stroke_editcurve_generate(bGPDstroke *gps,
+ const float error_threshold,
+ const float corner_angle)
+{
+ if (gps->totpoints < 3) {
+ return gpencil_stroke_editcurve_generate_edgecases(gps);
}
+ const bool is_cyclic = (gps->flag & GP_STROKE_CYCLIC);
+ const float diag_length = len_v3v3(gps->boundbox_min, gps->boundbox_max);
+ tGPCurveSegment *tcs = gpencil_fit_curve_to_points_ex(
+ gps, diag_length, error_threshold, corner_angle, 0, gps->totpoints - 1, is_cyclic);
+
+ bGPDcurve *editcurve = BKE_gpencil_stroke_editcurve_new(tcs->cubic_array_len);
+ memcpy(
+ editcurve->curve_points, tcs->curve_points, sizeof(bGPDcurve_point) * tcs->cubic_array_len);
+ gpencil_free_curve_segment(tcs);
-#undef POINT_DIM
return editcurve;
}
/**
- * Updates the editcurve for a stroke. Frees the old curve if one exists and generates a new one.
+ * Creates a bGPDcurve by doing a cubic curve fitting on the tagged segments (parts of the curve
+ * with at least one bGPDspoint with GP_SPOINT_TAG).
+ */
+bGPDcurve *BKE_gpencil_stroke_editcurve_tagged_segments_update(bGPDstroke *gps,
+ const float error_threshold,
+ const float corner_angle)
+{
+ if (gps->totpoints < 3) {
+ BKE_gpencil_free_stroke_editcurve(gps);
+ return gpencil_stroke_editcurve_generate_edgecases(gps);
+ }
+
+ /* The reason why we cant just take all the bad segments and regenerate them is because that
+ * will leave the control points at their original spot (if a control point moved, we should no
+ * longer treat it as a control point). Therefor the only option is to find */
+
+ bGPDcurve *gpc = gps->editcurve;
+ const int num_segments = gpc->tot_curve_points - 1;
+ float diag_length = len_v3v3(gps->boundbox_min, gps->boundbox_max);
+
+ ListBase curve_segments = {NULL, NULL};
+
+ /* TODO: If the stroke is cyclic we need to move the start of the curve to the last curve point
+ * that is not tagged. */
+
+ tGPCurveSegment *tcs;
+ int j = 0, new_num_segments = 0;
+ for (int i = 0; i < num_segments; i = j) {
+ int island_length = 1;
+ int start_idx = gpc->curve_points[i].point_index;
+ bool is_tagged = gpencil_is_segment_tagged(gps, i);
+
+ /* Find the end of this island (tagged or un-tagged). */
+ int end_idx;
+ bGPDspoint *end_pt;
+ for (j = i + 1; j < gpc->tot_curve_points; j++, island_length++) {
+ end_idx = gpc->curve_points[j].point_index;
+ end_pt = &gps->points[end_idx];
+ if (!(is_tagged && (end_pt->flag & GP_SPOINT_TAG)) &&
+ (gpencil_is_segment_tagged(gps, j) != is_tagged)) {
+ break;
+ }
+ }
+
+ if (is_tagged) {
+ /* Regenerate this segment. */
+ tcs = gpencil_fit_curve_to_points_ex(
+ gps, diag_length, error_threshold, corner_angle, start_idx, end_idx, false);
+ }
+ else {
+ /* Save this segment. */
+ tcs = MEM_callocN(sizeof(tGPCurveSegment), __func__);
+ tcs->cubic_array_len = island_length;
+ tcs->point_array_len = end_idx - start_idx + 1;
+ tcs->curve_points = MEM_callocN(sizeof(bGPDcurve_point) * tcs->cubic_array_len, __func__);
+ memcpy(tcs->curve_points,
+ &gpc->curve_points[i],
+ sizeof(bGPDcurve_point) * tcs->cubic_array_len);
+ }
+
+ new_num_segments += (tcs->cubic_array_len - 1);
+ BLI_addtail(&curve_segments, tcs);
+ }
+
+ /* TODO: If the stroke is cyclic we need to check if the last segment needs to be regenerated or
+ * saved. */
+ bGPDcurve *editcurve = BKE_gpencil_stroke_editcurve_new(new_num_segments + 1);
+
+ /* Copy first point. */
+ tGPCurveSegment *frist_cs = (tGPCurveSegment *)&curve_segments.first;
+ memcpy(&editcurve->curve_points[0], &frist_cs->curve_points[0], sizeof(bGPDcurve_point));
+
+ /* Combine listbase curve segments to gpencil curve. */
+ int offset = 0;
+ LISTBASE_FOREACH_MUTABLE (tGPCurveSegment *, cs, &curve_segments) {
+ /* Copy second handle of first point */
+ bGPDcurve_point *first_dst = &editcurve->curve_points[offset];
+ bGPDcurve_point *first_src = &cs->curve_points[0];
+ copy_v3_v3(first_dst->bezt.vec[2], first_src->bezt.vec[2]);
+
+ /* Copy the rest of the points. */
+ memcpy(&editcurve->curve_points[offset + 1],
+ &cs->curve_points[1],
+ sizeof(bGPDcurve_point) * (cs->cubic_array_len - 1));
+
+ offset += (cs->cubic_array_len - 1);
+ gpencil_free_curve_segment(cs);
+ }
+
+ /* Free the old curve. */
+ BKE_gpencil_free_stroke_editcurve(gps);
+ return editcurve;
+}
+
+static void gpencil_stroke_editcurve_regenerate_single_ex(bGPDcurve_point *start,
+ bGPDcurve_point *end,
+ bGPDspoint *points,
+ const int totpoints,
+ const float boundbox_min[3],
+ const float diag_length,
+ const bool is_cyclic,
+ const float error_threshold,
+ float *r_error_sq)
+{
+#define POINT_DIM 3
+ BezTriple *bezt_prev = &start->bezt;
+ BezTriple *bezt_next = &end->bezt;
+
+ const uint32_t start_pt_idx = start->point_index;
+ const uint32_t end_pt_idx = end->point_index;
+
+ uint32_t length = 0;
+ if (start_pt_idx > end_pt_idx) {
+ if (!is_cyclic) {
+ return;
+ }
+ length = (totpoints - start_pt_idx) + end_pt_idx + 1;
+ }
+ else {
+ length = (end_pt_idx - start_pt_idx) + 1;
+ }
+
+ uint32_t point_array_len = length * POINT_DIM;
+ float *point_array = MEM_callocN(sizeof(float) * point_array_len, __func__);
+
+ float tmp_vec[3];
+ for (int i = 0; i < length; i++) {
+ int idx = (i + start_pt_idx) % totpoints;
+ bGPDspoint *pt = &points[idx];
+ float *point = &point_array[i * POINT_DIM];
+ /* normalize coordinate to 0..1 */
+ sub_v3_v3v3(tmp_vec, &pt->x, boundbox_min);
+ mul_v3_v3fl(point, tmp_vec, 1.0f / diag_length);
+ }
+
+ float tan_l[3], tan_r[3];
+
+ sub_v3_v3v3(tan_l, bezt_prev->vec[1], bezt_prev->vec[2]);
+ normalize_v3(tan_l);
+ sub_v3_v3v3(tan_r, bezt_next->vec[0], bezt_next->vec[1]);
+ normalize_v3(tan_r);
+
+ float error_sq;
+ uint error_index;
+ int r = curve_fit_cubic_to_points_single_fl(point_array,
+ length,
+ NULL,
+ POINT_DIM,
+ error_threshold,
+ tan_l,
+ tan_r,
+ bezt_prev->vec[2],
+ bezt_next->vec[0],
+ &error_sq,
+ &error_index);
+ if (r != 0) {
+ MEM_freeN(point_array);
+ return;
+ }
+
+ madd_v3_v3v3fl(bezt_prev->vec[2], boundbox_min, bezt_prev->vec[2], diag_length);
+ madd_v3_v3v3fl(bezt_next->vec[0], boundbox_min, bezt_next->vec[0], diag_length);
+
+ if (!ELEM(bezt_prev->h2, HD_FREE, HD_ALIGN)) {
+ bezt_prev->h2 = (bezt_prev->h2 == HD_VECT) ? HD_FREE : HD_ALIGN;
+ }
+ if (!ELEM(bezt_next->h1, HD_FREE, HD_ALIGN)) {
+ bezt_next->h1 = (bezt_next->h1 == HD_VECT) ? HD_FREE : HD_ALIGN;
+ }
+
+ MEM_freeN(point_array);
+
+ if (r_error_sq != NULL) {
+ *r_error_sq = error_sq;
+ }
+#undef POINT_DIM
+}
+
+/**
+ * Regenerate the handles for the segment from `start_idx` to `end_idx` by refitting the curve to
+ * the points. As an example, this is used by the dissolve operator to fit the curve to a segment
+ * where the curve poitns have been deleted.
+ * Note: `start_idx` can be greater than `end_idx`. If this is the case and the stroke is cyclic,
+ * then the fitting will use the points from the end and beginning of the stroke.
+ */
+void BKE_gpencil_stroke_editcurve_regenerate_single(bGPDstroke *gps,
+ uint32_t start_idx,
+ uint32_t end_idx,
+ const float error_threshold)
+{
+ if (!GPENCIL_STROKE_TYPE_BEZIER(gps) || (start_idx == end_idx)) {
+ return;
+ }
+ bGPDcurve *gpc = gps->editcurve;
+ const float diag_length = len_v3v3(gps->boundbox_min, gps->boundbox_max);
+
+ BLI_assert(start_idx < gpc->tot_curve_points && end_idx < gpc->tot_curve_points);
+
+ bGPDcurve_point *start = &gpc->curve_points[start_idx];
+ bGPDcurve_point *end = &gpc->curve_points[end_idx];
+
+ gpencil_stroke_editcurve_regenerate_single_ex(start,
+ end,
+ gps->points,
+ gps->totpoints,
+ gps->boundbox_min,
+ diag_length,
+ gps->flag & GP_STROKE_CYCLIC,
+ error_threshold,
+ NULL);
+}
+
+/**
+ * Updates the editcurve for a stroke.
+ * \param gps: The stroke.
+ * \param threshold: Fitting threshold. The gernerated curve should not deviate more than this
+ * amount from the stroke.
+ * \param corner_angle: If angles greater than this amount are detected during fitting, they will
+ * be sharp (non-aligned handles).
+ * \param flag: Flag for refitting options (see eGPStrokeGeoUpdateFlag).
*/
-void BKE_gpencil_stroke_editcurve_update(bGPdata *gpd, bGPDlayer *gpl, bGPDstroke *gps)
+void BKE_gpencil_stroke_refit_curve(bGPDstroke *gps,
+ const float threshold,
+ const float corner_angle,
+ const eGPStrokeGeoUpdateFlag flag)
{
if (gps == NULL || gps->totpoints < 0) {
return;
}
+ bGPDcurve *editcurve = NULL;
+ short prev_flag = 0;
+
+ /* If editcurve exists save the selection to the stroke points (only for syncing later). */
if (gps->editcurve != NULL) {
- BKE_gpencil_free_stroke_editcurve(gps);
+ BKE_gpencil_stroke_editcurve_sync_selection(NULL, gps, gps->editcurve);
+ }
+
+ /* Do a partial update by only updating the curve segments that contain tagged points. */
+ if ((flag & GP_GEO_UPDATE_CURVE_PARTIAL_REFIT) && gps->editcurve != NULL) {
+ prev_flag = gps->editcurve->flag;
+ /* Find the segments that need an update, then update them. */
+ const int tot_num_segments = (gps->flag & GP_STROKE_CYCLIC) ?
+ gps->editcurve->tot_curve_points :
+ gps->editcurve->tot_curve_points - 1;
+ const int num_sel_segments = gpencil_count_tagged_curve_segments(gps);
+ if (num_sel_segments == 0) {
+ /* No update needed. */
+ return;
+ }
+ else if (num_sel_segments == tot_num_segments) {
+ /* All segments need to be updated. Do a full update. */
+ BKE_gpencil_free_stroke_editcurve(gps);
+ editcurve = BKE_gpencil_stroke_editcurve_generate(gps, threshold, corner_angle);
+ }
+ else {
+ /* Some segments are unchanged. Do a partial update. */
+ editcurve = BKE_gpencil_stroke_editcurve_tagged_segments_update(
+ gps, threshold, corner_angle);
+ gpencil_clear_point_tag(gps);
+ }
}
+ else {
+ /* Do a full update. Delete the old curve and generate a new one. */
+ if (gps->editcurve != NULL) {
+ prev_flag = gps->editcurve->flag;
+ BKE_gpencil_free_stroke_editcurve(gps);
+ }
- float defaultpixsize = 1000.0f / gpd->pixfactor;
- float stroke_radius = ((gps->thickness + gpl->line_change) / defaultpixsize) / 2.0f;
+ editcurve = BKE_gpencil_stroke_editcurve_generate(gps, threshold, corner_angle);
+ }
- bGPDcurve *editcurve = BKE_gpencil_stroke_editcurve_generate(
- gps, gpd->curve_edit_threshold, gpd->curve_edit_corner_angle, stroke_radius);
if (editcurve == NULL) {
+ /* This should not happen. Maybe add an assert here? */
return;
}
+ /* Assign pointer. This makes the stroke a bezier stroke now. */
gps->editcurve = editcurve;
+ if (prev_flag) {
+ gps->editcurve->flag = prev_flag;
+ }
+
+ if ((flag & GP_GEO_UPDATE_CURVE_PARTIAL_REFIT)) {
+ BKE_gpencil_editcurve_recalculate_handles(gps);
+ }
}
/**
* Sync the selection from stroke to editcurve
*/
-void BKE_gpencil_editcurve_stroke_sync_selection(bGPdata *UNUSED(gpd),
- 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;
+ BKE_gpencil_stroke_select_index_set(gpd, gps);
for (int i = 0; i < gpc->tot_curve_points; i++) {
bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
@@ -804,6 +1265,7 @@ void BKE_gpencil_editcurve_stroke_sync_selection(bGPdata *UNUSED(gpd),
else {
gpc->flag &= ~GP_CURVE_SELECT;
gpencil_editstroke_deselect_all(gpc);
+ BKE_gpencil_stroke_select_index_reset(gps);
}
}
@@ -814,7 +1276,9 @@ void BKE_gpencil_stroke_editcurve_sync_selection(bGPdata *gpd, bGPDstroke *gps,
{
if (gpc->flag & GP_CURVE_SELECT) {
gps->flag |= GP_STROKE_SELECT;
- BKE_gpencil_stroke_select_index_set(gpd, gps);
+ if (gpd != NULL) {
+ BKE_gpencil_stroke_select_index_set(gpd, gps);
+ }
for (int i = 0; i < gpc->tot_curve_points - 1; i++) {
bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
@@ -868,7 +1332,9 @@ void BKE_gpencil_stroke_editcurve_sync_selection(bGPdata *gpd, bGPDstroke *gps,
}
else {
gps->flag &= ~GP_STROKE_SELECT;
- BKE_gpencil_stroke_select_index_reset(gps);
+ if (gpd != NULL) {
+ BKE_gpencil_stroke_select_index_reset(gps);
+ }
for (int i = 0; i < gps->totpoints; i++) {
bGPDspoint *pt = &gps->points[i];
pt->flag &= ~GP_SPOINT_SELECT;
@@ -876,28 +1342,36 @@ void BKE_gpencil_stroke_editcurve_sync_selection(bGPdata *gpd, bGPDstroke *gps,
}
}
-static void gpencil_interpolate_fl_from_to(
+static float smooth_interpf(const float target, const float origin, const float t)
+{
+ float fac = 3.0f * t * t - 2.0f * t * t * t; // smooth
+ return interpf(target, origin, fac);
+}
+
+static void smooth_interp_v4_v4v4(float *r, const float *a, const float *b, const float t)
+{
+ float fac = 3.0f * t * t - 2.0f * t * t * t; // smooth
+ interp_v4_v4v4(r, a, b, fac);
+}
+
+static void gpencil_interp_stride_fl_from_to(
float from, float to, float *point_offset, int it, int stride)
{
/* smooth interpolation */
float *r = point_offset;
for (int i = 0; i <= it; i++) {
- float fac = (float)i / (float)it;
- fac = 3.0f * fac * fac - 2.0f * fac * fac * fac; // smooth
- *r = interpf(to, from, fac);
+ *r = smooth_interpf(to, from, (float)i / (float)it);
r = POINTER_OFFSET(r, stride);
}
}
-static void gpencil_interpolate_v4_from_to(
+static void gpencil_interp_stride_v4_from_to(
float from[4], float to[4], float *point_offset, int it, int stride)
{
/* smooth interpolation */
float *r = point_offset;
for (int i = 0; i <= it; i++) {
- float fac = (float)i / (float)it;
- fac = 3.0f * fac * fac - 2.0f * fac * fac * fac; // smooth
- interp_v4_v4v4(r, from, to, fac);
+ smooth_interp_v4_v4v4(r, from, to, (float)i / (float)it);
r = POINTER_OFFSET(r, stride);
}
}
@@ -916,49 +1390,66 @@ static float gpencil_approximate_curve_segment_arclength(bGPDcurve_point *cpt_st
return (chord_len + net_len) / 2.0f;
}
-static void gpencil_calculate_stroke_points_curve_segment(
- bGPDcurve_point *cpt, bGPDcurve_point *cpt_next, float *points_offset, int resolu, int stride)
+/* Helper: Interpolate curve point attributes from curve point pair into point array. */
+static void gpencil_calculate_stroke_points_curve_segment(bGPDcurve_point *cpt,
+ bGPDcurve_point *cpt_next,
+ float *points_offset,
+ int resolu,
+ int stride,
+ const eGPStrokeGeoUpdateFlag flag)
{
- /* sample points on all 3 axis between two curve points */
- for (uint axis = 0; axis < 3; axis++) {
- BKE_curve_forward_diff_bezier(cpt->bezt.vec[1][axis],
- cpt->bezt.vec[2][axis],
- cpt_next->bezt.vec[0][axis],
- cpt_next->bezt.vec[1][axis],
- POINTER_OFFSET(points_offset, sizeof(float) * axis),
- (int)resolu,
- stride);
+ const bool update_all_attributes = (flag == GP_GEO_UPDATE_DEFAULT);
+
+ if (update_all_attributes || (flag & GP_GEO_UPDATE_POLYLINE_POSITION)) {
+ /* sample points on all 3 axis between two curve points */
+ for (uint axis = 0; axis < 3; axis++) {
+ BKE_curve_forward_diff_bezier(cpt->bezt.vec[1][axis],
+ cpt->bezt.vec[2][axis],
+ cpt_next->bezt.vec[0][axis],
+ cpt_next->bezt.vec[1][axis],
+ POINTER_OFFSET(points_offset, sizeof(float) * axis),
+ (int)resolu,
+ stride);
+ }
}
/* interpolate other attributes */
- gpencil_interpolate_fl_from_to(cpt->pressure,
- cpt_next->pressure,
- POINTER_OFFSET(points_offset, sizeof(float) * 3),
- resolu,
- stride);
- gpencil_interpolate_fl_from_to(cpt->strength,
- cpt_next->strength,
- POINTER_OFFSET(points_offset, sizeof(float) * 4),
- resolu,
- stride);
- gpencil_interpolate_v4_from_to(cpt->vert_color,
- cpt_next->vert_color,
- POINTER_OFFSET(points_offset, sizeof(float) * 5),
- resolu,
- stride);
+ if (update_all_attributes || (flag & GP_GEO_UPDATE_POLYLINE_PRESSURE)) {
+ gpencil_interp_stride_fl_from_to(cpt->pressure,
+ cpt_next->pressure,
+ POINTER_OFFSET(points_offset, sizeof(float) * 3),
+ resolu,
+ stride);
+ }
+ if (update_all_attributes || (flag & GP_GEO_UPDATE_POLYLINE_STRENGTH)) {
+ gpencil_interp_stride_fl_from_to(cpt->strength,
+ cpt_next->strength,
+ POINTER_OFFSET(points_offset, sizeof(float) * 4),
+ resolu,
+ stride);
+ }
+ if (update_all_attributes || (flag & GP_GEO_UPDATE_POLYLINE_COLOR)) {
+ gpencil_interp_stride_v4_from_to(cpt->vert_color,
+ cpt_next->vert_color,
+ POINTER_OFFSET(points_offset, sizeof(float) * 5),
+ resolu,
+ stride);
+ }
}
static float *gpencil_stroke_points_from_editcurve_adaptive_resolu(
bGPDcurve_point *curve_point_array,
int curve_point_array_len,
int resolution,
+ const uint num_segments,
bool is_cyclic,
- int *r_points_len)
+ const eGPStrokeGeoUpdateFlag flag,
+ int *r_points_len,
+ int **r_segment_lengths)
{
/* One stride contains: x, y, z, pressure, strength, Vr, Vg, Vb, Vmix_factor */
const uint stride = sizeof(float[9]);
const uint cpt_last = curve_point_array_len - 1;
- const uint num_segments = (is_cyclic) ? curve_point_array_len : curve_point_array_len - 1;
int *segment_point_lengths = MEM_callocN(sizeof(int) * num_segments, __func__);
uint points_len = 1;
@@ -992,7 +1483,7 @@ static float *gpencil_stroke_points_from_editcurve_adaptive_resolu(
bGPDcurve_point *cpt_next = &curve_point_array[i + 1];
int segment_resolu = segment_point_lengths[i];
gpencil_calculate_stroke_points_curve_segment(
- cpt_curr, cpt_next, points_offset, segment_resolu, stride);
+ cpt_curr, cpt_next, points_offset, segment_resolu, stride, flag);
/* update the index */
cpt_curr->point_index = point_index;
point_index += segment_resolu;
@@ -1005,12 +1496,11 @@ static float *gpencil_stroke_points_from_editcurve_adaptive_resolu(
bGPDcurve_point *cpt_next = &curve_point_array[0];
int segment_resolu = segment_point_lengths[cpt_last];
gpencil_calculate_stroke_points_curve_segment(
- cpt_curr, cpt_next, points_offset, segment_resolu, stride);
+ cpt_curr, cpt_next, points_offset, segment_resolu, stride, flag);
}
- MEM_freeN(segment_point_lengths);
-
*r_points_len = points_len;
+ *r_segment_lengths = segment_point_lengths;
return (float(*))r_points;
}
@@ -1021,6 +1511,7 @@ static float *gpencil_stroke_points_from_editcurve_fixed_resolu(bGPDcurve_point
int curve_point_array_len,
int resolution,
bool is_cyclic,
+ const eGPStrokeGeoUpdateFlag flag,
int *r_points_len)
{
/* One stride contains: x, y, z, pressure, strength, Vr, Vg, Vb, Vmix_factor */
@@ -1037,7 +1528,7 @@ static float *gpencil_stroke_points_from_editcurve_fixed_resolu(bGPDcurve_point
bGPDcurve_point *cpt_next = &curve_point_array[i + 1];
gpencil_calculate_stroke_points_curve_segment(
- cpt_curr, cpt_next, points_offset, resolution, stride);
+ cpt_curr, cpt_next, points_offset, resolution, stride, flag);
/* update the index */
cpt_curr->point_index = i * resolution;
points_offset = POINTER_OFFSET(points_offset, resolu_stride);
@@ -1048,7 +1539,7 @@ static float *gpencil_stroke_points_from_editcurve_fixed_resolu(bGPDcurve_point
if (is_cyclic) {
bGPDcurve_point *cpt_next = &curve_point_array[0];
gpencil_calculate_stroke_points_curve_segment(
- cpt_curr, cpt_next, points_offset, resolution, stride);
+ cpt_curr, cpt_next, points_offset, resolution, stride, flag);
}
*r_points_len = points_len;
@@ -1060,15 +1551,17 @@ static float *gpencil_stroke_points_from_editcurve_fixed_resolu(bGPDcurve_point
*/
void BKE_gpencil_stroke_update_geometry_from_editcurve(bGPDstroke *gps,
const uint resolution,
- const bool adaptive)
+ const bool adaptive,
+ const eGPStrokeGeoUpdateFlag flag)
{
if (gps == NULL || gps->editcurve == NULL) {
return;
}
+ const bool update_all_attributes = (flag == GP_GEO_UPDATE_DEFAULT);
- bGPDcurve *editcurve = gps->editcurve;
- bGPDcurve_point *curve_point_array = editcurve->curve_points;
- int curve_point_array_len = editcurve->tot_curve_points;
+ bGPDcurve *gpc = gps->editcurve;
+ bGPDcurve_point *curve_point_array = gpc->curve_points;
+ int curve_point_array_len = gpc->tot_curve_points;
if (curve_point_array_len == 0) {
return;
}
@@ -1078,17 +1571,30 @@ void BKE_gpencil_stroke_update_geometry_from_editcurve(bGPDstroke *gps,
/* resize stroke point array */
gps->totpoints = 1;
gps->points = MEM_recallocN(gps->points, sizeof(bGPDspoint) * gps->totpoints);
- if (gps->dvert != NULL) {
- gps->dvert = MEM_recallocN(gps->dvert, sizeof(MDeformVert) * gps->totpoints);
- }
bGPDspoint *pt = &gps->points[0];
- copy_v3_v3(&pt->x, cpt->bezt.vec[1]);
+ if (update_all_attributes || (flag & GP_GEO_UPDATE_POLYLINE_POSITION)) {
+ copy_v3_v3(&pt->x, cpt->bezt.vec[1]);
+ }
+ if (update_all_attributes || (flag & GP_GEO_UPDATE_POLYLINE_PRESSURE)) {
+ pt->pressure = cpt->pressure;
+ }
+ if (update_all_attributes || (flag & GP_GEO_UPDATE_POLYLINE_STRENGTH)) {
+ pt->strength = cpt->strength;
+ }
+ if (update_all_attributes || (flag & GP_GEO_UPDATE_POLYLINE_COLOR)) {
+ copy_v4_v4(pt->vert_color, cpt->vert_color);
+ }
- pt->pressure = cpt->pressure;
- pt->strength = cpt->strength;
+ if (gpc->dvert != NULL && (update_all_attributes || (flag & GP_GEO_UPDATE_POLYLINE_WEIGHT))) {
+ gps->dvert = MEM_recallocN(gps->dvert, sizeof(MDeformVert) * gps->totpoints);
- copy_v4_v4(pt->vert_color, cpt->vert_color);
+ /* Copy the weights of the curve point. */
+ MDeformVert *dv_first = &gpc->dvert[0];
+ MDeformVert *dvert = &gps->dvert[0];
+ BKE_defvert_copy(dvert, dv_first);
+ BKE_defvert_sync(dvert, dv_first, true);
+ }
/* deselect */
pt->flag &= ~GP_SPOINT_SELECT;
@@ -1102,65 +1608,161 @@ void BKE_gpencil_stroke_update_geometry_from_editcurve(bGPDstroke *gps,
int points_len = 0;
float(*points)[9] = NULL;
+ int *segment_length_cache = NULL;
+ const uint num_segments = (is_cyclic) ? curve_point_array_len : curve_point_array_len - 1;
if (adaptive) {
points = (float(*)[9])gpencil_stroke_points_from_editcurve_adaptive_resolu(
- curve_point_array, curve_point_array_len, resolution, is_cyclic, &points_len);
+ curve_point_array,
+ curve_point_array_len,
+ resolution,
+ num_segments,
+ is_cyclic,
+ flag,
+ &points_len,
+ &segment_length_cache);
}
else {
points = (float(*)[9])gpencil_stroke_points_from_editcurve_fixed_resolu(
- curve_point_array, curve_point_array_len, resolution, is_cyclic, &points_len);
+ curve_point_array, curve_point_array_len, resolution, is_cyclic, flag, &points_len);
}
if (points == NULL || points_len == 0) {
return;
}
+ /* We have to free all of the old weight data and replace it completely. */
+ BKE_gpencil_free_stroke_weights(gps);
+
/* resize stroke point array */
gps->totpoints = points_len;
gps->points = MEM_recallocN(gps->points, sizeof(bGPDspoint) * gps->totpoints);
- if (gps->dvert != NULL) {
- gps->dvert = MEM_recallocN(gps->dvert, sizeof(MDeformVert) * gps->totpoints);
- }
+ int idx = 0;
/* write new data to stroke point array */
for (int i = 0; i < points_len; i++) {
+ bGPDcurve_point *gpc_pt = &curve_point_array[idx % curve_point_array_len];
bGPDspoint *pt = &gps->points[i];
- copy_v3_v3(&pt->x, &points[i][0]);
-
- pt->pressure = points[i][3];
- pt->strength = points[i][4];
-
- copy_v4_v4(pt->vert_color, &points[i][5]);
+ if (update_all_attributes || (flag & GP_GEO_UPDATE_POLYLINE_POSITION)) {
+ copy_v3_v3(&pt->x, &points[i][0]);
+ }
+ if (update_all_attributes || (flag & GP_GEO_UPDATE_POLYLINE_PRESSURE)) {
+ pt->pressure = points[i][3];
+ }
+ if (update_all_attributes || (flag & GP_GEO_UPDATE_POLYLINE_STRENGTH)) {
+ pt->strength = points[i][4];
+ }
+ if (update_all_attributes || (flag & GP_GEO_UPDATE_POLYLINE_COLOR)) {
+ copy_v4_v4(pt->vert_color, &points[i][5]);
+ }
/* deselect points */
pt->flag &= ~GP_SPOINT_SELECT;
+
+ if (gpc_pt->point_index == i) {
+ pt->flag |= GP_SPOINT_IS_BEZT_CONTROL;
+ idx++;
+ }
}
gps->flag &= ~GP_STROKE_SELECT;
BKE_gpencil_stroke_select_index_reset(gps);
+ /* Interpolate weights. */
+ if (gpc->dvert != NULL && (update_all_attributes || (flag & GP_GEO_UPDATE_POLYLINE_WEIGHT))) {
+ gps->dvert = MEM_callocN(sizeof(MDeformVert) * gps->totpoints, __func__);
+
+ idx = 0;
+ for (int i = 0; i < gpc->tot_curve_points - 1; i++) {
+ MDeformVert *dv_curr = &gpc->dvert[i];
+ MDeformVert *dv_next = &gpc->dvert[i + 1];
+ int segment_length = (adaptive) ? segment_length_cache[i] : resolution;
+ int totweight = max_ii(dv_curr->totweight, dv_next->totweight);
+
+ if (totweight == 0) {
+ idx += segment_length;
+ continue;
+ }
+
+ for (int j = 0; j < segment_length; j++) {
+ MDeformVert *dvert = &gps->dvert[idx + j];
+ BKE_defvert_copy(dvert, dv_curr);
+ float t = (float)j / (float)segment_length;
+ for (int d = 0; d < totweight; d++) {
+ MDeformWeight *dw_a = BKE_defvert_ensure_index(dv_curr, d);
+ MDeformWeight *dw_b = BKE_defvert_ensure_index(dv_next, d);
+ MDeformWeight *dw_final = BKE_defvert_ensure_index(dvert, d);
+
+ if (dw_a->weight == dw_b->weight) {
+ dw_final->weight = dw_a->weight;
+ }
+ else {
+ dw_final->weight = smooth_interpf(dw_b->weight, dw_a->weight, t);
+ }
+ }
+ }
+
+ idx += segment_length;
+ }
+
+ if (is_cyclic) {
+ /* Interpolate weights between last and first curve point. */
+ MDeformVert *dv_curr = &gpc->dvert[gpc->tot_curve_points - 1];
+ MDeformVert *dv_next = &gpc->dvert[0];
+ int segment_length = (adaptive) ? segment_length_cache[gpc->tot_curve_points - 1] :
+ resolution;
+ int totweight = max_ii(dv_curr->totweight, dv_next->totweight);
+
+ if (totweight) {
+ for (int j = 0; j < segment_length; j++) {
+ MDeformVert *dvert = &gps->dvert[idx + j];
+ BKE_defvert_copy(dvert, dv_curr);
+ float t = (float)j / (float)segment_length;
+ for (int d = 0; d < totweight; d++) {
+ MDeformWeight *dw_a = BKE_defvert_ensure_index(dv_curr, d);
+ MDeformWeight *dw_b = BKE_defvert_ensure_index(dv_next, d);
+ MDeformWeight *dw_final = BKE_defvert_ensure_index(dvert, d);
+
+ if (dw_a->weight == dw_b->weight) {
+ dw_final->weight = dw_a->weight;
+ }
+ else {
+ dw_final->weight = smooth_interpf(dw_b->weight, dw_a->weight, t);
+ }
+ }
+ }
+ }
+ }
+ else {
+ /* Copy the weights of the last curve point. */
+ MDeformVert *dv_last = &gpc->dvert[gpc->tot_curve_points - 1];
+ MDeformVert *dvert = &gps->dvert[gps->totpoints - 1];
+ BKE_defvert_copy(dvert, dv_last);
+ BKE_defvert_sync(dvert, dv_last, true);
+ }
+ }
+
/* free temp data */
MEM_freeN(points);
+ MEM_SAFE_FREE(segment_length_cache);
}
/**
* Recalculate the handles of the edit curve of a grease pencil stroke
*/
-void BKE_gpencil_editcurve_recalculate_handles(bGPDstroke *gps)
+bool BKE_gpencil_editcurve_recalculate_handles(bGPDstroke *gps)
{
if (gps == NULL || gps->editcurve == NULL) {
- return;
+ return false;
}
bool changed = false;
bGPDcurve *gpc = gps->editcurve;
if (gpc->tot_curve_points < 2) {
- return;
+ return false;
}
if (gpc->tot_curve_points == 1) {
BKE_nurb_handle_calc(
&(gpc->curve_points[0].bezt), NULL, &(gpc->curve_points[0].bezt), false, 0);
- gps->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
}
for (int i = 1; i < gpc->tot_curve_points - 1; i++) {
@@ -1183,34 +1785,26 @@ void BKE_gpencil_editcurve_recalculate_handles(bGPDstroke *gps)
bGPDcurve_point *gpc_last = &gpc->curve_points[gpc->tot_curve_points - 1];
bGPDcurve_point *gpc_first_next = &gpc->curve_points[1];
bGPDcurve_point *gpc_last_prev = &gpc->curve_points[gpc->tot_curve_points - 2];
- if (gps->flag & GP_STROKE_CYCLIC) {
- if (gpc_first->flag & GP_CURVE_POINT_SELECT || gpc_last->flag & GP_CURVE_POINT_SELECT) {
- BezTriple *bezt_first = &gpc_first->bezt;
- BezTriple *bezt_last = &gpc_last->bezt;
- BezTriple *bezt_first_next = &gpc_first_next->bezt;
- BezTriple *bezt_last_prev = &gpc_last_prev->bezt;
+ if (gpc_first->flag & GP_CURVE_POINT_SELECT || gpc_last->flag & GP_CURVE_POINT_SELECT) {
+ BezTriple *bezt_first = &gpc_first->bezt;
+ BezTriple *bezt_last = &gpc_last->bezt;
+ BezTriple *bezt_first_next = &gpc_first_next->bezt;
+ BezTriple *bezt_last_prev = &gpc_last_prev->bezt;
+
+ if (gps->flag & GP_STROKE_CYCLIC) {
BKE_nurb_handle_calc(bezt_first, bezt_last, bezt_first_next, false, 0);
BKE_nurb_handle_calc(bezt_last, bezt_last_prev, bezt_first, false, 0);
- changed = true;
}
- }
- else {
- if (gpc_first->flag & GP_CURVE_POINT_SELECT || gpc_last->flag & GP_CURVE_POINT_SELECT) {
- BezTriple *bezt_first = &gpc_first->bezt;
- BezTriple *bezt_last = &gpc_last->bezt;
- BezTriple *bezt_first_next = &gpc_first_next->bezt;
- BezTriple *bezt_last_prev = &gpc_last_prev->bezt;
-
+ else {
BKE_nurb_handle_calc(bezt_first, NULL, bezt_first_next, false, 0);
BKE_nurb_handle_calc(bezt_last, bezt_last_prev, NULL, false, 0);
- changed = true;
}
- }
- if (changed) {
- gps->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
+ changed = true;
}
+
+ return changed;
}
/* Helper: count how many new curve points must be generated. */
@@ -1238,6 +1832,46 @@ static int gpencil_editcurve_subdivide_count(bGPDcurve *gpc, bool is_cyclic)
return count;
}
+/* Helper: Divide the curve segment at t, where t is a value between 0 and 1. */
+static void gpencil_editcurve_subdivide_curve_segment_factor(bGPDcurve_point *cpt_start,
+ bGPDcurve_point *cpt_end,
+ bGPDcurve_point *cpt_new,
+ const float t)
+{
+ BLI_assert(t >= 0.0f && t <= 1.0f);
+
+ BezTriple *bezt_start = &cpt_start->bezt;
+ BezTriple *bezt_end = &cpt_end->bezt;
+ BezTriple *bezt_new = &cpt_new->bezt;
+ const float z = 1 - t, tt = t * t, zz = z * z, ttt = t * t * t, zzz = z * z * z;
+
+ for (int axis = 0; axis < 3; axis++) {
+ float p0, p1, p2, p3, m0, m1, q0, q1, b;
+ p0 = bezt_start->vec[1][axis];
+ p1 = bezt_start->vec[2][axis];
+ p2 = bezt_end->vec[0][axis];
+ p3 = bezt_end->vec[1][axis];
+
+ m0 = (t * p1) + (z * p0);
+ q0 = (tt * p2) + 2 * (t * z * p1) + (zz * p0);
+ b = (ttt * p3) + 3 * (tt * z * p2) + 3 * (t * zz * p1) + (zzz * p0);
+ q1 = (tt * p3) + 2 * (t * z * p2) + (zz * p1);
+ m1 = (t * p3) + (z * p2);
+
+ bezt_new->vec[0][axis] = q0;
+ bezt_new->vec[1][axis] = b;
+ bezt_new->vec[2][axis] = q1;
+
+ bezt_start->vec[2][axis] = m0;
+ bezt_end->vec[0][axis] = m1;
+ }
+
+ cpt_new->pressure = interpf(cpt_end->pressure, cpt_start->pressure, t);
+ cpt_new->strength = interpf(cpt_end->strength, cpt_start->strength, t);
+ interp_v4_v4v4(cpt_new->vert_color, cpt_start->vert_color, cpt_end->vert_color, t);
+ /* TODO: interpolate weights */
+}
+
static void gpencil_editcurve_subdivide_curve_segment(bGPDcurve_point *cpt_start,
bGPDcurve_point *cpt_end,
bGPDcurve_point *cpt_new)
@@ -1280,6 +1914,9 @@ void BKE_gpencil_editcurve_subdivide(bGPDstroke *gps, const int cuts)
bool is_cyclic = gps->flag & GP_STROKE_CYCLIC;
/* repeat for number of cuts */
+ /* TODO: Ideally the subdivisions should match how it works in the rest of Blender. Here we get
+ * 1->1, 2->3, 3->7 etc. because we keep supdividing already subdivided segments. But it would be
+ * better to get the exact number of cuts per segment.*/
for (int s = 0; s < cuts; s++) {
int old_tot_curve_points = gpc->tot_curve_points;
int new_num_curve_points = gpencil_editcurve_subdivide_count(gpc, is_cyclic);
@@ -1352,66 +1989,510 @@ void BKE_gpencil_editcurve_subdivide(bGPDstroke *gps, const int cuts)
}
}
-void BKE_gpencil_strokes_selected_update_editcurve(bGPdata *gpd)
+static void gpencil_refit_single_from_to(bGPDstroke *gps,
+ bGPDcurve *gpc,
+ bGPDcurve_point *new_points,
+ int from_start,
+ int from_end,
+ int to_start,
+ int to_end,
+ float error_threshold)
{
- const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
- /* For all selected strokes, update edit curve. */
- LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
- if (!BKE_gpencil_layer_is_editable(gpl)) {
- continue;
+ bGPDcurve_point *cpt_start = &gpc->curve_points[from_start];
+ bGPDcurve_point *cpt_end = &gpc->curve_points[from_end];
+ bGPDcurve_point *new_cpt_start = &new_points[to_start];
+ bGPDcurve_point *new_cpt_end = &new_points[to_end];
+
+ BKE_gpencil_stroke_editcurve_regenerate_single(gps, from_start, from_end, error_threshold);
+
+ memcpy(new_cpt_start, cpt_start, sizeof(bGPDcurve_point));
+ memcpy(new_cpt_end, cpt_end, sizeof(bGPDcurve_point));
+}
+
+/**
+ * Dissolves the curve points tagged with `flag`.
+ * \param gps: The stroke.
+ * \param flag: flag (see eGPDcurve_point_Flag).
+ * \param refit_segments: Do a refit of the segments where points have been dissolved.
+ * \param error_threshold: Refit threshold when refitting.
+ *
+ * \returns The number of points that remain after dissolving. Can be 0 in which case the caller
+ * should delete the stroke.
+ */
+int BKE_gpencil_editcurve_dissolve(bGPDstroke *gps,
+ const uint flag,
+ const bool refit_segments,
+ const float error_threshold)
+{
+ bGPDcurve *gpc = gps->editcurve;
+ if (gpc == NULL || gpc->tot_curve_points < 2) {
+ return gpc->tot_curve_points;
+ }
+ const bool is_cyclic = (gps->flag & GP_STROKE_CYCLIC);
+
+ int first = -1, last = 0;
+ int num_points_remaining = gpc->tot_curve_points;
+ for (int i = 0; i < gpc->tot_curve_points; i++) {
+ bGPDcurve_point *cpt = &gpc->curve_points[i];
+ if ((cpt->flag & flag)) {
+ num_points_remaining--;
}
- bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe;
- for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
- if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && is_multiedit)) {
- LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
- /* skip deselected stroke */
- if (!(gps->flag & GP_STROKE_SELECT)) {
- continue;
+ else {
+ if (first < 0) {
+ first = i;
+ }
+ last = i;
+ }
+ }
+
+ /* All points will be deleted. */
+ if (num_points_remaining < 1) {
+ return 0;
+ }
+ /* All points remain. */
+ else if (num_points_remaining == gpc->tot_curve_points) {
+ return gpc->tot_curve_points;
+ }
+
+ bGPDcurve_point *new_points = MEM_callocN(sizeof(bGPDcurve_point) * num_points_remaining,
+ __func__);
+
+ int new_idx = 0, start = first, end = first;
+ for (int i = first; i < gpc->tot_curve_points; i++) {
+ bGPDcurve_point *cpt = &gpc->curve_points[i];
+ bGPDcurve_point *new_cpt = &new_points[new_idx];
+ if ((cpt->flag & flag) == 0) {
+ memcpy(new_cpt, cpt, sizeof(bGPDcurve_point));
+ /* Check that the indices are in bounds. */
+ if (refit_segments && (start > 0) && IN_RANGE(end, start, gpc->tot_curve_points)) {
+ /* Refit this segment. */
+ gpencil_refit_single_from_to(
+ gps, gpc, new_points, start - 1, end, new_idx - 1, new_idx, error_threshold);
+ start = end = i;
+ }
+ new_idx++;
+ start++;
+ }
+ end++;
+ }
+
+ if (is_cyclic && refit_segments) {
+ /* Make sure that there is at least one segment that needs updating. */
+ if (last != first && first >= 0 && last >= 0 &&
+ !(first == 0 && last == (gpc->tot_curve_points - 1))) {
+
+ /* Refit this segment. */
+ gpencil_refit_single_from_to(
+ gps, gpc, new_points, last, first, num_points_remaining - 1, 0, error_threshold);
+ }
+ }
+
+ /* Recreate array. */
+ if (gpc->curve_points != NULL) {
+ MEM_freeN(gpc->curve_points);
+ }
+
+ gpc->curve_points = new_points;
+ gpc->tot_curve_points = num_points_remaining;
+ BKE_gpencil_editcurve_recalculate_handles(gps);
+
+ return num_points_remaining;
+}
+
+/* Helper: Dissolve the curve point with the lowest re-fit error. Dissolve only if error is under
+ * `threshold`. Returns true if a point was dissolved. */
+static bool gpencil_editcurve_dissolve_smallest_error_curve_point(bGPDstroke *gps,
+ bGPDcurve *gpc,
+ const float diag_length,
+ const bool is_cyclic,
+ const float threshold)
+{
+ bool changed = false;
+ /* Duplicate the curve point array. */
+ bGPDcurve_point *curve_point_array = MEM_dupallocN(gpc->curve_points);
+
+ float lowest_error = FLT_MAX;
+ int lowest_error_idx = 0;
+ /* Loop over control point pairs with one control point in between.
+ * Find the control point that produces the lowest error when removed. */
+ for (int i = 0; i < gpc->tot_curve_points - 2; i++) {
+ bGPDcurve_point *cpt_prev = &curve_point_array[i];
+ bGPDcurve_point *cpt_next = &curve_point_array[i + 2];
+
+ float error_sq;
+ /* Regenerate the handles between cpt_prev and cpt_next as if the point in the middle didn't
+ * exist. Get the re-fit error. */
+ gpencil_stroke_editcurve_regenerate_single_ex(cpt_prev,
+ cpt_next,
+ gps->points,
+ gps->totpoints,
+ gps->boundbox_min,
+ diag_length,
+ is_cyclic,
+ 0.0f,
+ &error_sq);
+
+ if (error_sq < lowest_error) {
+ lowest_error = error_sq;
+ lowest_error_idx = i + 1;
+ }
+ }
+
+ /* Dissolve the control point with the lowest error found. */
+ if (sqrtf(lowest_error) < threshold) {
+ int new_tot_curve_points = gpc->tot_curve_points - 1;
+ bGPDcurve_point *new_points = MEM_callocN(sizeof(bGPDcurve_point) * new_tot_curve_points,
+ __func__);
+ /* Copy all other points. Skip the point that will be dissolved. */
+ memcpy(new_points, gpc->curve_points, lowest_error_idx * sizeof(bGPDcurve_point));
+ memcpy(new_points + lowest_error_idx,
+ gpc->curve_points + lowest_error_idx + 1,
+ (new_tot_curve_points - lowest_error_idx) * sizeof(bGPDcurve_point));
+
+ /* Get the start and end points of the segment. */
+ bGPDcurve_point *cpt_start = &curve_point_array[lowest_error_idx - 1];
+ bGPDcurve_point *cpt_end = &curve_point_array[lowest_error_idx + 1];
+ bGPDcurve_point *new_cpt_start = &new_points[lowest_error_idx - 1];
+ bGPDcurve_point *new_cpt_end = &new_points[lowest_error_idx];
+
+ /* Write the new handles. */
+ copy_v3_v3(new_cpt_start->bezt.vec[2], cpt_start->bezt.vec[2]);
+ copy_v3_v3(new_cpt_end->bezt.vec[0], cpt_end->bezt.vec[0]);
+
+ /* Overwrite curve points. */
+ MEM_freeN(gpc->curve_points);
+ gpc->curve_points = new_points;
+ gpc->tot_curve_points = new_tot_curve_points;
+
+ changed = true;
+ }
+
+ MEM_freeN(curve_point_array);
+
+ return changed;
+}
+
+/**
+ * Simplify Adaptive
+ * Dissolves all the curve points with a refit-error that is less than threshold.
+ */
+void BKE_gpencil_editcurve_simplify_adaptive(bGPDstroke *gps, const float threshold)
+{
+ bGPDcurve *gpc = gps->editcurve;
+ if (gpc == NULL || gpc->tot_curve_points < 3) {
+ return;
+ }
+ const bool is_cyclic = gps->flag & GP_STROKE_CYCLIC;
+ const float diag_length = len_v3v3(gps->boundbox_min, gps->boundbox_max);
+
+ bool changed = true;
+ /* Loop until we have removed all points that causes an error less than `threshold`. */
+ while (gpc->tot_curve_points > 2 && changed) {
+ changed = gpencil_editcurve_dissolve_smallest_error_curve_point(
+ gps, gpc, diag_length, is_cyclic, threshold);
+ }
+}
+
+/**
+ * Simplify Fixed
+ * Dissolves `count` curve points with the lowest refit-error.
+ */
+void BKE_gpencil_editcurve_simplify_fixed(bGPDstroke *gps, const int count)
+{
+ bGPDcurve *gpc = gps->editcurve;
+ if (gpc == NULL || gpc->tot_curve_points < 3 || count < 1) {
+ return;
+ }
+ const bool is_cyclic = gps->flag & GP_STROKE_CYCLIC;
+ const float diag_length = len_v3v3(gps->boundbox_min, gps->boundbox_max);
+
+ /* Loop until we have removed all points that causes an error less than `threshold`. */
+ for (int i = count; i >= 0 && gpc->tot_curve_points > 2; i--) {
+ gpencil_editcurve_dissolve_smallest_error_curve_point(
+ gps, gpc, diag_length, is_cyclic, FLT_MAX);
+ }
+}
+
+/**
+ * Smooth curve extra
+ * \param gps: The grease pencil stroke.
+ * \param factor: Smoothing factor.
+ * \param step_size: Number of adjacent points to consider when smoothing.
+ * \param repeat: Number of times to repeat the smoothing process.
+ * \param only_selected: Only smooth selected points.
+ * \param affect_endpoints: Include end-points when smoothing (only used when stroke is
+ * non-cyclic).
+ * \param use_vertex_groups: Use vertex group to mask the smoothing effect.
+ * \param invert_weights: Invert the effect when using vertex groups.
+ * \param deform_group: The vertex group.
+ * \param curve_mapping: If not NULL use the curve mapping along the stroke to weigh the influence
+ * of the smoothing effect.
+ * \param do_positions: Smooth positions.
+ * \param do_pressure: Smooth pressure.
+ * \param do_strength: Smooth strength.
+ */
+void BKE_gpencil_editcurve_smooth_ex(bGPDstroke *gps,
+ const float factor,
+ const uint step_size,
+ const uint repeat,
+ const bool only_selected,
+ const bool affect_endpoints,
+ const bool use_vertex_groups,
+ const bool invert_weights,
+ const int deform_group,
+ const CurveMapping *curve_mapping,
+ const bool do_positions,
+ const bool do_pressure,
+ const bool do_strength)
+{
+ bGPDcurve *gpc = gps->editcurve;
+ if (gpc == NULL || gpc->tot_curve_points < 2 || factor == 0.0f || repeat < 1) {
+ return;
+ }
+
+ if (!(do_positions || do_pressure || do_strength)) {
+ return;
+ }
+
+ const bool is_cyclic = gps->flag & GP_STROKE_CYCLIC;
+ const bool use_curve_mapping = (curve_mapping != NULL);
+
+ /* Total number of cubic points. */
+ const uint tot_points = gpc->tot_curve_points * 3;
+ for (uint r = 0; r < repeat; r++) {
+ uint start_idx = (affect_endpoints || is_cyclic) ? 0 : 2;
+ uint end_idx = (affect_endpoints || is_cyclic) ? tot_points : tot_points - 2;
+
+ for (uint i = start_idx; i < end_idx; i++) {
+ /* bGPDcurve_point index */
+ uint pt_idx = i / 3;
+ /* BezTriple handle index */
+ uint hd_idx = i % 3;
+
+ bGPDcurve_point *gpc_pt = &gpc->curve_points[pt_idx];
+ MDeformVert *dvert = &gpc->dvert[pt_idx];
+ if ((gpc_pt->flag & GP_CURVE_POINT_SELECT) == 0 && only_selected) {
+ continue;
+ }
+
+ float weight = 1.0f;
+ if (use_vertex_groups) {
+ weight = get_point_weight(dvert, invert_weights, deform_group);
+ if (weight < 0.0f) {
+ continue;
+ }
+ }
+
+ if (curve_mapping != NULL) {
+ float value = (float)i / (float)(tot_points - 1);
+ weight *= BKE_curvemapping_evaluateF(curve_mapping, 0, value);
+ if (weight < 0.0f) {
+ continue;
+ }
+ }
+
+ BezTriple *bezt = &gpc_pt->bezt;
+ float sco[3] = {0.0f};
+ float smoothed_pressure = 0.0f;
+ float smoothed_strength = 0.0f;
+
+ float average_fac = 1.0f / (float)(step_size * 2 + 1);
+ float weighted_factor = factor;
+ if (use_vertex_groups || use_curve_mapping) {
+ weighted_factor *= weight;
+ }
+
+ if (do_positions) {
+ /* Include the current point. */
+ madd_v3_v3fl(sco, bezt->vec[hd_idx], average_fac);
+
+ for (uint step = 1; step <= step_size; step++) {
+ int prev_idx = i - step;
+ int next_idx = i + step;
+
+ if (is_cyclic) {
+ prev_idx = mod_i(prev_idx, tot_points);
+ next_idx = next_idx % tot_points;
+ }
+ else {
+ CLAMP_MIN(prev_idx, 0);
+ CLAMP_MAX(next_idx, tot_points - 1);
}
- /* Generate the curve if there is none or the stroke was changed */
- if (gps->editcurve == NULL) {
- BKE_gpencil_stroke_editcurve_update(gpd, gpl, gps);
- /* Continue if curve could not be generated. */
- if (gps->editcurve == NULL) {
- continue;
- }
+ uint prev_pt_idx = prev_idx / 3;
+ uint prev_hd_idx = prev_idx % 3;
+ uint next_pt_idx = next_idx / 3;
+ uint next_hd_idx = next_idx % 3;
+
+ bGPDcurve_point *gpc_prev_pt = &gpc->curve_points[prev_pt_idx];
+ bGPDcurve_point *gpc_next_pt = &gpc->curve_points[next_pt_idx];
+ BezTriple *prev_bezt = &gpc_prev_pt->bezt;
+ BezTriple *next_bezt = &gpc_next_pt->bezt;
+
+ madd_v3_v3fl(sco, prev_bezt->vec[prev_hd_idx], average_fac);
+ madd_v3_v3fl(sco, next_bezt->vec[next_hd_idx], average_fac);
+ }
+
+ interp_v3_v3v3(bezt->vec[hd_idx], bezt->vec[hd_idx], sco, weighted_factor);
+ }
+
+ if (do_pressure && hd_idx == 1) {
+ /* Include the current point. */
+ smoothed_pressure += gpc_pt->pressure * average_fac;
+
+ for (uint step = 1; step <= step_size; step++) {
+ int prev_idx = pt_idx - step;
+ int next_idx = pt_idx + step;
+
+ if (is_cyclic) {
+ prev_idx = mod_i(prev_idx, gpc->tot_curve_points);
+ next_idx = next_idx % gpc->tot_curve_points;
+ }
+ else {
+ CLAMP_MIN(prev_idx, 0);
+ CLAMP_MAX(next_idx, gpc->tot_curve_points - 1);
+ }
+
+ bGPDcurve_point *gpc_prev_pt = &gpc->curve_points[prev_idx];
+ bGPDcurve_point *gpc_next_pt = &gpc->curve_points[next_idx];
+ smoothed_pressure += gpc_prev_pt->pressure * average_fac;
+ smoothed_pressure += gpc_next_pt->pressure * average_fac;
+ }
+
+ gpc_pt->pressure = interpf(smoothed_pressure, gpc_pt->pressure, weighted_factor);
+ }
+
+ if (do_strength && hd_idx == 1) {
+ /* Include the current point. */
+ smoothed_strength += gpc_pt->strength * average_fac;
+
+ for (uint step = 1; step <= step_size; step++) {
+ int prev_idx = pt_idx - step;
+ int next_idx = pt_idx + step;
+
+ if (is_cyclic) {
+ prev_idx = mod_i(prev_idx, gpc->tot_curve_points);
+ next_idx = next_idx % gpc->tot_curve_points;
}
- else if (gps->editcurve->flag & GP_CURVE_NEEDS_STROKE_UPDATE) {
- BKE_gpencil_stroke_editcurve_update(gpd, gpl, gps);
+ else {
+ CLAMP_MIN(prev_idx, 0);
+ CLAMP_MAX(next_idx, gpc->tot_curve_points - 1);
}
- /* Update the selection from the stroke to the curve. */
- BKE_gpencil_editcurve_stroke_sync_selection(gpd, gps, gps->editcurve);
- gps->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
- BKE_gpencil_stroke_geometry_update(gpd, gps);
+ bGPDcurve_point *gpc_prev_pt = &gpc->curve_points[prev_idx];
+ bGPDcurve_point *gpc_next_pt = &gpc->curve_points[next_idx];
+ smoothed_strength += gpc_prev_pt->strength * average_fac;
+ smoothed_strength += gpc_next_pt->strength * average_fac;
}
+
+ gpc_pt->strength = interpf(smoothed_strength, gpc_pt->strength, weighted_factor);
}
}
}
}
-void BKE_gpencil_strokes_selected_sync_selection_editcurve(bGPdata *gpd)
+/**
+ * Smooth curve
+ * \param gps: The grease pencil stroke.
+ * \param factor: Smoothing factor.
+ * \param step_size: Number of adjacent points to consider when smoothing.
+ * \param repeat: Number of times to repeat the smoothing process.
+ * \param only_selected: Only smooth selected points.
+ * \param affect_endpoints: Include end-points when smoothing (only used when stroke is
+ * non-cyclic).
+ * \param do_positions: Smooth positions.
+ * \param do_pressure: Smooth pressure.
+ * \param do_strength: Smooth strength.
+ */
+void BKE_gpencil_editcurve_smooth(bGPDstroke *gps,
+ const float factor,
+ const uint step_size,
+ const uint repeat,
+ const bool only_selected,
+ const bool affect_endpoints,
+ const bool do_positions,
+ const bool do_pressure,
+ const bool do_strength)
+{
+ BKE_gpencil_editcurve_smooth_ex(gps,
+ factor,
+ step_size,
+ repeat,
+ only_selected,
+ affect_endpoints,
+ false,
+ false,
+ 0,
+ NULL,
+ do_positions,
+ do_pressure,
+ do_strength);
+}
+
+/**
+ * Curve Merge by Distance
+ * Merge the control points by distance. Merging will always occur on the first point.
+ * The first and last point are never merged. Note: The caller is resposible for the geometry
+ * update of the stroke.
+ * \param gps: Grease Pencil stroke.
+ * \param threshold: Distance between points.
+ * \param use_unselected: Set to true to analyze all stroke and not only selected points.
+ * \param refit_segments: Set to refit segments where points were dissolved.
+ * \param error_threshold: Error threshold for refitting (only used when refit_segments = true).
+ * \returns True if any point was merged and the geometry changed.
+ */
+bool BKE_gpencil_editcurve_merge_distance(bGPDstroke *gps,
+ const float threshold,
+ const bool use_unselected,
+ const bool refit_segments,
+ const float error_threshold)
{
- const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
- /* Sync selection for all strokes with editcurve. */
- LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
- if (!BKE_gpencil_layer_is_editable(gpl)) {
+ bGPDcurve *gpc = gps->editcurve;
+ if (gpc == NULL || gpc->tot_curve_points < 2 || threshold == 0.0f) {
+ return false;
+ }
+ const float th_square = threshold * threshold;
+
+ bool tagged = false;
+ int i = 0, step = 1;
+ while ((i < gpc->tot_curve_points - 1) && (i + step < gpc->tot_curve_points)) {
+ bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
+ bGPDcurve_point *gpc_pt_next = &gpc->curve_points[i + step];
+ if ((!use_unselected && ((gpc_pt->flag & GP_CURVE_POINT_SELECT) == 0 ||
+ (gpc_pt_next->flag & GP_CURVE_POINT_SELECT) == 0)) ||
+ (gpc_pt->flag & GP_CURVE_POINT_TAG)) {
+ i++;
+ step = 1;
continue;
}
- bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe;
- for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
- if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && is_multiedit)) {
- LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
- bGPDcurve *gpc = gps->editcurve;
- if (gpc != NULL) {
- /* Update the selection of every stroke that has an editcurve */
- BKE_gpencil_stroke_editcurve_sync_selection(gpd, gps, gpc);
- }
- }
- }
+ if (gpc_pt_next->flag & GP_CURVE_POINT_TAG) {
+ step++;
+ continue;
}
+ BezTriple *bezt = &gpc_pt->bezt;
+ BezTriple *bezt_next = &gpc_pt_next->bezt;
+
+ float len_square = len_squared_v3v3(bezt->vec[1], bezt_next->vec[1]);
+ if (len_square <= th_square) {
+ gpc_pt_next->flag |= GP_CURVE_POINT_TAG;
+ tagged = true;
+ step++;
+ }
+ else {
+ i++;
+ step = 1;
+ }
+ }
+
+ gpc->curve_points[0].flag &= ~GP_CURVE_POINT_TAG;
+ gpc->curve_points[gpc->tot_curve_points - 1].flag &= ~GP_CURVE_POINT_TAG;
+
+ int old_num_points = gpc->tot_curve_points;
+ if (tagged) {
+ return BKE_gpencil_editcurve_dissolve(
+ gps, GP_CURVE_POINT_TAG, refit_segments, error_threshold) != old_num_points;
}
+ return false;
}
/** \} */
diff --git a/source/blender/blenkernel/intern/gpencil_geom.c b/source/blender/blenkernel/intern/gpencil_geom.c
index 4dcd94fdeec..be5a148f6f9 100644
--- a/source/blender/blenkernel/intern/gpencil_geom.c
+++ b/source/blender/blenkernel/intern/gpencil_geom.c
@@ -523,7 +523,7 @@ bool BKE_gpencil_stroke_sample(bGPdata *gpd, bGPDstroke *gps, const float dist,
gps->totpoints = i;
/* Calc geometry data. */
- BKE_gpencil_stroke_geometry_update(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
return true;
}
@@ -708,7 +708,7 @@ bool BKE_gpencil_stroke_split(bGPdata *gpd,
* Keep the end point. */
BKE_gpencil_stroke_trim_points(gps, 0, old_count);
- BKE_gpencil_stroke_geometry_update(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
return true;
}
@@ -1376,29 +1376,25 @@ void BKE_gpencil_stroke_uv_update(bGPDstroke *gps)
* Recalc all internal geometry data for the stroke
* \param gpd: Grease pencil data-block
* \param gps: Grease pencil stroke
+ * \param flag: eGPStrokeGeoUpdateFlag flag (use GP_GEO_UPDATE_DEFAULT=0 for the default)
*/
-void BKE_gpencil_stroke_geometry_update(bGPdata *gpd, bGPDstroke *gps)
+void BKE_gpencil_stroke_geometry_update(bGPdata *gpd,
+ bGPDstroke *gps,
+ const eGPStrokeGeoUpdateFlag flag)
{
if (gps == NULL) {
return;
}
- if (gps->editcurve != NULL) {
- if (GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd)) {
- /* curve geometry was updated: stroke needs recalculation */
- if (gps->flag & GP_STROKE_NEEDS_CURVE_UPDATE) {
- bool is_adaptive = gpd->flag & GP_DATA_CURVE_ADAPTIVE_RESOLUTION;
- BKE_gpencil_stroke_update_geometry_from_editcurve(
- gps, gpd->curve_edit_resolution, is_adaptive);
- gps->flag &= ~GP_STROKE_NEEDS_CURVE_UPDATE;
- }
- }
- else {
- /* stroke geometry was updated: editcurve needs recalculation */
- gps->editcurve->flag |= GP_CURVE_NEEDS_STROKE_UPDATE;
- }
+ /* Update curve points first if it's a bezier stroke. */
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps) && GP_GEO_UPDATE_POLYLINE_REGENERATE_ANY(flag)) {
+ /* Regenerate the polyline points from the curve data. */
+ const uint resolution = gpd->curve_edit_resolution;
+ const bool is_adaptive = gpd->flag & GP_DATA_CURVE_ADAPTIVE_RESOLUTION;
+ BKE_gpencil_stroke_update_geometry_from_editcurve(gps, resolution, is_adaptive, flag);
}
+ /* Triangulate the stroke. */
if (gps->totpoints > 2) {
BKE_gpencil_stroke_fill_triangulate(gps);
}
@@ -1407,7 +1403,7 @@ void BKE_gpencil_stroke_geometry_update(bGPdata *gpd, bGPDstroke *gps)
MEM_SAFE_FREE(gps->triangles);
}
- /* calc uv data along the stroke */
+ /* Calc UV data along the stroke. */
BKE_gpencil_stroke_uv_update(gps);
/* Calc stroke bounding box. */
@@ -1559,7 +1555,7 @@ bool BKE_gpencil_stroke_trim(bGPdata *gpd, bGPDstroke *gps)
MEM_SAFE_FREE(old_dvert);
}
- BKE_gpencil_stroke_geometry_update(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
return intersect;
}
@@ -1736,7 +1732,7 @@ void BKE_gpencil_dissolve_points(bGPdata *gpd, bGPDframe *gpf, bGPDstroke *gps,
gps->totpoints = tot;
/* triangles cache needs to be recalculated */
- BKE_gpencil_stroke_geometry_update(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
}
}
@@ -1884,7 +1880,7 @@ void BKE_gpencil_stroke_simplify_adaptive(bGPdata *gpd, bGPDstroke *gps, float e
gps->totpoints = j;
/* Calc geometry data. */
- BKE_gpencil_stroke_geometry_update(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
MEM_SAFE_FREE(old_points);
MEM_SAFE_FREE(old_dvert);
@@ -1950,7 +1946,7 @@ void BKE_gpencil_stroke_simplify_fixed(bGPdata *gpd, bGPDstroke *gps)
gps->totpoints = j;
/* Calc geometry data. */
- BKE_gpencil_stroke_geometry_update(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
MEM_SAFE_FREE(old_points);
MEM_SAFE_FREE(old_dvert);
@@ -2072,7 +2068,7 @@ void BKE_gpencil_stroke_subdivide(bGPdata *gpd, bGPDstroke *gps, int level, int
}
/* Calc geometry data. */
- BKE_gpencil_stroke_geometry_update(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
}
/* Merge by distance ------------------------------------- */
@@ -2157,7 +2153,7 @@ void BKE_gpencil_stroke_merge_distance(bGPdata *gpd,
}
/* Calc geometry data. */
- BKE_gpencil_stroke_geometry_update(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
}
typedef struct GpEdge {
@@ -2372,7 +2368,7 @@ static void gpencil_generate_edgeloops(Object *ob,
pt->strength = 1.0f;
}
- BKE_gpencil_stroke_geometry_update(gpd, gps_stroke);
+ BKE_gpencil_stroke_geometry_update(gpd, gps_stroke, GP_GEO_UPDATE_DEFAULT);
}
/* Free memory. */
@@ -2557,7 +2553,7 @@ bool BKE_gpencil_convert_mesh(Main *bmain,
BKE_gpencil_stroke_subdivide(gpd, gps_fill, 1, GP_SUBDIV_SIMPLE);
}
- BKE_gpencil_stroke_geometry_update(gpd, gps_fill);
+ BKE_gpencil_stroke_geometry_update(gpd, gps_fill, GP_GEO_UPDATE_DEFAULT);
}
}
}
@@ -2614,7 +2610,7 @@ void BKE_gpencil_transform(bGPdata *gpd, const float mat[4][4])
}
/* Distortion may mean we need to re-triangulate. */
- BKE_gpencil_stroke_geometry_update(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
}
}
}
@@ -2706,7 +2702,7 @@ void BKE_gpencil_point_coords_apply(bGPdata *gpd, const GPencilPointCoordinates
}
/* Distortion may mean we need to re-triangulate. */
- BKE_gpencil_stroke_geometry_update(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
}
}
}
@@ -2743,7 +2739,7 @@ void BKE_gpencil_point_coords_apply_with_mat4(bGPdata *gpd,
}
/* Distortion may mean we need to re-triangulate. */
- BKE_gpencil_stroke_geometry_update(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
}
}
}
@@ -2768,49 +2764,108 @@ void BKE_gpencil_stroke_set_random_color(bGPDstroke *gps)
}
}
+static void gpencil_flip_beztriple(BezTriple *bezt)
+{
+ /* Flip handle position. */
+ float tmp[3];
+ copy_v3_v3(tmp, bezt->vec[0]);
+ copy_v3_v3(bezt->vec[0], bezt->vec[2]);
+ copy_v3_v3(bezt->vec[2], tmp);
+
+ /* Flip type and slection flag. */
+ SWAP(uint8_t, bezt->h1, bezt->h2);
+ SWAP(uint8_t, bezt->f1, bezt->f3);
+}
+
/* Flip stroke. */
void BKE_gpencil_stroke_flip(bGPDstroke *gps)
{
- int end = gps->totpoints - 1;
-
- for (int i = 0; i < gps->totpoints / 2; i++) {
- bGPDspoint *point, *point2;
- bGPDspoint pt;
-
- /* save first point */
- point = &gps->points[i];
- pt.x = point->x;
- pt.y = point->y;
- pt.z = point->z;
- pt.flag = point->flag;
- pt.pressure = point->pressure;
- pt.strength = point->strength;
- pt.time = point->time;
- copy_v4_v4(pt.vert_color, point->vert_color);
-
- /* replace first point with last point */
- point2 = &gps->points[end];
- point->x = point2->x;
- point->y = point2->y;
- point->z = point2->z;
- point->flag = point2->flag;
- point->pressure = point2->pressure;
- point->strength = point2->strength;
- point->time = point2->time;
- copy_v4_v4(point->vert_color, point2->vert_color);
-
- /* replace last point with first saved before */
- point = &gps->points[end];
- point->x = pt.x;
- point->y = pt.y;
- point->z = pt.z;
- point->flag = pt.flag;
- point->pressure = pt.pressure;
- point->strength = pt.strength;
- point->time = pt.time;
- copy_v4_v4(point->vert_color, pt.vert_color);
-
- end--;
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps)) {
+ bGPDcurve *gpc = gps->editcurve;
+
+ int end = gpc->tot_curve_points - 1;
+ for (int i = 0; i < gpc->tot_curve_points / 2; i++) {
+ bGPDcurve_point *cpt_first = &gpc->curve_points[i];
+ bGPDcurve_point *cpt_last = &gpc->curve_points[end];
+ bGPDcurve_point temp;
+
+ memcpy(&temp.bezt, &cpt_first->bezt, sizeof(BezTriple));
+ temp.pressure = cpt_first->pressure;
+ temp.strength = cpt_first->strength;
+ temp.point_index = cpt_first->point_index;
+ temp.flag = cpt_first->flag;
+ temp.uv_fac = cpt_first->uv_fac;
+ temp.uv_rot = cpt_first->uv_rot;
+ copy_v2_v2(temp.uv_fill, cpt_first->uv_fill);
+ copy_v4_v4(temp.vert_color, cpt_first->vert_color);
+
+ memcpy(&cpt_first->bezt, &cpt_last->bezt, sizeof(BezTriple));
+ gpencil_flip_beztriple(&cpt_first->bezt);
+ cpt_first->pressure = cpt_last->pressure;
+ cpt_first->strength = cpt_last->strength;
+ cpt_first->point_index = cpt_last->point_index;
+ cpt_first->flag = cpt_last->flag;
+ cpt_first->uv_fac = cpt_last->uv_fac;
+ cpt_first->uv_rot = cpt_last->uv_rot;
+ copy_v2_v2(cpt_first->uv_fill, cpt_last->uv_fill);
+ copy_v4_v4(cpt_first->vert_color, cpt_last->vert_color);
+
+ memcpy(&cpt_last->bezt, &temp.bezt, sizeof(BezTriple));
+ gpencil_flip_beztriple(&cpt_last->bezt);
+ cpt_last->pressure = temp.pressure;
+ cpt_last->strength = temp.strength;
+ cpt_last->point_index = temp.point_index;
+ cpt_last->flag = temp.flag;
+ cpt_last->uv_fac = temp.uv_fac;
+ cpt_last->uv_rot = temp.uv_rot;
+ copy_v2_v2(cpt_last->uv_fill, temp.uv_fill);
+ copy_v4_v4(cpt_last->vert_color, temp.vert_color);
+
+ end--;
+ }
+ }
+ else {
+ int end = gps->totpoints - 1;
+
+ for (int i = 0; i < gps->totpoints / 2; i++) {
+ bGPDspoint *point, *point2;
+ bGPDspoint pt;
+
+ /* save first point */
+ point = &gps->points[i];
+ pt.x = point->x;
+ pt.y = point->y;
+ pt.z = point->z;
+ pt.flag = point->flag;
+ pt.pressure = point->pressure;
+ pt.strength = point->strength;
+ pt.time = point->time;
+ copy_v4_v4(pt.vert_color, point->vert_color);
+
+ /* replace first point with last point */
+ point2 = &gps->points[end];
+ point->x = point2->x;
+ point->y = point2->y;
+ point->z = point2->z;
+ point->flag = point2->flag;
+ point->pressure = point2->pressure;
+ point->strength = point2->strength;
+ point->time = point2->time;
+ copy_v4_v4(point->vert_color, point2->vert_color);
+
+ /* replace last point with first saved before */
+ point = &gps->points[end];
+ point->x = pt.x;
+ point->y = pt.y;
+ point->z = pt.z;
+ point->flag = pt.flag;
+ point->pressure = pt.pressure;
+ point->strength = pt.strength;
+ point->time = pt.time;
+ copy_v4_v4(point->vert_color, pt.vert_color);
+
+ end--;
+ }
}
}
@@ -2901,7 +2956,7 @@ static void gpencil_stroke_join_islands(bGPdata *gpd,
/* add new stroke at head */
BLI_addhead(&gpf->strokes, join_stroke);
/* Calc geometry data. */
- BKE_gpencil_stroke_geometry_update(gpd, join_stroke);
+ BKE_gpencil_stroke_geometry_update(gpd, join_stroke, GP_GEO_UPDATE_DEFAULT);
/* remove first stroke */
BLI_remlink(&gpf->strokes, gps_first);
@@ -3053,7 +3108,7 @@ bGPDstroke *BKE_gpencil_stroke_delete_tagged_points(bGPdata *gpd,
}
else {
/* Calc geometry data. */
- BKE_gpencil_stroke_geometry_update(gpd, new_stroke);
+ BKE_gpencil_stroke_geometry_update(gpd, new_stroke, GP_GEO_UPDATE_DEFAULT);
if (next_stroke) {
BLI_insertlinkbefore(&gpf->strokes, next_stroke, new_stroke);
@@ -3137,10 +3192,10 @@ void BKE_gpencil_curve_delete_tagged_points(bGPdata *gpd,
sizeof(bGPDcurve_point) * island_length);
BKE_gpencil_editcurve_recalculate_handles(new_stroke);
- new_stroke->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
+ BKE_gpencil_curve_sync_selection(gpd, new_stroke);
/* Calc geometry data. */
- BKE_gpencil_stroke_geometry_update(gpd, new_stroke);
+ BKE_gpencil_stroke_geometry_update(gpd, new_stroke, GP_GEO_UPDATE_DEFAULT);
if (next_stroke) {
BLI_insertlinkbefore(&gpf->strokes, next_stroke, new_stroke);
@@ -3170,10 +3225,10 @@ void BKE_gpencil_curve_delete_tagged_points(bGPdata *gpd,
sizeof(bGPDcurve_point) * first_tot_points);
BKE_gpencil_editcurve_recalculate_handles(gps_last);
- gps_last->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
+ BKE_gpencil_curve_sync_selection(gpd, gps_last);
/* Calc geometry data. */
- BKE_gpencil_stroke_geometry_update(gpd, gps_last);
+ BKE_gpencil_stroke_geometry_update(gpd, gps_last, GP_GEO_UPDATE_DEFAULT);
/* remove first one */
BLI_remlink(&gpf->strokes, gps_first);
@@ -3241,7 +3296,6 @@ void BKE_gpencil_stroke_join(bGPDstroke *gps_a,
{
bGPDspoint point;
bGPDspoint *pt;
- int i;
const float delta[3] = {1.0f, 1.0f, 1.0f};
float deltatime = 0.0f;
@@ -3250,6 +3304,11 @@ void BKE_gpencil_stroke_join(bGPDstroke *gps_a,
return;
}
+ /* Cannot join strokes of different types for now... */
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps_a) != GPENCIL_STROKE_TYPE_BEZIER(gps_b)) {
+ return;
+ }
+
if ((gps_a->totpoints == 0) || (gps_b->totpoints == 0)) {
return;
}
@@ -3302,28 +3361,52 @@ void BKE_gpencil_stroke_join(bGPDstroke *gps_a,
BKE_gpencil_stroke_flip(gps_b);
}
- /* don't visibly link the first and last points? */
- if (leave_gaps) {
- /* 1st: add one tail point to start invisible area */
- point = gps_a->points[gps_a->totpoints - 1];
- deltatime = point.time;
+ const float thickness_ratio = (fit_thickness && gps_a->thickness > 0.0f) ?
+ (float)gps_b->thickness / (float)gps_a->thickness :
+ 1.0f;
+
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps_a)) {
+ /* TODO: Support leave gaps. */
+ bGPDcurve *gpc_a = gps_a->editcurve;
+ bGPDcurve *gpc_b = gps_b->editcurve;
- gpencil_stroke_copy_point(gps_a, NULL, &point, delta, 0.0f, 0.0f, 0.0f);
+ int old_num_points = gpc_a->tot_curve_points;
+ gpc_a->tot_curve_points += gpc_b->tot_curve_points;
+ gpc_a->curve_points = MEM_recallocN(gpc_a->curve_points,
+ sizeof(bGPDcurve_point) * gpc_a->tot_curve_points);
- /* 2nd: add one head point to finish invisible area */
- point = gps_b->points[0];
- gpencil_stroke_copy_point(gps_a, NULL, &point, delta, 0.0f, 0.0f, deltatime);
+ memcpy(gpc_a->curve_points + old_num_points,
+ gpc_b->curve_points,
+ sizeof(bGPDcurve_point) * gpc_b->tot_curve_points);
+ /* TODO: copy dvert curve data. */
+
+ /* Rescale other points. */
+ for (int i = old_num_points; i < gpc_a->tot_curve_points; i++) {
+ bGPDcurve_point *cpt = &gpc_a->curve_points[i];
+ cpt->pressure *= thickness_ratio;
+ }
}
+ else {
+ /* don't visibly link the first and last points? */
+ if (leave_gaps) {
+ /* 1st: add one tail point to start invisible area */
+ point = gps_a->points[gps_a->totpoints - 1];
+ deltatime = point.time;
- const float ratio = (fit_thickness && gps_a->thickness > 0.0f) ?
- (float)gps_b->thickness / (float)gps_a->thickness :
- 1.0f;
+ gpencil_stroke_copy_point(gps_a, NULL, &point, delta, 0.0f, 0.0f, 0.0f);
- /* 3rd: add all points */
- for (i = 0, pt = gps_b->points; i < gps_b->totpoints && pt; i++, pt++) {
- MDeformVert *dvert = (gps_b->dvert) ? &gps_b->dvert[i] : NULL;
- gpencil_stroke_copy_point(
- gps_a, dvert, pt, delta, pt->pressure * ratio, pt->strength, deltatime);
+ /* 2nd: add one head point to finish invisible area */
+ point = gps_b->points[0];
+ gpencil_stroke_copy_point(gps_a, NULL, &point, delta, 0.0f, 0.0f, deltatime);
+ }
+
+ /* 3rd: add all points */
+ for (int i = 0; i < gps_b->totpoints && pt; i++) {
+ pt = &gps_b->points[i];
+ MDeformVert *dvert = (gps_b->dvert) ? &gps_b->dvert[i] : NULL;
+ gpencil_stroke_copy_point(
+ gps_a, dvert, pt, delta, pt->pressure * thickness_ratio, pt->strength, deltatime);
+ }
}
}
@@ -3556,7 +3639,7 @@ void BKE_gpencil_stroke_uniform_subdivide(bGPdata *gpd,
}
/* Update the geometry of the stroke. */
- BKE_gpencil_stroke_geometry_update(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
}
/**
@@ -4067,7 +4150,7 @@ bGPDstroke *BKE_gpencil_stroke_perimeter_from_view(struct RegionView3D *rv3d,
MEM_freeN(perimeter_points);
/* Triangles cache needs to be recalculated. */
- BKE_gpencil_stroke_geometry_update(gpd, perimeter_stroke);
+ BKE_gpencil_stroke_geometry_update(gpd, perimeter_stroke, GP_GEO_UPDATE_DEFAULT);
perimeter_stroke->flag |= GP_STROKE_SELECT | GP_STROKE_CYCLIC;
diff --git a/source/blender/blenkernel/intern/gpencil_modifier.c b/source/blender/blenkernel/intern/gpencil_modifier.c
index 4d9c9878a67..d24152eae21 100644
--- a/source/blender/blenkernel/intern/gpencil_modifier.c
+++ b/source/blender/blenkernel/intern/gpencil_modifier.c
@@ -774,9 +774,7 @@ void BKE_gpencil_prepare_eval_data(Depsgraph *depsgraph, Scene *scene, Object *o
}
const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd_eval);
- const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd_eval);
- const bool do_modifiers = (bool)((!is_multiedit) && (!is_curve_edit) &&
- (ob->greasepencil_modifiers.first != NULL) &&
+ const bool do_modifiers = (bool)((!is_multiedit) && (ob->greasepencil_modifiers.first != NULL) &&
(!GPENCIL_SIMPLIFY_MODIF(scene)));
if ((!do_modifiers) && (!do_parent) && (!do_transform)) {
return;
@@ -812,11 +810,9 @@ void BKE_gpencil_modifiers_calc(Depsgraph *depsgraph, Scene *scene, Object *ob)
{
bGPdata *gpd = (bGPdata *)ob->data;
const bool is_edit = GPENCIL_ANY_EDIT_MODE(gpd);
+ const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
const bool is_render = (bool)(DEG_get_mode(depsgraph) == DAG_EVAL_RENDER);
- const bool is_curve_edit = (bool)(GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd) && !is_render);
- const bool is_multiedit = (bool)(GPENCIL_MULTIEDIT_SESSIONS_ON(gpd) && !is_render);
- const bool do_modifiers = (bool)((!is_multiedit) && (!is_curve_edit) &&
- (ob->greasepencil_modifiers.first != NULL) &&
+ const bool do_modifiers = (bool)((!is_multiedit) && (ob->greasepencil_modifiers.first != NULL) &&
(!GPENCIL_SIMPLIFY_MODIF(scene)));
if (!do_modifiers) {
return;
@@ -849,16 +845,20 @@ void BKE_gpencil_modifiers_calc(Depsgraph *depsgraph, Scene *scene, Object *ob)
}
/* Apply deform modifiers and Time remap (only change geometry). */
- if ((time_remap) || (mti && mti->deformStroke)) {
+ if ((time_remap) || (mti && mti->deformPolyline) || (mti && mti->deformBezier)) {
LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
bGPDframe *gpf = BKE_gpencil_frame_retime_get(depsgraph, scene, ob, gpl);
if (gpf == NULL) {
continue;
}
-
- if (mti->deformStroke) {
- LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
- mti->deformStroke(md, depsgraph, ob, gpl, gpf, gps);
+ LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
+ if (!GPENCIL_STROKE_TYPE_BEZIER(gps)) {
+ if (mti->deformPolyline) {
+ mti->deformPolyline(md, depsgraph, ob, gpl, gpf, gps);
+ }
+ }
+ else if (mti->deformBezier) {
+ mti->deformBezier(md, depsgraph, ob, gpl, gpf, gps);
}
}
}
diff --git a/source/blender/blenloader/intern/versioning_280.c b/source/blender/blenloader/intern/versioning_280.c
index 82c4709f187..7b8854d4203 100644
--- a/source/blender/blenloader/intern/versioning_280.c
+++ b/source/blender/blenloader/intern/versioning_280.c
@@ -4878,7 +4878,7 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain)
gps->fill_opacity_fac = 1.0f;
/* Calc geometry data because in old versions this data was not saved. */
- BKE_gpencil_stroke_geometry_update(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
srgb_to_linearrgb_v4(gps->vert_color_fill, gps->vert_color_fill);
int i;
diff --git a/source/blender/blenloader/intern/versioning_290.c b/source/blender/blenloader/intern/versioning_290.c
index 09d43676b8f..947a46d4b37 100644
--- a/source/blender/blenloader/intern/versioning_290.c
+++ b/source/blender/blenloader/intern/versioning_290.c
@@ -1693,13 +1693,6 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain)
gpd->flag |= GP_DATA_CURVE_ADAPTIVE_RESOLUTION;
}
}
- /* Init grease pencil curve editing error threshold. */
- if (!DNA_struct_elem_find(fd->filesdna, "bGPdata", "float", "curve_edit_threshold")) {
- LISTBASE_FOREACH (bGPdata *, gpd, &bmain->gpencils) {
- gpd->curve_edit_threshold = GP_DEFAULT_CURVE_ERROR;
- gpd->curve_edit_corner_angle = GP_DEFAULT_CURVE_EDIT_CORNER_ANGLE;
- }
- }
}
if ((!MAIN_VERSION_ATLEAST(bmain, 292, 14)) ||
diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c
index 5d3bde428c0..dd5577283c5 100644
--- a/source/blender/blenloader/intern/versioning_300.c
+++ b/source/blender/blenloader/intern/versioning_300.c
@@ -21,6 +21,7 @@
#define DNA_DEPRECATED_ALLOW
#include "BLI_listbase.h"
+#include "BLI_math.h"
#include "BLI_math_vector.h"
#include "BLI_string.h"
#include "BLI_utildefines.h"
@@ -30,6 +31,7 @@
#include "DNA_brush_types.h"
#include "DNA_collection_types.h"
#include "DNA_genfile.h"
+#include "DNA_gpencil_types.h"
#include "DNA_listBase.h"
#include "DNA_modifier_types.h"
#include "DNA_text_types.h"
@@ -303,6 +305,17 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain)
}
}
}
+
+ /* Init grease pencil curve editing error threshold. */
+ if (!DNA_struct_elem_find(
+ fd->filesdna, "ToolSettings", "float", "gpencil_curve_fit_threshold")) {
+ LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
+ if (scene->toolsettings->sequencer_tool_settings == NULL) {
+ scene->toolsettings->gpencil_curve_fit_threshold = GP_DEFAULT_CURVE_ERROR;
+ scene->toolsettings->gpencil_curve_fit_corner_angle = GP_DEFAULT_CURVE_EDIT_CORNER_ANGLE;
+ }
+ }
+ }
}
if (!MAIN_VERSION_ATLEAST(bmain, 300, 2)) {
diff --git a/source/blender/draw/engines/overlay/overlay_gpencil.c b/source/blender/draw/engines/overlay/overlay_gpencil.c
index 5c03d70d7be..7e97e6babd6 100644
--- a/source/blender/draw/engines/overlay/overlay_gpencil.c
+++ b/source/blender/draw/engines/overlay/overlay_gpencil.c
@@ -111,8 +111,7 @@ void OVERLAY_edit_gpencil_cache_init(OVERLAY_Data *vedata)
(GPENCIL_EDIT_MODE(gpd) &&
(ts->gpencil_selectmode_edit != GP_SELECTMODE_STROKE));
- if ((!GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd)) &&
- ((!GPENCIL_VERTEX_MODE(gpd) && !GPENCIL_PAINT_MODE(gpd)) || use_vertex_mask)) {
+ if (((!GPENCIL_VERTEX_MODE(gpd) && !GPENCIL_PAINT_MODE(gpd)) || use_vertex_mask)) {
DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS_EQUAL |
DRW_STATE_BLEND_ALPHA;
DRW_PASS_CREATE(psl->edit_gpencil_ps, state | pd->clipping_state);
@@ -139,35 +138,25 @@ void OVERLAY_edit_gpencil_cache_init(OVERLAY_Data *vedata)
}
}
- /* Handles and curve point for Curve Edit submode. */
- if (GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd)) {
+ /* Handles and curve point for Edit mode. */
+ if (GPENCIL_EDIT_MODE(gpd)) {
DRWState state = DRW_STATE_WRITE_COLOR;
DRW_PASS_CREATE(psl->edit_gpencil_curve_ps, state | pd->clipping_state);
- /* Edit lines. */
- if (show_lines) {
- sh = OVERLAY_shader_edit_gpencil_wire();
- pd->edit_gpencil_wires_grp = grp = DRW_shgroup_create(sh, psl->edit_gpencil_curve_ps);
+ if (show_points && !hide_select) {
+ sh = OVERLAY_shader_edit_curve_handle();
+ pd->edit_gpencil_curve_handle_grp = grp = DRW_shgroup_create(sh, psl->edit_gpencil_curve_ps);
DRW_shgroup_uniform_block(grp, "globalsBlock", G_draw.block_ubo);
- DRW_shgroup_uniform_bool_copy(grp, "doMultiframe", show_multi_edit_lines);
- DRW_shgroup_uniform_bool_copy(grp, "doWeightColor", is_weight_paint);
- DRW_shgroup_uniform_bool_copy(grp, "hideSelect", hide_select);
- DRW_shgroup_uniform_float_copy(grp, "gpEditOpacity", v3d->vertex_opacity);
- DRW_shgroup_uniform_texture(grp, "weightTex", G_draw.weight_ramp);
- }
-
- sh = OVERLAY_shader_edit_curve_handle();
- pd->edit_gpencil_curve_handle_grp = grp = DRW_shgroup_create(sh, psl->edit_gpencil_curve_ps);
- DRW_shgroup_uniform_block(grp, "globalsBlock", G_draw.block_ubo);
- DRW_shgroup_uniform_bool_copy(grp, "showCurveHandles", pd->edit_curve.show_handles);
- DRW_shgroup_uniform_int_copy(grp, "curveHandleDisplay", pd->edit_curve.handle_display);
- DRW_shgroup_state_enable(grp, DRW_STATE_BLEND_ALPHA);
+ DRW_shgroup_uniform_bool_copy(grp, "showCurveHandles", pd->edit_curve.show_handles);
+ DRW_shgroup_uniform_int_copy(grp, "curveHandleDisplay", pd->edit_curve.handle_display);
+ DRW_shgroup_state_enable(grp, DRW_STATE_BLEND_ALPHA);
- sh = OVERLAY_shader_edit_curve_point();
- pd->edit_gpencil_curve_points_grp = grp = DRW_shgroup_create(sh, psl->edit_gpencil_curve_ps);
- DRW_shgroup_uniform_block(grp, "globalsBlock", G_draw.block_ubo);
- DRW_shgroup_uniform_bool_copy(grp, "showCurveHandles", pd->edit_curve.show_handles);
- DRW_shgroup_uniform_int_copy(grp, "curveHandleDisplay", pd->edit_curve.handle_display);
+ sh = OVERLAY_shader_edit_curve_point();
+ pd->edit_gpencil_curve_points_grp = grp = DRW_shgroup_create(sh, psl->edit_gpencil_curve_ps);
+ DRW_shgroup_uniform_block(grp, "globalsBlock", G_draw.block_ubo);
+ DRW_shgroup_uniform_bool_copy(grp, "showCurveHandles", pd->edit_curve.show_handles);
+ DRW_shgroup_uniform_int_copy(grp, "curveHandleDisplay", pd->edit_curve.handle_display);
+ }
}
/* control points for primitives and speed guide */
diff --git a/source/blender/draw/engines/overlay/shaders/edit_gpencil_vert.glsl b/source/blender/draw/engines/overlay/shaders/edit_gpencil_vert.glsl
index a6161d36a07..ee7375714aa 100644
--- a/source/blender/draw/engines/overlay/shaders/edit_gpencil_vert.glsl
+++ b/source/blender/draw/engines/overlay/shaders/edit_gpencil_vert.glsl
@@ -21,12 +21,13 @@ void discard_vert()
gl_Position = vec4(0.0, 0.0, -3e36, 0.0);
}
-#define GP_EDIT_POINT_SELECTED 1u /* 1 << 0 */
+#define GP_EDIT_POINT_SELECTED 1u /* 1 << 0 */
#define GP_EDIT_STROKE_SELECTED 2u /* 1 << 1 */
-#define GP_EDIT_MULTIFRAME 4u /* 1 << 2 */
-#define GP_EDIT_STROKE_START 8u /* 1 << 3 */
-#define GP_EDIT_STROKE_END 16u /* 1 << 4 */
-#define GP_EDIT_POINT_DIMMED 32u /* 1 << 5 */
+#define GP_EDIT_MULTIFRAME 4u /* 1 << 2 */
+#define GP_EDIT_STROKE_START 8u /* 1 << 3 */
+#define GP_EDIT_STROKE_END 16u /* 1 << 4 */
+#define GP_EDIT_POINT_DIMMED 32u /* 1 << 5 */
+#define GP_EDIT_POINT_HIDDEN 64u /* 1 << 6 */
#ifdef USE_POINTS
# define colorUnselect colorGpencilVertex
@@ -62,6 +63,7 @@ void main()
bool is_stroke_sel = (vflag & GP_EDIT_STROKE_SELECTED) != 0u;
bool is_point_sel = (vflag & GP_EDIT_POINT_SELECTED) != 0u;
bool is_point_dimmed = (vflag & GP_EDIT_POINT_DIMMED) != 0u;
+ bool is_point_hidden = (vflag & GP_EDIT_POINT_HIDDEN) != 0u;
if (doWeightColor) {
finalColor.rgb = weight_to_rgb(weight);
@@ -78,6 +80,9 @@ void main()
if (is_point_dimmed) {
finalColor.rgb = clamp(colorUnselect.rgb + vec3(0.3), 0.0, 1.0);
}
+ if (is_point_hidden) {
+ gl_PointSize = sizeVertexGpencil * 0.8;
+ }
if (doStrokeEndpoints && !doWeightColor) {
bool is_stroke_start = (vflag & GP_EDIT_STROKE_START) != 0u;
diff --git a/source/blender/draw/intern/draw_cache_impl_gpencil.c b/source/blender/draw/intern/draw_cache_impl_gpencil.c
index 7fe543db01f..5dab0070aff 100644
--- a/source/blender/draw/intern/draw_cache_impl_gpencil.c
+++ b/source/blender/draw/intern/draw_cache_impl_gpencil.c
@@ -686,6 +686,7 @@ void DRW_cache_gpencil_sbuffer_clear(Object *ob)
#define GP_EDIT_STROKE_START (1 << 3)
#define GP_EDIT_STROKE_END (1 << 4)
#define GP_EDIT_POINT_DIMMED (1 << 5)
+#define GP_EDIT_POINT_HIDDEN (1 << 6)
typedef struct gpEditIterData {
gpEditVert *verts;
@@ -697,16 +698,19 @@ typedef struct gpEditCurveIterData {
int vgindex;
} gpEditCurveIterData;
-static uint32_t gpencil_point_edit_flag(const bool layer_lock,
- const bGPDspoint *pt,
- int v,
- int v_len)
+static uint32_t gpencil_point_edit_flag(
+ const bool layer_lock, const bool is_bezier, const bGPDspoint *pt, int v, int v_len)
{
uint32_t sflag = 0;
SET_FLAG_FROM_TEST(sflag, (!layer_lock) && pt->flag & GP_SPOINT_SELECT, GP_EDIT_POINT_SELECTED);
SET_FLAG_FROM_TEST(sflag, v == 0, GP_EDIT_STROKE_START);
SET_FLAG_FROM_TEST(sflag, v == (v_len - 1), GP_EDIT_STROKE_END);
- SET_FLAG_FROM_TEST(sflag, pt->runtime.pt_orig == NULL, GP_EDIT_POINT_DIMMED);
+ if (!is_bezier) {
+ SET_FLAG_FROM_TEST(sflag, pt->runtime.pt_orig == NULL, GP_EDIT_POINT_DIMMED);
+ }
+ else {
+ SET_FLAG_FROM_TEST(sflag, ((pt->flag & GP_SPOINT_IS_BEZT_CONTROL) == 0), GP_EDIT_POINT_HIDDEN);
+ }
return sflag;
}
@@ -725,6 +729,7 @@ static void gpencil_edit_stroke_iter_cb(bGPDlayer *gpl,
const int v = gps->runtime.stroke_start + 1;
MDeformVert *dvert = ((iter->vgindex > -1) && gps->dvert) ? gps->dvert : NULL;
gpEditVert *vert_ptr = iter->verts + v;
+ const bool is_bezier = GPENCIL_STROKE_TYPE_BEZIER(gps);
const bool layer_lock = (gpl->flag & GP_LAYER_LOCKED);
uint32_t sflag = 0;
@@ -733,12 +738,14 @@ static void gpencil_edit_stroke_iter_cb(bGPDlayer *gpl,
SET_FLAG_FROM_TEST(sflag, gpf->runtime.onion_id != 0.0f, GP_EDIT_MULTIFRAME);
for (int i = 0; i < v_len; i++) {
- vert_ptr->vflag = sflag | gpencil_point_edit_flag(layer_lock, &gps->points[i], i, v_len);
+ vert_ptr->vflag = sflag |
+ gpencil_point_edit_flag(layer_lock, is_bezier, &gps->points[i], i, v_len);
vert_ptr->weight = gpencil_point_edit_weight(dvert, i, iter->vgindex);
vert_ptr++;
}
/* Draw line to first point to complete the loop for cyclic strokes. */
- vert_ptr->vflag = sflag | gpencil_point_edit_flag(layer_lock, &gps->points[0], 0, v_len);
+ vert_ptr->vflag = sflag |
+ gpencil_point_edit_flag(layer_lock, is_bezier, &gps->points[0], 0, v_len);
vert_ptr->weight = gpencil_point_edit_weight(dvert, 0, iter->vgindex);
}
diff --git a/source/blender/editors/gpencil/CMakeLists.txt b/source/blender/editors/gpencil/CMakeLists.txt
index bff7310e9f7..83ba519c4c7 100644
--- a/source/blender/editors/gpencil/CMakeLists.txt
+++ b/source/blender/editors/gpencil/CMakeLists.txt
@@ -43,6 +43,7 @@ set(SRC
gpencil_armature.c
gpencil_bake_animation.c
gpencil_convert.c
+ gpencil_curve_draw.c
gpencil_data.c
gpencil_edit.c
gpencil_edit_curve.c
diff --git a/source/blender/editors/gpencil/gpencil_add_monkey.c b/source/blender/editors/gpencil/gpencil_add_monkey.c
index d8734c4ae6b..95d7648ace2 100644
--- a/source/blender/editors/gpencil/gpencil_add_monkey.c
+++ b/source/blender/editors/gpencil/gpencil_add_monkey.c
@@ -848,115 +848,115 @@ void ED_gpencil_create_monkey(bContext *C, Object *ob, float mat[4][4])
/* generate strokes */
gps = BKE_gpencil_stroke_add(frameFills, color_Skin, 270, 75, false);
BKE_gpencil_stroke_add_points(gps, data0, 270, mat);
- BKE_gpencil_stroke_geometry_update(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
gps = BKE_gpencil_stroke_add(frameFills, color_Skin_Shadow, 33, 60, false);
BKE_gpencil_stroke_add_points(gps, data1, 33, mat);
- BKE_gpencil_stroke_geometry_update(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
gps = BKE_gpencil_stroke_add(frameFills, color_Skin_Shadow, 18, 60, false);
BKE_gpencil_stroke_add_points(gps, data2, 18, mat);
- BKE_gpencil_stroke_geometry_update(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
gps = BKE_gpencil_stroke_add(frameFills, color_Skin_Light, 64, 60, false);
BKE_gpencil_stroke_add_points(gps, data3, 64, mat);
- BKE_gpencil_stroke_geometry_update(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
gps = BKE_gpencil_stroke_add(frameFills, color_Skin_Light, 33, 60, false);
BKE_gpencil_stroke_add_points(gps, data4, 33, mat);
- BKE_gpencil_stroke_geometry_update(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
gps = BKE_gpencil_stroke_add(frameFills, color_Skin_Light, 64, 60, false);
BKE_gpencil_stroke_add_points(gps, data5, 64, mat);
- BKE_gpencil_stroke_geometry_update(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
gps = BKE_gpencil_stroke_add(frameFills, color_Skin_Light, 33, 60, false);
BKE_gpencil_stroke_add_points(gps, data6, 33, mat);
- BKE_gpencil_stroke_geometry_update(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
gps = BKE_gpencil_stroke_add(frameFills, color_Skin_Light, 18, 40, false);
BKE_gpencil_stroke_add_points(gps, data7, 18, mat);
- BKE_gpencil_stroke_geometry_update(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
gps = BKE_gpencil_stroke_add(frameFills, color_Eyes, 49, 60, false);
BKE_gpencil_stroke_add_points(gps, data8, 49, mat);
- BKE_gpencil_stroke_geometry_update(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
gps = BKE_gpencil_stroke_add(frameFills, color_Skin_Shadow, 33, 60, false);
BKE_gpencil_stroke_add_points(gps, data9, 33, mat);
- BKE_gpencil_stroke_geometry_update(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
gps = BKE_gpencil_stroke_add(frameFills, color_Eyes, 49, 60, false);
BKE_gpencil_stroke_add_points(gps, data10, 49, mat);
- BKE_gpencil_stroke_geometry_update(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
gps = BKE_gpencil_stroke_add(frameFills, color_Skin_Shadow, 18, 40, false);
BKE_gpencil_stroke_add_points(gps, data11, 18, mat);
- BKE_gpencil_stroke_geometry_update(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
gps = BKE_gpencil_stroke_add(frameFills, color_Skin_Shadow, 18, 40, false);
BKE_gpencil_stroke_add_points(gps, data12, 18, mat);
- BKE_gpencil_stroke_geometry_update(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
gps = BKE_gpencil_stroke_add(frameLines, color_Black, 33, 60, false);
BKE_gpencil_stroke_add_points(gps, data13, 33, mat);
- BKE_gpencil_stroke_geometry_update(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
gps = BKE_gpencil_stroke_add(frameLines, color_Black, 33, 60, false);
BKE_gpencil_stroke_add_points(gps, data14, 33, mat);
- BKE_gpencil_stroke_geometry_update(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
gps = BKE_gpencil_stroke_add(frameLines, color_Black, 65, 60, false);
BKE_gpencil_stroke_add_points(gps, data15, 65, mat);
- BKE_gpencil_stroke_geometry_update(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
gps = BKE_gpencil_stroke_add(frameLines, color_Black, 34, 60, false);
BKE_gpencil_stroke_add_points(gps, data16, 34, mat);
- BKE_gpencil_stroke_geometry_update(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
gps = BKE_gpencil_stroke_add(frameLines, color_Black, 33, 60, false);
BKE_gpencil_stroke_add_points(gps, data17, 33, mat);
- BKE_gpencil_stroke_geometry_update(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
gps = BKE_gpencil_stroke_add(frameLines, color_Black, 33, 40, false);
BKE_gpencil_stroke_add_points(gps, data18, 33, mat);
- BKE_gpencil_stroke_geometry_update(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
gps = BKE_gpencil_stroke_add(frameLines, color_Black, 34, 40, false);
BKE_gpencil_stroke_add_points(gps, data19, 34, mat);
- BKE_gpencil_stroke_geometry_update(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
gps = BKE_gpencil_stroke_add(frameLines, color_Black, 33, 60, false);
BKE_gpencil_stroke_add_points(gps, data20, 33, mat);
- BKE_gpencil_stroke_geometry_update(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
gps = BKE_gpencil_stroke_add(frameLines, color_Black, 64, 60, false);
BKE_gpencil_stroke_add_points(gps, data21, 64, mat);
- BKE_gpencil_stroke_geometry_update(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
gps = BKE_gpencil_stroke_add(frameLines, color_Pupils, 26, 60, false);
BKE_gpencil_stroke_add_points(gps, data22, 26, mat);
- BKE_gpencil_stroke_geometry_update(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
gps = BKE_gpencil_stroke_add(frameLines, color_Pupils, 26, 60, false);
BKE_gpencil_stroke_add_points(gps, data23, 26, mat);
- BKE_gpencil_stroke_geometry_update(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
gps = BKE_gpencil_stroke_add(frameLines, color_Black, 33, 60, false);
BKE_gpencil_stroke_add_points(gps, data24, 33, mat);
- BKE_gpencil_stroke_geometry_update(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
gps = BKE_gpencil_stroke_add(frameLines, color_Black, 18, 40, false);
BKE_gpencil_stroke_add_points(gps, data25, 18, mat);
- BKE_gpencil_stroke_geometry_update(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
gps = BKE_gpencil_stroke_add(frameLines, color_Black, 18, 40, false);
BKE_gpencil_stroke_add_points(gps, data26, 18, mat);
- BKE_gpencil_stroke_geometry_update(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
gps = BKE_gpencil_stroke_add(frameLines, color_Black, 33, 60, false);
BKE_gpencil_stroke_add_points(gps, data27, 33, mat);
- BKE_gpencil_stroke_geometry_update(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
/* update depsgraph */
DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
diff --git a/source/blender/editors/gpencil/gpencil_add_stroke.c b/source/blender/editors/gpencil/gpencil_add_stroke.c
index e95496b51ee..291df3b678e 100644
--- a/source/blender/editors/gpencil/gpencil_add_stroke.c
+++ b/source/blender/editors/gpencil/gpencil_add_stroke.c
@@ -236,7 +236,7 @@ void ED_gpencil_create_stroke(bContext *C, Object *ob, float mat[4][4])
/* generate stroke */
gps = BKE_gpencil_stroke_add(frame_lines, color_black, 175, 75, false);
BKE_gpencil_stroke_add_points(gps, data0, 175, mat);
- BKE_gpencil_stroke_geometry_update(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
/* update depsgraph */
DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
diff --git a/source/blender/editors/gpencil/gpencil_bake_animation.c b/source/blender/editors/gpencil/gpencil_bake_animation.c
index 1a5e2950e09..af9bd55fb44 100644
--- a/source/blender/editors/gpencil/gpencil_bake_animation.c
+++ b/source/blender/editors/gpencil/gpencil_bake_animation.c
@@ -346,7 +346,7 @@ static int gpencil_bake_grease_pencil_animation_exec(bContext *C, wmOperator *op
depsgraph, &gsc, sctx, gpl_dst, gpf_dst, gps, project_type, false);
}
else {
- BKE_gpencil_stroke_geometry_update(gpd_dst, gps);
+ BKE_gpencil_stroke_geometry_update(gpd_dst, gps, GP_GEO_UPDATE_DEFAULT);
}
}
}
diff --git a/source/blender/editors/gpencil/gpencil_convert.c b/source/blender/editors/gpencil/gpencil_convert.c
index 2cb1e09d9a6..2d2d3cf95ab 100644
--- a/source/blender/editors/gpencil/gpencil_convert.c
+++ b/source/blender/editors/gpencil/gpencil_convert.c
@@ -1378,6 +1378,7 @@ static void gpencil_layer_to_curve(bContext *C,
nu = NULL;
}
+ /* TODO: check if the strok is of type BEZIER. In that case the conversion can be less expensive. */
switch (mode) {
case GP_STROKECONVERT_PATH:
gpencil_stroke_to_path(C,
diff --git a/source/blender/editors/gpencil/gpencil_curve_draw.c b/source/blender/editors/gpencil/gpencil_curve_draw.c
new file mode 100644
index 00000000000..83df6bb63ad
--- /dev/null
+++ b/source/blender/editors/gpencil/gpencil_curve_draw.c
@@ -0,0 +1,845 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2017, Blender Foundation
+ * This is a new part of Blender
+ * Operators for creating new Grease Pencil primitives (boxes, circles, ...)
+ */
+
+/** \file
+ * \ingroup edgpencil
+ */
+#include <stdio.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_listbase.h"
+#include "BLI_math.h"
+#include "BLI_string.h"
+
+#include "BLT_translation.h"
+
+#include "DNA_brush_types.h"
+#include "DNA_gpencil_types.h"
+#include "DNA_scene_types.h"
+#include "DNA_space_types.h"
+#include "DNA_userdef_types.h"
+#include "DNA_windowmanager_types.h"
+
+#include "BKE_brush.h"
+#include "BKE_context.h"
+#include "BKE_global.h"
+#include "BKE_gpencil.h"
+#include "BKE_gpencil_curve.h"
+#include "BKE_gpencil_geom.h"
+#include "BKE_main.h"
+#include "BKE_paint.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+#include "RNA_types.h"
+
+#include "ED_gpencil.h"
+#include "ED_screen.h"
+#include "ED_space_api.h"
+#include "ED_view3d.h"
+
+#include "GPU_immediate.h"
+#include "GPU_matrix.h"
+#include "GPU_shader.h"
+#include "GPU_state.h"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "DEG_depsgraph.h"
+#include "DEG_depsgraph_query.h"
+
+#include "gpencil_intern.h"
+
+/* ------------------------------------------------------------------------- */
+/* Structs & enums */
+
+typedef enum eGPDcurve_draw_state {
+ IN_MOVE = 0,
+ IN_SET_VECTOR = 1,
+ IN_DRAG_ALIGNED_HANDLE = 2,
+ IN_DRAG_FREE_HANDLE = 3,
+ IN_SET_THICKNESS = 4,
+} eGPDcurve_draw_state;
+
+typedef struct tGPDcurve_draw {
+ Scene *scene;
+ ARegion *region;
+ Object *ob;
+ bGPdata *gpd;
+ bGPDlayer *gpl;
+ bGPDframe *gpf;
+ bGPDstroke *gps;
+ bGPDcurve *gpc;
+ int cframe;
+
+ Brush *brush;
+
+ GP_SpaceConversion gsc;
+
+ /* imval of current event */
+ int imval[2];
+ /* imval of previous event */
+ int imval_prev[2];
+ /* imval when mouse was last pressed */
+ int imval_start[2];
+ /* imval when mouse was last released */
+ int imval_end[2];
+ bool is_mouse_down;
+
+ bool is_cyclic;
+ float prev_pressure;
+
+ /* Curve resolution */
+ int resolution;
+
+ /* Callback for viewport drawing. */
+ void *draw_handle;
+
+ eGPDcurve_draw_state state;
+} tGPDcurve_draw;
+
+enum {
+ CD_MODAL_CANCEL = 1,
+ CD_MODAL_CONFIRM,
+ CD_MODAL_FREE_HANDLE_ON,
+ CD_MODAL_FREE_HANDLE_OFF,
+ CD_MODAL_CYCLIC_TOGGLE,
+ CD_MODAL_DELETE_LAST,
+ CD_MODAL_SET_THICKNESS,
+};
+
+/* Forward declaration */
+static void gpencil_curve_draw_init(bContext *C, wmOperator *op, const wmEvent *event);
+static void gpencil_curve_draw_update(bContext *C, tGPDcurve_draw *tcd);
+static void gpencil_curve_draw_confirm(bContext *C, wmOperator *op, tGPDcurve_draw *tcd);
+static void gpencil_curve_draw_exit(bContext *C, wmOperator *op);
+
+/* ------------------------------------------------------------------------- */
+/* Helper functions */
+
+static void debug_print_state(tGPDcurve_draw *tcd)
+{
+ const char *state_str[] = {"MOVE", "VECTOR", "ALIGN", "FREE", "THICK", "ALPHA"};
+ printf("State: %s\tMouse x=%d\ty=%d\tpressed:%s\n",
+ state_str[tcd->state],
+ tcd->imval[0],
+ tcd->imval[1],
+ (tcd->is_mouse_down) ? "TRUE" : "FALSE");
+}
+
+static void gpencil_project_mval_to_v3(
+ Scene *scene, ARegion *region, Object *ob, const int mval_i[2], float r_out[3])
+{
+ ToolSettings *ts = scene->toolsettings;
+ float mval_f[2], mval_prj[2], rvec[3], dvec[3], zfac;
+ copy_v2fl_v2i(mval_f, mval_i);
+
+ ED_gpencil_drawing_reference_get(scene, ob, ts->gpencil_v3d_align, rvec);
+ zfac = ED_view3d_calc_zfac(region->regiondata, rvec, NULL);
+
+ if (ED_view3d_project_float_global(region, rvec, mval_prj, V3D_PROJ_TEST_NOP) ==
+ V3D_PROJ_RET_OK) {
+ sub_v2_v2v2(mval_f, mval_prj, mval_f);
+ ED_view3d_win_to_delta(region, mval_f, dvec, zfac);
+ sub_v3_v3v3(r_out, rvec, dvec);
+ }
+ else {
+ zero_v3(r_out);
+ }
+}
+
+/* Helper: Add a new curve point at the end (duplicating the previous last) */
+static void gpencil_push_curve_point(bContext *C, tGPDcurve_draw *tcd)
+{
+ bGPDcurve *gpc = tcd->gpc;
+ int old_num_points = gpc->tot_curve_points;
+ int new_num_points = old_num_points + 1;
+ gpc->tot_curve_points = new_num_points;
+
+ gpc->curve_points = MEM_recallocN(gpc->curve_points, sizeof(bGPDcurve_point) * new_num_points);
+
+ bGPDcurve_point *old_last = &gpc->curve_points[gpc->tot_curve_points - 2];
+ bGPDcurve_point *new_last = &gpc->curve_points[gpc->tot_curve_points - 1];
+ memcpy(new_last, old_last, sizeof(bGPDcurve_point));
+
+ new_last->bezt.h1 = new_last->bezt.h2 = HD_VECT;
+
+ BKE_gpencil_stroke_update_geometry_from_editcurve(
+ tcd->gps, tcd->gpd->curve_edit_resolution, false, GP_GEO_UPDATE_DEFAULT);
+}
+
+/* Helper: Remove the last curve point */
+static void gpencil_pop_curve_point(bContext *C, tGPDcurve_draw *tcd)
+{
+ bGPdata *gpd = tcd->gpd;
+ bGPDstroke *gps = tcd->gps;
+ bGPDcurve *gpc = tcd->gpc;
+ const int old_num_points = gpc->tot_curve_points;
+ const int new_num_points = old_num_points - 1;
+ if (G.debug & G_DEBUG) {
+ printf("old: %d, new: %d\n", old_num_points, new_num_points);
+ }
+
+ /* Create new stroke and curve */
+ bGPDstroke *new_stroke = BKE_gpencil_stroke_duplicate(tcd->gps, false, false);
+ new_stroke->points = NULL;
+
+ bGPDcurve *new_curve = BKE_gpencil_stroke_editcurve_new(new_num_points);
+ new_curve->flag = gpc->flag;
+ memcpy(new_curve->curve_points, gpc->curve_points, sizeof(bGPDcurve_point) * new_num_points);
+ new_stroke->editcurve = new_curve;
+
+ BKE_gpencil_stroke_update_geometry_from_editcurve(
+ new_stroke, gpd->curve_edit_resolution, false, GP_GEO_UPDATE_DEFAULT);
+
+ /* Remove and free old stroke and curve */
+ BLI_remlink(&tcd->gpf->strokes, gps);
+ BKE_gpencil_free_stroke(gps);
+
+ tcd->gps = new_stroke;
+ tcd->gpc = new_curve;
+
+ BLI_addtail(&tcd->gpf->strokes, new_stroke);
+ BKE_gpencil_stroke_geometry_update(gpd, new_stroke, GP_GEO_UPDATE_DEFAULT);
+
+ DEG_id_tag_update(&gpd->id, ID_RECALC_COPY_ON_WRITE);
+ DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+}
+
+static void gpencil_set_handle_type_last_point(tGPDcurve_draw *tcd, eBezTriple_Handle type)
+{
+ bGPDcurve *gpc = tcd->gpc;
+ bGPDcurve_point *cpt = &gpc->curve_points[gpc->tot_curve_points - 1];
+ cpt->bezt.h1 = cpt->bezt.h2 = type;
+}
+
+static void gpencil_set_alpha_last_segment(tGPDcurve_draw *tcd, float alpha)
+{
+ bGPDstroke *gps = tcd->gps;
+ bGPDcurve *gpc = tcd->gpc;
+
+ if (gpc->tot_curve_points < 2) {
+ return;
+ }
+
+ bGPDcurve_point *old_last = &gpc->curve_points[gpc->tot_curve_points - 2];
+ for (uint32_t i = old_last->point_index; i < gps->totpoints; i++) {
+ bGPDspoint *pt = &gps->points[i];
+ pt->strength = alpha;
+ }
+}
+
+static void gpencil_curve_draw_ui_callback(const struct bContext *UNUSED(C),
+ struct ARegion *UNUSED(region),
+ void *customdata)
+{
+ const tGPDcurve_draw *tcd = customdata;
+ GPU_depth_test(GPU_DEPTH_NONE);
+
+ GPU_matrix_push_projection();
+ GPU_polygon_offset(1.0f, 1.0f);
+
+ GPU_matrix_push();
+ GPU_matrix_mul(tcd->ob->obmat);
+
+ GPUVertFormat *format = immVertexFormat();
+ uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
+
+ immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
+
+ /* Draw overlays. */
+ if (ELEM(tcd->state, IN_DRAG_ALIGNED_HANDLE, IN_DRAG_FREE_HANDLE)) {
+ bGPDcurve *gpc = tcd->gpc;
+ bGPDcurve_point *cpt_last = &gpc->curve_points[gpc->tot_curve_points - 1];
+ BezTriple *bezt = &cpt_last->bezt;
+
+ float viewport[4];
+ GPU_viewport_size_get_f(viewport);
+ immUniform2fv("viewportSize", &viewport[2]);
+
+ float color[4] = {0, 0, 0, 1.0f};
+ UI_GetThemeColorType3fv(TH_GP_VERTEX_SELECT, SPACE_VIEW3D, color);
+
+ /* TODO: Use the GPU_SHADER_3D_POLYLINE_* shader instead. GPU_line_smooth will be deprecated.
+ */
+ GPU_line_smooth(true);
+ GPU_blend(GPU_BLEND_ALPHA);
+
+ // immUniform1f("lineWidth", U.pixelsize * 2.0f);
+ // immUniform1i("lineSmooth", 1);
+
+ immUniformColor4fv(color);
+
+ /* Handle lines. */
+ immBegin(GPU_PRIM_LINES, 4);
+ immVertex3fv(pos, bezt->vec[0]);
+ immVertex3fv(pos, bezt->vec[1]);
+ immVertex3fv(pos, bezt->vec[1]);
+ immVertex3fv(pos, bezt->vec[2]);
+ immEnd();
+
+ // immUniform1f("size", U.pixelsize * UI_GetThemeValuef(TH_GP_VERTEX_SIZE) * 2.0f);
+
+ // immUniformColor4fv(color);
+
+ /* Handle points. */
+ immBegin(GPU_PRIM_POINTS, 3);
+ immVertex3fv(pos, bezt->vec[0]);
+ immVertex3fv(pos, bezt->vec[1]);
+ immVertex3fv(pos, bezt->vec[2]);
+ immEnd();
+
+ GPU_line_smooth(false);
+ GPU_blend(GPU_BLEND_NONE);
+ }
+
+ immUnbindProgram();
+
+ GPU_matrix_pop();
+ GPU_matrix_pop_projection();
+
+ /* Reset default */
+ GPU_depth_test(GPU_DEPTH_LESS_EQUAL);
+}
+
+/* ------------------------------------------------------------------------- */
+/* Main drawing functions */
+
+static void gpencil_curve_draw_update_header(bContext *C,
+ wmOperator *op,
+ const tGPDcurve_draw *tcd)
+{
+ char header[UI_MAX_DRAW_STR];
+ char buf[UI_MAX_DRAW_STR];
+
+ char *p = buf;
+ int available_len = sizeof(buf);
+
+#define WM_MODALKEY(_id) \
+ WM_modalkeymap_operator_items_to_string_buf( \
+ op->type, (_id), true, UI_MAX_SHORTCUT_STR, &available_len, &p)
+
+ switch (tcd->state) {
+ case IN_MOVE:
+ case IN_SET_VECTOR:
+ BLI_snprintf(header,
+ sizeof(header),
+ TIP_("%s: confirm, %s: cancel, "
+ "%s: toggle cyclic (%s), "
+ "%s: delete last, %s: set thickness"),
+ WM_MODALKEY(CD_MODAL_CONFIRM),
+ WM_MODALKEY(CD_MODAL_CANCEL),
+ WM_MODALKEY(CD_MODAL_CYCLIC_TOGGLE),
+ WM_bool_as_string(tcd->is_cyclic),
+ WM_MODALKEY(CD_MODAL_DELETE_LAST),
+ WM_MODALKEY(CD_MODAL_SET_THICKNESS));
+ break;
+ case IN_DRAG_FREE_HANDLE:
+ case IN_DRAG_ALIGNED_HANDLE:
+ BLI_snprintf(header,
+ sizeof(header),
+ TIP_("%s: confirm, %s: cancel, "
+ "%s: toggle cyclic (%s), "
+ "%s: free handle (%s), "
+ "%s: delete last, %s: set thickness"),
+ WM_MODALKEY(CD_MODAL_CONFIRM),
+ WM_MODALKEY(CD_MODAL_CANCEL),
+ WM_MODALKEY(CD_MODAL_CYCLIC_TOGGLE),
+ WM_bool_as_string(tcd->is_cyclic),
+ WM_MODALKEY(CD_MODAL_FREE_HANDLE_ON),
+ WM_bool_as_string(tcd->state == IN_DRAG_FREE_HANDLE),
+ WM_MODALKEY(CD_MODAL_DELETE_LAST),
+ WM_MODALKEY(CD_MODAL_SET_THICKNESS));
+ break;
+ case IN_SET_THICKNESS:
+ BLI_snprintf(header,
+ sizeof(header),
+ TIP_("%s: confirm, %s: cancel, "
+ "%s: toggle cyclic (%s), "
+ "%s: delete last"),
+ WM_MODALKEY(CD_MODAL_CONFIRM),
+ WM_MODALKEY(CD_MODAL_CANCEL),
+ WM_MODALKEY(CD_MODAL_CYCLIC_TOGGLE),
+ WM_bool_as_string(tcd->is_cyclic),
+ WM_MODALKEY(CD_MODAL_DELETE_LAST));
+ break;
+ }
+
+ ED_workspace_status_text(C, header);
+
+#undef WM_MODALKEY
+}
+
+/* ------------------------------------------------------------------------- */
+/* Main drawing functions */
+
+static void gpencil_curve_draw_init(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ Main *bmain = CTX_data_main(C);
+ Scene *scene = CTX_data_scene(C);
+ ARegion *region = CTX_wm_region(C);
+ Object *ob = CTX_data_active_object(C);
+ bGPdata *gpd = CTX_data_gpencil_data(C);
+
+ ToolSettings *ts = scene->toolsettings;
+ Paint *paint = &ts->gp_paint->paint;
+ int cfra = CFRA;
+
+ /* Allocate temp curve draw data. */
+ tGPDcurve_draw *tcd = MEM_callocN(sizeof(tGPDcurve_draw), __func__);
+ tcd->scene = scene;
+ tcd->region = region;
+ tcd->gpd = gpd;
+ tcd->ob = ob;
+ /* Fixed resolution. */
+ tcd->resolution = 32;
+
+ /* Initialize mouse state */
+ copy_v2_v2_int(tcd->imval, event->mval);
+ copy_v2_v2_int(tcd->imval_prev, event->mval);
+ tcd->is_mouse_down = (event->val == KM_PRESS);
+ tcd->state = IN_SET_VECTOR;
+
+ if ((paint->brush == NULL) || (paint->brush->gpencil_settings == NULL)) {
+ BKE_brush_gpencil_paint_presets(bmain, ts, true);
+ }
+
+ Brush *brush = BKE_paint_toolslots_brush_get(paint, 0);
+ BKE_brush_tool_set(brush, paint, 0);
+ BKE_paint_brush_set(paint, brush);
+ BrushGpencilSettings *brush_settings = brush->gpencil_settings;
+ tcd->brush = brush;
+
+ /* Get active layer or create a new one. */
+ bGPDlayer *gpl = CTX_data_active_gpencil_layer(C);
+ if (gpl == NULL) {
+ gpl = BKE_gpencil_layer_addnew(tcd->gpd, DATA_("Curve"), true, false);
+ }
+ tcd->gpl = gpl;
+
+ /* Recalculate layer transform matrix to avoid problems if props are animated. */
+ loc_eul_size_to_mat4(
+ tcd->gpl->layer_mat, tcd->gpl->location, tcd->gpl->rotation, tcd->gpl->scale);
+ invert_m4_m4(tcd->gpl->layer_invmat, tcd->gpl->layer_mat);
+
+ /* Get current frame or create new one. */
+ short add_frame_mode;
+ if (ts->gpencil_flags & GP_TOOL_FLAG_RETAIN_LAST) {
+ add_frame_mode = GP_GETFRAME_ADD_COPY;
+ }
+ else {
+ add_frame_mode = GP_GETFRAME_ADD_NEW;
+ }
+
+ tcd->cframe = cfra;
+ bool need_tag = tcd->gpl->actframe == NULL;
+ bGPDframe *gpf = BKE_gpencil_layer_frame_get(tcd->gpl, tcd->cframe, add_frame_mode);
+ if (need_tag) {
+ DEG_id_tag_update(&tcd->gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
+ }
+ tcd->gpf = gpf;
+
+ /* Create stroke. */
+ int mat_idx = BKE_gpencil_object_material_get_index_from_brush(ob, brush);
+ bGPDstroke *gps = BKE_gpencil_stroke_new(mat_idx, 1, brush->size);
+ gps->thickness = brush->size;
+ gps->hardeness = brush_settings->hardeness;
+ copy_v2_v2(gps->aspect_ratio, brush_settings->aspect_ratio);
+
+ float first_pt[3];
+ gpencil_project_mval_to_v3(scene, region, ob, tcd->imval, first_pt);
+ gps->points[0].pressure = 1.0f;
+ gps->points[0].strength = 1.0f;
+ copy_v3_v3(&gps->points[0].x, first_pt);
+
+ BLI_addtail(&gpf->strokes, gps);
+ tcd->gps = gps;
+
+ /* Create editcurve. */
+ bGPDcurve *gpc = BKE_gpencil_stroke_editcurve_new(1);
+ bGPDcurve_point *cpt = &gpc->curve_points[0];
+ copy_v3_v3(cpt->bezt.vec[0], first_pt);
+ copy_v3_v3(cpt->bezt.vec[1], first_pt);
+ copy_v3_v3(cpt->bezt.vec[2], first_pt);
+ cpt->pressure = 1.0f;
+ cpt->strength = 1.0f;
+
+ gps->editcurve = gpc;
+ tcd->gpc = gpc;
+
+ /* Calc geometry data. */
+ BKE_gpencil_stroke_geometry_update(tcd->gpd, gps, GP_GEO_UPDATE_DEFAULT);
+
+ /* Initialize space conversion. */
+ gpencil_point_conversion_init(C, &tcd->gsc);
+
+ tcd->draw_handle = ED_region_draw_cb_activate(
+ tcd->region->type, gpencil_curve_draw_ui_callback, tcd, REGION_DRAW_POST_VIEW);
+
+ gpencil_curve_draw_update(C, tcd);
+ op->customdata = tcd;
+}
+
+static void gpencil_curve_draw_update(bContext *C, tGPDcurve_draw *tcd)
+{
+ bGPdata *gpd = tcd->gpd;
+ bGPDstroke *gps = tcd->gps;
+ bGPDcurve *gpc = tcd->gpc;
+ int tot_points = gpc->tot_curve_points;
+ bGPDcurve_point *cpt = &gpc->curve_points[tot_points - 1];
+ BezTriple *bezt = &cpt->bezt;
+
+ float co[3];
+ switch (tcd->state) {
+ case IN_MOVE: {
+ gpencil_project_mval_to_v3(tcd->scene, tcd->region, tcd->ob, tcd->imval, co);
+ copy_v3_v3(bezt->vec[0], co);
+ copy_v3_v3(bezt->vec[1], co);
+ copy_v3_v3(bezt->vec[2], co);
+
+ BKE_gpencil_editcurve_recalculate_handles(gps);
+ BKE_gpencil_stroke_update_geometry_from_editcurve(
+ gps, tcd->resolution, false, GP_GEO_UPDATE_DEFAULT);
+ gpencil_set_alpha_last_segment(tcd, 0.1f);
+ break;
+ }
+ case IN_DRAG_ALIGNED_HANDLE: {
+ float vec[3];
+ gpencil_project_mval_to_v3(tcd->scene, tcd->region, tcd->ob, tcd->imval, co);
+ sub_v3_v3v3(vec, bezt->vec[1], co);
+ add_v3_v3(vec, bezt->vec[1]);
+ copy_v3_v3(bezt->vec[0], vec);
+ copy_v3_v3(bezt->vec[2], co);
+
+ BKE_gpencil_stroke_update_geometry_from_editcurve(
+ gps, tcd->resolution, false, GP_GEO_UPDATE_DEFAULT);
+ break;
+ }
+ case IN_DRAG_FREE_HANDLE: {
+ gpencil_project_mval_to_v3(tcd->scene, tcd->region, tcd->ob, tcd->imval, co);
+ copy_v3_v3(bezt->vec[2], co);
+
+ BKE_gpencil_stroke_update_geometry_from_editcurve(
+ gps, tcd->resolution, false, GP_GEO_UPDATE_DEFAULT);
+ break;
+ }
+ case IN_SET_THICKNESS: {
+ int move[2];
+ sub_v2_v2v2_int(move, tcd->imval, tcd->imval_start);
+ int dir = move[0] > 0.0f ? 1 : -1;
+ int dist = len_manhattan_v2_int(move);
+ /* TODO: calculate correct radius. */
+ float dr = dir * ((float)dist / 10.0f);
+ cpt->pressure = tcd->prev_pressure + dr;
+ CLAMP_MIN(cpt->pressure, 0.0f);
+
+ BKE_gpencil_stroke_update_geometry_from_editcurve(
+ gps, tcd->resolution, false, GP_GEO_UPDATE_DEFAULT);
+ break;
+ }
+ default:
+ break;
+ }
+
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
+
+ DEG_id_tag_update(&gpd->id, ID_RECALC_COPY_ON_WRITE);
+ DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+}
+
+static void gpencil_curve_draw_confirm(bContext *C, wmOperator *op, tGPDcurve_draw *tcd)
+{
+ if (G.debug & G_DEBUG) {
+ printf("Confirm curve draw\n");
+ }
+ bGPDcurve *gpc = tcd->gpc;
+ int tot_points = gpc->tot_curve_points;
+ bGPDcurve_point *cpt = &gpc->curve_points[tot_points - 1];
+ cpt->flag &= ~GP_CURVE_POINT_SELECT;
+ BEZT_DESEL_ALL(&cpt->bezt);
+
+ BKE_gpencil_editcurve_recalculate_handles(tcd->gps);
+}
+
+static void gpencil_curve_draw_exit(bContext *C, wmOperator *op)
+{
+ if (G.debug & G_DEBUG) {
+ printf("Exit curve draw\n");
+ }
+
+ wmWindow *win = CTX_wm_window(C);
+ tGPDcurve_draw *tcd = op->customdata;
+
+ ED_workspace_status_text(C, NULL);
+ WM_cursor_modal_restore(win);
+
+ ED_region_draw_cb_exit(tcd->region->type, tcd->draw_handle);
+
+ bGPdata *gpd = tcd->gpd;
+
+ MEM_SAFE_FREE(tcd);
+
+ DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE);
+ WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL);
+
+ op->customdata = NULL;
+}
+
+/* ------------------------------------------------------------------------- */
+/* Operator callbacks */
+
+static int gpencil_curve_draw_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ if (G.debug & G_DEBUG) {
+ printf("Invoke curve draw\n");
+ }
+ wmWindow *win = CTX_wm_window(C);
+
+ /* Set cursor to dot. */
+ WM_cursor_modal_set(win, WM_CURSOR_DOT);
+
+ gpencil_curve_draw_init(C, op, event);
+ // tGPDcurve_draw *tcd = op->customdata;
+
+ /* Add modal handler. */
+ WM_event_add_modal_handler(C, op);
+ return OPERATOR_RUNNING_MODAL;
+}
+
+static int gpencil_curve_draw_modal(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ tGPDcurve_draw *tcd = op->customdata;
+ wmWindow *win = CTX_wm_window(C);
+ float drag_threshold = (float)WM_event_drag_threshold(event);
+
+ copy_v2_v2_int(tcd->imval, event->mval);
+
+ /* Modal keymap event. */
+ if (event->type == EVT_MODAL_MAP) {
+ switch (event->val) {
+ case CD_MODAL_CONFIRM: {
+ /* Delete the 'preview' point. */
+ if (tcd->state == IN_MOVE) {
+ gpencil_pop_curve_point(C, tcd);
+ }
+ /* Create curve */
+ gpencil_curve_draw_confirm(C, op, tcd);
+ gpencil_curve_draw_exit(C, op);
+ return OPERATOR_FINISHED;
+ }
+ case CD_MODAL_CANCEL: {
+ /* Delete the stroke. */
+ BLI_remlink(&tcd->gpf->strokes, tcd->gps);
+ BKE_gpencil_free_stroke(tcd->gps);
+ gpencil_curve_draw_exit(C, op);
+ return OPERATOR_CANCELLED;
+ }
+ case CD_MODAL_FREE_HANDLE_ON: {
+ if (tcd->state == IN_DRAG_ALIGNED_HANDLE) {
+ tcd->state = IN_DRAG_FREE_HANDLE;
+ gpencil_set_handle_type_last_point(tcd, HD_FREE);
+ gpencil_curve_draw_update(C, tcd);
+ }
+ break;
+ }
+ case CD_MODAL_FREE_HANDLE_OFF: {
+ if (tcd->state == IN_DRAG_FREE_HANDLE) {
+ tcd->state = IN_DRAG_ALIGNED_HANDLE;
+ gpencil_set_handle_type_last_point(tcd, HD_ALIGN);
+ gpencil_curve_draw_update(C, tcd);
+ }
+ break;
+ }
+ case CD_MODAL_CYCLIC_TOGGLE: {
+ if (tcd->is_cyclic) {
+ tcd->gps->flag &= ~GP_STROKE_CYCLIC;
+ }
+ else {
+ tcd->gps->flag |= GP_STROKE_CYCLIC;
+ }
+ tcd->is_cyclic = !tcd->is_cyclic;
+ gpencil_curve_draw_update(C, tcd);
+ break;
+ }
+ case CD_MODAL_DELETE_LAST: {
+ if (tcd->state == IN_MOVE) {
+ gpencil_pop_curve_point(C, tcd);
+ }
+ else if (ELEM(tcd->state, IN_DRAG_ALIGNED_HANDLE, IN_DRAG_FREE_HANDLE)) {
+ tcd->state = IN_MOVE;
+ }
+ gpencil_curve_draw_update(C, tcd);
+ break;
+ }
+ case CD_MODAL_SET_THICKNESS: {
+ if (tcd->state != IN_SET_THICKNESS) {
+ tcd->state = IN_SET_THICKNESS;
+ WM_cursor_modal_set(win, WM_CURSOR_EW_SCROLL);
+
+ bGPDcurve_point *cpt_last = &tcd->gpc->curve_points[tcd->gpc->tot_curve_points - 1];
+ tcd->prev_pressure = cpt_last->pressure;
+ copy_v2_v2_int(tcd->imval_start, tcd->imval);
+
+ gpencil_curve_draw_update(C, tcd);
+ }
+ break;
+ }
+ }
+ }
+ /* Event not in keymap. */
+ else {
+ switch (event->type) {
+ case LEFTMOUSE: {
+ if (event->val == KM_PRESS) {
+ copy_v2_v2_int(tcd->imval_start, tcd->imval);
+ tcd->is_mouse_down = true;
+ /* Set state to vector. */
+ if (tcd->state == IN_MOVE) {
+ tcd->state = IN_SET_VECTOR;
+ }
+ /* Reset state to move. */
+ else if (tcd->state == IN_SET_THICKNESS) {
+ tcd->state = IN_MOVE;
+ WM_cursor_modal_set(win, WM_CURSOR_DOT);
+ }
+ }
+ else if (event->val == KM_RELEASE) {
+ copy_v2_v2_int(tcd->imval_end, tcd->imval);
+ tcd->is_mouse_down = false;
+ /* Reset state to move. */
+ if (ELEM(tcd->state, IN_SET_VECTOR, IN_DRAG_ALIGNED_HANDLE, IN_DRAG_FREE_HANDLE)) {
+ tcd->state = IN_MOVE;
+ gpencil_push_curve_point(C, tcd);
+ }
+
+ gpencil_curve_draw_update(C, tcd);
+ }
+ break;
+ }
+ case MOUSEMOVE: {
+ if (tcd->state == IN_SET_VECTOR &&
+ len_v2v2_int(tcd->imval, tcd->imval_start) > drag_threshold) {
+ tcd->state = IN_DRAG_ALIGNED_HANDLE;
+ gpencil_set_handle_type_last_point(tcd, HD_ALIGN);
+ }
+ gpencil_curve_draw_update(C, tcd);
+ break;
+ }
+ default: {
+ copy_v2_v2_int(tcd->imval_prev, tcd->imval);
+ return OPERATOR_RUNNING_MODAL | OPERATOR_PASS_THROUGH;
+ }
+ }
+ }
+
+ gpencil_curve_draw_update_header(C, op, tcd);
+
+ if (G.debug & G_DEBUG) {
+ debug_print_state(tcd);
+ }
+ copy_v2_v2_int(tcd->imval_prev, tcd->imval);
+ return OPERATOR_RUNNING_MODAL;
+}
+
+static void gpencil_curve_draw_cancel(bContext *C, wmOperator *op)
+{
+ if (G.debug & G_DEBUG) {
+ printf("Cancel curve draw\n");
+ }
+ gpencil_curve_draw_exit(C, op);
+}
+
+static bool gpencil_curve_draw_poll(bContext *C)
+{
+ if (G.debug & G_DEBUG) {
+ printf("Poll curve draw\n");
+ }
+ ScrArea *area = CTX_wm_area(C);
+ if (area && area->spacetype != SPACE_VIEW3D) {
+ return false;
+ }
+
+ bGPdata *gpd = CTX_data_gpencil_data(C);
+ if (gpd == NULL) {
+ return false;
+ }
+
+ if ((gpd->flag & GP_DATA_STROKE_PAINTMODE) == 0) {
+ return false;
+ }
+
+ bGPDlayer *gpl = BKE_gpencil_layer_active_get(gpd);
+ if ((gpl) && (gpl->flag & (GP_LAYER_LOCKED | GP_LAYER_HIDE))) {
+ return false;
+ }
+
+ return true;
+}
+
+wmKeyMap *gpencil_curve_draw_modal_keymap(wmKeyConfig *keyconf)
+{
+ static const EnumPropertyItem modal_items[] = {
+ {CD_MODAL_CANCEL, "CANCEL", 0, "Cancel", ""},
+ {CD_MODAL_CONFIRM, "CONFIRM", 0, "Confirm", ""},
+ {CD_MODAL_FREE_HANDLE_ON, "FREE_HANDLE_ON", 0, "Free Handle On", ""},
+ {CD_MODAL_FREE_HANDLE_OFF, "FREE_HANDLE_OFF", 0, "Free Handle Off", ""},
+ {CD_MODAL_CYCLIC_TOGGLE, "CYCLIC_TOGGLE", 0, "Toggle Stroke Cyclic", ""},
+ {CD_MODAL_DELETE_LAST, "DELETE_LAST", 0, "Delete the Last Confirmed Point", ""},
+ {CD_MODAL_SET_THICKNESS, "SET_THICKNESS", 0, "Set the Thickness", ""},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ wmKeyMap *keymap = WM_modalkeymap_find(keyconf, "Curve Draw Tool Modal Map");
+
+ /* this function is called for each spacetype, only needs to add map once */
+ if (keymap && keymap->modal_items) {
+ return NULL;
+ }
+
+ keymap = WM_modalkeymap_ensure(keyconf, "Curve Draw Tool Modal Map", modal_items);
+
+ WM_modalkeymap_assign(keymap, "GPENCIL_OT_draw_curve");
+
+ return keymap;
+}
+
+void GPENCIL_OT_draw_curve(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Grease Pencil Draw Curve";
+ ot->idname = "GPENCIL_OT_draw_curve";
+ ot->description = "Draw a bézier stroke in the active grease pencil object";
+
+ /* api callbacks */
+ ot->invoke = gpencil_curve_draw_invoke;
+ ot->modal = gpencil_curve_draw_modal;
+ ot->cancel = gpencil_curve_draw_cancel;
+ ot->poll = gpencil_curve_draw_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING;
+}
diff --git a/source/blender/editors/gpencil/gpencil_data.c b/source/blender/editors/gpencil/gpencil_data.c
index e272f46d13d..685cd54fc1b 100644
--- a/source/blender/editors/gpencil/gpencil_data.c
+++ b/source/blender/editors/gpencil/gpencil_data.c
@@ -1552,7 +1552,6 @@ static int gpencil_stroke_arrange_exec(bContext *C, wmOperator *op)
ListBase selected = {NULL};
bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe;
- bGPDstroke *gps = NULL;
for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) {
@@ -1560,13 +1559,12 @@ static int gpencil_stroke_arrange_exec(bContext *C, wmOperator *op)
continue;
}
/* verify if any selected stroke is in the extreme of the stack and select to move */
- for (gps = gpf->strokes.first; gps; gps = gps->next) {
+ LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
+ bool is_stroke_selected = GPENCIL_STROKE_TYPE_BEZIER(gps) ?
+ (bool)(gps->editcurve->flag & GP_CURVE_SELECT) :
+ (bool)(gps->flag & GP_STROKE_SELECT);
/* only if selected */
- if (gps->flag & GP_STROKE_SELECT) {
- /* skip strokes that are invalid for current view */
- if (ED_gpencil_stroke_can_use(C, gps) == false) {
- continue;
- }
+ if (is_stroke_selected) {
/* check if the color is editable */
if (ED_gpencil_stroke_material_editable(ob, gpl, gps) == false) {
continue;
@@ -1597,6 +1595,7 @@ static int gpencil_stroke_arrange_exec(bContext *C, wmOperator *op)
int prev_index = target_index;
/* Now do the movement of the stroke */
switch (direction) {
+ bGPDstroke *gps = NULL;
/* Bring to Front */
case GP_STROKE_MOVE_TOP:
LISTBASE_FOREACH (LinkData *, link, &selected) {
@@ -1755,21 +1754,26 @@ static int gpencil_stroke_change_color_exec(bContext *C, wmOperator *op)
LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
/* only if selected */
- if (gps->flag & GP_STROKE_SELECT) {
- /* skip strokes that are invalid for current view */
- if (ED_gpencil_stroke_can_use(C, gps) == false) {
- continue;
- }
- /* check if the color is editable */
- if (ED_gpencil_stroke_material_editable(ob, gpl, gps) == false) {
- continue;
- }
-
- /* assign new color */
- gps->mat_nr = idx;
+ bool is_stroke_selected = GPENCIL_STROKE_TYPE_BEZIER(gps) ?
+ (bool)(gps->editcurve->flag & GP_CURVE_SELECT) :
+ (bool)(gps->flag & GP_STROKE_SELECT);
+ if (!is_stroke_selected) {
+ continue;
+ }
- changed = true;
+ /* skip strokes that are invalid for current view */
+ if (ED_gpencil_stroke_can_use(C, gps) == false) {
+ continue;
}
+ /* check if the color is editable */
+ if (ED_gpencil_stroke_material_editable(ob, gpl, gps) == false) {
+ continue;
+ }
+
+ /* assign new color */
+ gps->mat_nr = idx;
+
+ changed = true;
}
}
/* If not multi-edit, exit loop. */
diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c
index 8d1f841da6c..b57b2fb3ace 100644
--- a/source/blender/editors/gpencil/gpencil_edit.c
+++ b/source/blender/editors/gpencil/gpencil_edit.c
@@ -207,22 +207,6 @@ static int gpencil_editmode_toggle_exec(bContext *C, wmOperator *op)
ob->mode = mode;
}
- /* Recalculate editcurves for strokes where the geometry/vertex colors have changed */
- if (GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd)) {
- GP_EDITABLE_CURVES_BEGIN(gps_iter, C, gpl, gps, gpc)
- {
- 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(gpd, gps, gps->editcurve);
-
- gps->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
- BKE_gpencil_stroke_geometry_update(gpd, gps);
- }
- }
- GP_EDITABLE_CURVES_END(gps_iter);
- }
-
/* setup other modes */
ED_gpencil_setup_modes(C, gpd, mode);
/* set cache as dirty */
@@ -846,24 +830,99 @@ static void gpencil_duplicate_points(bGPdata *gpd,
ListBase *new_strokes,
const char *layername)
{
- bGPDspoint *pt;
- int i;
-
int start_idx = -1;
- /* Step through the original stroke's points:
- * - We accumulate selected points (from start_idx to current index)
- * and then convert that to a new stroke
- */
- for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
- /* searching for start, are waiting for end? */
- if (start_idx == -1) {
- /* is this the first selected point for a new island? */
- if (pt->flag & GP_SPOINT_SELECT) {
- start_idx = i;
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps)) {
+ bGPDcurve *gpc = gps->editcurve;
+ for (int i = 0; i < gpc->tot_curve_points; i++) {
+ bGPDcurve_point *cpt = &gpc->curve_points[i];
+ if (start_idx == -1) {
+ if (cpt->flag & GP_CURVE_POINT_SELECT) {
+ start_idx = i;
+ }
+ continue;
+ }
+
+ size_t len = 0;
+
+ if ((cpt->flag & GP_CURVE_POINT_SELECT) == 0) {
+ len = i - start_idx;
}
+ else if (i == gpc->tot_curve_points - 1) {
+ len = i - start_idx + 1;
+ }
+
+ if (len < 1) {
+ continue;
+ }
+
+ /* make a stupid copy first of the entire stroke (to get the flags too) */
+ bGPDstroke *gpsd = BKE_gpencil_stroke_duplicate((bGPDstroke *)gps, false, false);
+
+ /* saves original layer name */
+ BLI_strncpy(gpsd->runtime.tmp_layerinfo, layername, sizeof(gpsd->runtime.tmp_layerinfo));
+
+ /* To avoid a curve update, we just copy the points. */
+ int start_idx_stroke = gpc->curve_points[start_idx].point_index;
+ int len_stroke = (gpc->curve_points[start_idx + len - 1].point_index - start_idx_stroke) + 1;
+
+ gpsd->points = MEM_mallocN(sizeof(bGPDspoint) * len_stroke, "gps stroke points copy");
+ memcpy(gpsd->points, gps->points + start_idx_stroke, sizeof(bGPDspoint) * len_stroke);
+ gpsd->totpoints = len_stroke;
+
+ gpsd->editcurve = BKE_gpencil_stroke_editcurve_new(len);
+ bGPDcurve *gpcd = gpsd->editcurve;
+ memcpy(gpcd->curve_points, gpc->curve_points + start_idx, sizeof(bGPDcurve_point) * len);
+
+ if (gps->dvert != NULL) {
+ gpsd->dvert = MEM_mallocN(sizeof(MDeformVert) * len, "gps stroke weights copy");
+ memcpy(gpsd->dvert, gps->dvert + start_idx, sizeof(MDeformVert) * len);
+
+ /* Copy weights */
+ int e = start_idx;
+ for (int j = 0; j < gpsd->totpoints; j++) {
+ MDeformVert *dvert_dst = &gps->dvert[e];
+ MDeformVert *dvert_src = &gps->dvert[j];
+ dvert_dst->dw = MEM_dupallocN(dvert_src->dw);
+ e++;
+ }
+ }
+
+ /* TODO: Copy vertex weights*/
+ for (uint32_t j = 0; j < gpcd->tot_curve_points; j++) {
+ bGPDcurve_point *gpcd_pt = &gpcd->curve_points[j];
+ BezTriple *bezt = &gpcd_pt->bezt;
+ gpcd_pt->flag |= GP_CURVE_POINT_SELECT;
+ BEZT_SEL_ALL(bezt);
+ }
+ gpcd->flag |= GP_CURVE_SELECT;
+
+ BKE_gpencil_stroke_geometry_update(gpd, gpsd, GP_GEO_UPDATE_DEFAULT);
+
+ /* add to temp buffer */
+ gpsd->next = gpsd->prev = NULL;
+
+ BLI_addtail(new_strokes, gpsd);
+
+ start_idx = -1;
}
- if ((start_idx != -1) || (start_idx == gps->totpoints - 1)) {
+ }
+ else {
+ /* Step through the original stroke's points:
+ * - We accumulate selected points (from start_idx to current index)
+ * and then convert that to a new stroke
+ */
+ for (int i = 0; i < gps->totpoints; i++) {
+ bGPDspoint *pt = &gps->points[i];
+ /* searching for start, are waiting for end? */
+ if ((start_idx != -1) || (start_idx == gps->totpoints - 1)) {
+ /* is this the first selected point for a new island? */
+ if (pt->flag & GP_SPOINT_SELECT) {
+ start_idx = i;
+ }
+ continue;
+ }
+
size_t len = 0;
/* is this the end of current island yet?
@@ -877,45 +936,47 @@ static void gpencil_duplicate_points(bGPdata *gpd,
len = i - start_idx + 1;
}
+ if (len < 1) {
+ continue;
+ }
+
/* make copies of the relevant data */
- if (len) {
- bGPDstroke *gpsd;
+ bGPDstroke *gpsd;
- /* make a stupid copy first of the entire stroke (to get the flags too) */
- gpsd = BKE_gpencil_stroke_duplicate((bGPDstroke *)gps, false, true);
+ /* make a stupid copy first of the entire stroke (to get the flags too) */
+ gpsd = BKE_gpencil_stroke_duplicate((bGPDstroke *)gps, false, false);
- /* saves original layer name */
- BLI_strncpy(gpsd->runtime.tmp_layerinfo, layername, sizeof(gpsd->runtime.tmp_layerinfo));
+ /* saves original layer name */
+ BLI_strncpy(gpsd->runtime.tmp_layerinfo, layername, sizeof(gpsd->runtime.tmp_layerinfo));
- /* now, make a new points array, and copy of the relevant parts */
- gpsd->points = MEM_mallocN(sizeof(bGPDspoint) * len, "gps stroke points copy");
- memcpy(gpsd->points, gps->points + start_idx, sizeof(bGPDspoint) * len);
- gpsd->totpoints = len;
+ /* now, make a new points array, and copy of the relevant parts */
+ gpsd->points = MEM_mallocN(sizeof(bGPDspoint) * len, "gps stroke points copy");
+ memcpy(gpsd->points, gps->points + start_idx, sizeof(bGPDspoint) * len);
+ gpsd->totpoints = len;
- if (gps->dvert != NULL) {
- gpsd->dvert = MEM_mallocN(sizeof(MDeformVert) * len, "gps stroke weights copy");
- memcpy(gpsd->dvert, gps->dvert + start_idx, sizeof(MDeformVert) * len);
-
- /* Copy weights */
- int e = start_idx;
- for (int j = 0; j < gpsd->totpoints; j++) {
- MDeformVert *dvert_dst = &gps->dvert[e];
- MDeformVert *dvert_src = &gps->dvert[j];
- dvert_dst->dw = MEM_dupallocN(dvert_src->dw);
- e++;
- }
+ if (gps->dvert != NULL) {
+ gpsd->dvert = MEM_mallocN(sizeof(MDeformVert) * len, "gps stroke weights copy");
+ memcpy(gpsd->dvert, gps->dvert + start_idx, sizeof(MDeformVert) * len);
+
+ /* Copy weights */
+ int e = start_idx;
+ for (int j = 0; j < gpsd->totpoints; j++) {
+ MDeformVert *dvert_dst = &gps->dvert[e];
+ MDeformVert *dvert_src = &gps->dvert[j];
+ dvert_dst->dw = MEM_dupallocN(dvert_src->dw);
+ e++;
}
+ }
- BKE_gpencil_stroke_geometry_update(gpd, gpsd);
+ BKE_gpencil_stroke_geometry_update(gpd, gpsd, GP_GEO_UPDATE_DEFAULT);
- /* add to temp buffer */
- gpsd->next = gpsd->prev = NULL;
+ /* add to temp buffer */
+ gpsd->next = gpsd->prev = NULL;
- BLI_addtail(new_strokes, gpsd);
+ BLI_addtail(new_strokes, gpsd);
- /* cleanup + reset for next */
- start_idx = -1;
- }
+ /* cleanup + reset for next */
+ start_idx = -1;
}
}
}
@@ -923,7 +984,6 @@ static void gpencil_duplicate_points(bGPdata *gpd,
static int gpencil_duplicate_exec(bContext *C, wmOperator *op)
{
bGPdata *gpd = ED_gpencil_data_get_active(C);
- const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
if (gpd == NULL) {
BKE_report(op->reports, RPT_ERROR, "No Grease Pencil data");
@@ -936,74 +996,108 @@ static int gpencil_duplicate_exec(bContext *C, wmOperator *op)
}
bool changed = false;
- if (is_curve_edit) {
- BKE_report(op->reports, RPT_ERROR, "Not implemented!");
- }
- else {
- /* for each visible (and editable) layer's selected strokes,
- * copy the strokes into a temporary buffer, then append
- * once all done
- */
- CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
- ListBase new_strokes = {NULL, NULL};
- bGPDframe *gpf = gpl->actframe;
- bGPDstroke *gps;
+ /* for each visible (and editable) layer's selected strokes,
+ * copy the strokes into a temporary buffer, then append
+ * once all done
+ */
+ CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
+ ListBase new_strokes = {NULL, NULL};
+ bGPDframe *gpf = gpl->actframe;
+
+ if (gpf == NULL) {
+ continue;
+ }
- if (gpf == NULL) {
+ /* make copies of selected strokes, and deselect these once we're done */
+ LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
+ /* skip strokes that are invalid for current view */
+ if (ED_gpencil_stroke_can_use(C, gps) == false) {
continue;
}
- /* make copies of selected strokes, and deselect these once we're done */
- for (gps = gpf->strokes.first; gps; gps = gps->next) {
- /* skip strokes that are invalid for current view */
- if (ED_gpencil_stroke_can_use(C, gps) == false) {
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps)) {
+ bGPDcurve *gpc = gps->editcurve;
+ if ((gpc->flag & GP_CURVE_SELECT) == 0) {
continue;
}
- if (gps->flag & GP_STROKE_SELECT) {
- if (gps->totpoints == 1) {
- /* Special Case: If there's just a single point in this stroke... */
- bGPDstroke *gpsd;
+ if (gpc->tot_curve_points == 1) {
+ /* Special Case: If there's just a single point in this stroke... */
+ bGPDstroke *gpsd;
- /* make direct copies of the stroke and its points */
- gpsd = BKE_gpencil_stroke_duplicate(gps, true, true);
+ /* make direct copies of the stroke and its points */
+ gpsd = BKE_gpencil_stroke_duplicate(gps, true, true);
- BLI_strncpy(
- gpsd->runtime.tmp_layerinfo, gpl->info, sizeof(gpsd->runtime.tmp_layerinfo));
+ BLI_strncpy(gpsd->runtime.tmp_layerinfo, gpl->info, sizeof(gpsd->runtime.tmp_layerinfo));
- /* Initialize triangle information. */
- BKE_gpencil_stroke_geometry_update(gpd, gpsd);
+ /* Initialize triangle information. */
+ BKE_gpencil_stroke_geometry_update(gpd, gpsd, GP_GEO_UPDATE_DEFAULT);
- /* add to temp buffer */
- gpsd->next = gpsd->prev = NULL;
- BLI_addtail(&new_strokes, gpsd);
- }
- else {
- /* delegate to a helper, as there's too much to fit in here (for copying subsets)... */
- gpencil_duplicate_points(gpd, gps, &new_strokes, gpl->info);
- }
-
- /* deselect original stroke, or else the originals get moved too
- * (when using the copy + move macro)
- */
- bGPDspoint *pt;
- int i;
- for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
- pt->flag &= ~GP_SPOINT_SELECT;
- }
- gps->flag &= ~GP_STROKE_SELECT;
- BKE_gpencil_stroke_select_index_reset(gps);
+ /* add to temp buffer */
+ gpsd->next = gpsd->prev = NULL;
+ BLI_addtail(&new_strokes, gpsd);
+ }
+ else {
+ gpencil_duplicate_points(gpd, gps, &new_strokes, gpl->info);
+ }
- changed = true;
+ /* Deselect the points */
+ for (uint32_t i = 0; i < gpc->tot_curve_points; i++) {
+ bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
+ BezTriple *bezt = &gpc_pt->bezt;
+ gpc_pt->flag &= ~GP_CURVE_POINT_SELECT;
+ BEZT_DESEL_ALL(bezt);
}
+ gpc->flag &= ~GP_CURVE_SELECT;
+ BKE_gpencil_stroke_select_index_reset(gps);
+
+ changed = true;
}
+ else {
+ if ((gps->flag & GP_STROKE_SELECT) == 0) {
+ continue;
+ }
+
+ if (gps->totpoints == 1) {
+ /* Special Case: If there's just a single point in this stroke... */
+ bGPDstroke *gpsd;
- /* add all new strokes in temp buffer to the frame (preventing double-copies) */
- BLI_movelisttolist(&gpf->strokes, &new_strokes);
- BLI_assert(new_strokes.first == NULL);
+ /* make direct copies of the stroke and its points */
+ gpsd = BKE_gpencil_stroke_duplicate(gps, true, true);
+
+ BLI_strncpy(gpsd->runtime.tmp_layerinfo, gpl->info, sizeof(gpsd->runtime.tmp_layerinfo));
+
+ /* Initialize triangle information. */
+ BKE_gpencil_stroke_geometry_update(gpd, gpsd, GP_GEO_UPDATE_DEFAULT);
+
+ /* add to temp buffer */
+ gpsd->next = gpsd->prev = NULL;
+ BLI_addtail(&new_strokes, gpsd);
+ }
+ else {
+ /* delegate to a helper, as there's too much to fit in here (for copying subsets)... */
+ gpencil_duplicate_points(gpd, gps, &new_strokes, gpl->info);
+ }
+
+ /* deselect original stroke, or else the originals get moved too
+ * (when using the copy + move macro)
+ */
+ for (int i = 0; i < gps->totpoints; i++) {
+ bGPDspoint *pt = &gps->points[i];
+ pt->flag &= ~GP_SPOINT_SELECT;
+ }
+ gps->flag &= ~GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_reset(gps);
+
+ changed = true;
+ }
}
- CTX_DATA_END;
+
+ /* add all new strokes in temp buffer to the frame (preventing double-copies) */
+ BLI_movelisttolist(&gpf->strokes, &new_strokes);
+ BLI_assert(new_strokes.first == NULL);
}
+ CTX_DATA_END;
if (changed) {
/* updates */
@@ -1109,8 +1203,8 @@ static void gpencil_add_move_points(bGPdata *gpd, bGPDframe *gpf, bGPDstroke *gp
gpencil_copy_move_point(gps_new, gps->points, gps->dvert, i, 0, true);
/* Calc geometry data. */
- BKE_gpencil_stroke_geometry_update(gpd, gps);
- BKE_gpencil_stroke_geometry_update(gpd, gps_new);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
+ BKE_gpencil_stroke_geometry_update(gpd, gps_new, GP_GEO_UPDATE_DEFAULT);
/* Deselect original point. */
pt->flag &= ~GP_SPOINT_SELECT;
@@ -1185,7 +1279,7 @@ static void gpencil_add_move_points(bGPdata *gpd, bGPDframe *gpf, bGPDstroke *gp
}
/* Calc geometry data. */
- BKE_gpencil_stroke_geometry_update(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
MEM_SAFE_FREE(temp_points);
MEM_SAFE_FREE(temp_dverts);
@@ -1235,8 +1329,7 @@ static void gpencil_curve_extrude_points(bGPdata *gpd,
BLI_insertlinkafter(&gpf->strokes, gps, gps_new);
- gps_new->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
- BKE_gpencil_stroke_geometry_update(gpd, gps_new);
+ BKE_gpencil_stroke_geometry_update(gpd, gps_new, GP_GEO_UPDATE_DEFAULT);
gpc_pt->flag &= ~GP_CURVE_POINT_SELECT;
BEZT_DESEL_ALL(&gpc_pt->bezt);
@@ -1282,8 +1375,7 @@ static void gpencil_curve_extrude_points(bGPdata *gpd,
BEZT_DESEL_ALL(&old_last->bezt);
}
- gps->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
- BKE_gpencil_stroke_geometry_update(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
}
}
@@ -1291,9 +1383,7 @@ static int gpencil_extrude_exec(bContext *C, wmOperator *op)
{
Object *obact = CTX_data_active_object(C);
bGPdata *gpd = (bGPdata *)obact->data;
- const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
- bGPDstroke *gps = NULL;
if (gpd == NULL) {
BKE_report(op->reports, RPT_ERROR, "No Grease Pencil data");
@@ -1310,16 +1400,13 @@ static int gpencil_extrude_exec(bContext *C, wmOperator *op)
continue;
}
- for (gps = gpf->strokes.first; gps; gps = gps->next) {
+ LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
/* skip strokes that are invalid for current view */
if (ED_gpencil_stroke_can_use(C, gps) == false) {
continue;
}
- if (is_curve_edit) {
- if (gps->editcurve == NULL) {
- continue;
- }
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps)) {
bGPDcurve *gpc = gps->editcurve;
if (gpc->flag & GP_CURVE_SELECT) {
gpencil_curve_extrude_points(gpd, gpf, gps, gpc);
@@ -1432,8 +1519,6 @@ static void gpencil_strokes_copypastebuf_colors_name_to_material_free(GHash *nam
/* Free copy/paste buffer data */
void ED_gpencil_strokes_copybuf_free(void)
{
- bGPDstroke *gps, *gpsn;
-
/* Free the colors buffer
* NOTE: This is done before the strokes so that the ptrs are still safe
*/
@@ -1443,23 +1528,12 @@ void ED_gpencil_strokes_copybuf_free(void)
}
/* Free the stroke buffer */
- for (gps = gpencil_strokes_copypastebuf.first; gps; gps = gpsn) {
- gpsn = gps->next;
-
- if (gps->points) {
- MEM_freeN(gps->points);
- }
- if (gps->dvert) {
- BKE_gpencil_free_stroke_weights(gps);
- MEM_freeN(gps->dvert);
- }
-
- MEM_SAFE_FREE(gps->triangles);
-
- BLI_freelinkN(&gpencil_strokes_copypastebuf, gps);
+ LISTBASE_FOREACH_MUTABLE (bGPDstroke *, gps, &gpencil_strokes_copypastebuf) {
+ BLI_remlink(&gpencil_strokes_copypastebuf, gps);
+ BKE_gpencil_free_stroke(gps);
}
- gpencil_strokes_copypastebuf.first = gpencil_strokes_copypastebuf.last = NULL;
+ BLI_listbase_clear(&gpencil_strokes_copypastebuf);
}
/**
@@ -1505,7 +1579,6 @@ static int gpencil_strokes_copy_exec(bContext *C, wmOperator *op)
Main *bmain = CTX_data_main(C);
Object *ob = CTX_data_active_object(C);
bGPdata *gpd = ED_gpencil_data_get_active(C);
- const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
if (gpd == NULL) {
BKE_report(op->reports, RPT_ERROR, "No Grease Pencil data");
@@ -1520,62 +1593,54 @@ static int gpencil_strokes_copy_exec(bContext *C, wmOperator *op)
/* clear the buffer first */
ED_gpencil_strokes_copybuf_free();
- if (is_curve_edit) {
- BKE_report(op->reports, RPT_ERROR, "Not implemented!");
- }
- else {
- /* for each visible (and editable) layer's selected strokes,
- * copy the strokes into a temporary buffer, then append
- * once all done
- */
- CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
- bGPDframe *gpf = gpl->actframe;
- bGPDstroke *gps;
+ /* for each visible (and editable) layer's selected strokes,
+ * copy the strokes into a temporary buffer, then append
+ * once all done
+ */
+ CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
+ bGPDframe *gpf = gpl->actframe;
- if (gpf == NULL) {
+ if (gpf == NULL) {
+ continue;
+ }
+
+ /* make copies of selected strokes, and deselect these once we're done */
+ LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
+ /* skip strokes that are invalid for current view */
+ if (ED_gpencil_stroke_can_use(C, gps) == false) {
continue;
}
- /* make copies of selected strokes, and deselect these once we're done */
- for (gps = gpf->strokes.first; gps; gps = gps->next) {
- /* skip strokes that are invalid for current view */
- if (ED_gpencil_stroke_can_use(C, gps) == false) {
- continue;
- }
+ bool is_stroke_selected = GPENCIL_STROKE_TYPE_BEZIER(gps) ?
+ (bool)(gps->editcurve->flag & GP_CURVE_SELECT) :
+ (bool)(gps->flag & GP_STROKE_SELECT);
- if (gps->flag & GP_STROKE_SELECT) {
- if (gps->totpoints == 1) {
- /* Special Case: If there's just a single point in this stroke... */
- bGPDstroke *gpsd;
+ if (is_stroke_selected) {
+ if (gps->totpoints == 1) {
+ /* Special Case: If there's just a single point in this stroke... */
+ bGPDstroke *gpsd;
- /* make direct copies of the stroke and its points */
- gpsd = BKE_gpencil_stroke_duplicate(gps, false, true);
+ /* make direct copies of the stroke and its points */
+ gpsd = BKE_gpencil_stroke_duplicate(gps, true, true);
- /* saves original layer name */
- BLI_strncpy(
- gpsd->runtime.tmp_layerinfo, gpl->info, sizeof(gpsd->runtime.tmp_layerinfo));
- gpsd->points = MEM_dupallocN(gps->points);
- if (gps->dvert != NULL) {
- gpsd->dvert = MEM_dupallocN(gps->dvert);
- BKE_gpencil_stroke_weights_duplicate(gps, gpsd);
- }
+ /* saves original layer name */
+ BLI_strncpy(gpsd->runtime.tmp_layerinfo, gpl->info, sizeof(gpsd->runtime.tmp_layerinfo));
- /* Calc geometry data. */
- BKE_gpencil_stroke_geometry_update(gpd, gpsd);
+ /* Calc geometry data. */
+ BKE_gpencil_stroke_geometry_update(gpd, gpsd, GP_GEO_UPDATE_DEFAULT);
- /* add to temp buffer */
- gpsd->next = gpsd->prev = NULL;
- BLI_addtail(&gpencil_strokes_copypastebuf, gpsd);
- }
- else {
- /* delegate to a helper, as there's too much to fit in here (for copying subsets)... */
- gpencil_duplicate_points(gpd, gps, &gpencil_strokes_copypastebuf, gpl->info);
- }
+ /* add to temp buffer */
+ gpsd->next = gpsd->prev = NULL;
+ BLI_addtail(&gpencil_strokes_copypastebuf, gpsd);
+ }
+ else {
+ /* delegate to a helper, as there's too much to fit in here (for copying subsets)... */
+ gpencil_duplicate_points(gpd, gps, &gpencil_strokes_copypastebuf, gpl->info);
}
}
}
- CTX_DATA_END;
}
+ CTX_DATA_END;
/* Build up hash of material colors used in these strokes */
if (gpencil_strokes_copypastebuf.first) {
@@ -1653,7 +1718,6 @@ static int gpencil_strokes_paste_exec(bContext *C, wmOperator *op)
{
Object *ob = CTX_data_active_object(C);
bGPdata *gpd = (bGPdata *)ob->data;
- const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
bGPDlayer *gpl = BKE_gpencil_layer_active_get(gpd); /* only use active for copy merge */
Scene *scene = CTX_data_scene(C);
bGPDframe *gpf;
@@ -1718,54 +1782,50 @@ static int gpencil_strokes_paste_exec(bContext *C, wmOperator *op)
/* Ensure that all the necessary colors exist */
new_colors = gpencil_copybuf_validate_colormap(C);
- if (is_curve_edit) {
- BKE_report(op->reports, RPT_ERROR, "Not implemented!");
- }
- else {
- /* Copy over the strokes from the buffer (and adjust the colors) */
- bGPDstroke *gps_init = (!on_back) ? gpencil_strokes_copypastebuf.first :
- gpencil_strokes_copypastebuf.last;
- for (bGPDstroke *gps = gps_init; gps; gps = (!on_back) ? gps->next : gps->prev) {
- if (ED_gpencil_stroke_can_use(C, gps)) {
- /* Need to verify if layer exists */
- if (type != GP_COPY_TO_ACTIVE) {
- gpl = BLI_findstring(
- &gpd->layers, gps->runtime.tmp_layerinfo, offsetof(bGPDlayer, info));
- if (gpl == NULL) {
- /* no layer - use active (only if layer deleted before paste) */
- gpl = BKE_gpencil_layer_active_get(gpd);
- }
- }
-
- /* Ensure we have a frame to draw into
- * NOTE: Since this is an op which creates strokes,
- * we are obliged to add a new frame if one
- * doesn't exist already
- */
- gpf = BKE_gpencil_layer_frame_get(gpl, CFRA, GP_GETFRAME_ADD_NEW);
- if (gpf) {
- /* Create new stroke */
- bGPDstroke *new_stroke = BKE_gpencil_stroke_duplicate(gps, true, true);
- new_stroke->runtime.tmp_layerinfo[0] = '\0';
- new_stroke->next = new_stroke->prev = NULL;
+ /* Copy over the strokes from the buffer (and adjust the colors) */
+ bGPDstroke *gps_init = (!on_back) ? gpencil_strokes_copypastebuf.first :
+ gpencil_strokes_copypastebuf.last;
+ for (bGPDstroke *gps = gps_init; gps; gps = (!on_back) ? gps->next : gps->prev) {
+ if (!ED_gpencil_stroke_can_use(C, gps)) {
+ continue;
+ }
+ /* Need to verify if layer exists */
+ if (type != GP_COPY_TO_ACTIVE) {
+ gpl = BLI_findstring(&gpd->layers, gps->runtime.tmp_layerinfo, offsetof(bGPDlayer, info));
+ if (gpl == NULL) {
+ /* no layer - use active (only if layer deleted before paste) */
+ gpl = BKE_gpencil_layer_active_get(gpd);
+ }
+ }
- /* Calc geometry data. */
- BKE_gpencil_stroke_geometry_update(gpd, new_stroke);
+ /* Ensure we have a frame to draw into
+ * NOTE: Since this is an op which creates strokes,
+ * we are obliged to add a new frame if one
+ * doesn't exist already
+ */
+ gpf = BKE_gpencil_layer_frame_get(gpl, CFRA, GP_GETFRAME_ADD_NEW);
+ if (gpf == NULL) {
+ continue;
+ }
+ /* Create new stroke */
+ bGPDstroke *new_stroke = BKE_gpencil_stroke_duplicate(gps, true, true);
+ new_stroke->runtime.tmp_layerinfo[0] = '\0';
+ new_stroke->next = new_stroke->prev = NULL;
- if (on_back) {
- BLI_addhead(&gpf->strokes, new_stroke);
- }
- else {
- BLI_addtail(&gpf->strokes, new_stroke);
- }
+ /* Calc geometry data. */
+ BKE_gpencil_stroke_geometry_update(gpd, new_stroke, GP_GEO_UPDATE_DEFAULT);
- /* Remap material */
- Material *ma = BLI_ghash_lookup(new_colors, POINTER_FROM_INT(new_stroke->mat_nr));
- new_stroke->mat_nr = BKE_gpencil_object_material_index_get(ob, ma);
- CLAMP_MIN(new_stroke->mat_nr, 0);
- }
- }
+ if (on_back) {
+ BLI_addhead(&gpf->strokes, new_stroke);
}
+ else {
+ BLI_addtail(&gpf->strokes, new_stroke);
+ }
+
+ /* Remap material */
+ Material *ma = BLI_ghash_lookup(new_colors, POINTER_FROM_INT(new_stroke->mat_nr));
+ new_stroke->mat_nr = BKE_gpencil_object_material_index_get(ob, ma);
+ CLAMP_MIN(new_stroke->mat_nr, 0);
}
/* free temp data */
@@ -1874,7 +1934,12 @@ static int gpencil_move_to_layer_exec(bContext *C, wmOperator *op)
continue;
}
- if (gps->flag & GP_STROKE_SELECT) {
+ bool is_stroke_selected = GPENCIL_STROKE_TYPE_BEZIER(gps) ?
+ gps->editcurve->flag & GP_CURVE_SELECT :
+ gps->flag & GP_STROKE_SELECT;
+
+ /* TODO: Don't just move entire strokes - instead, only copy the selected portions... */
+ if (is_stroke_selected) {
BLI_remlink(&gpf_src->strokes, gps);
BLI_addtail(&strokes, gps);
}
@@ -2210,7 +2275,11 @@ static int gpencil_delete_selected_strokes(bContext *C)
}
/* free stroke if selected */
- if (gps->flag & GP_STROKE_SELECT) {
+ bool is_stroke_selected = GPENCIL_STROKE_TYPE_BEZIER(gps) ?
+ gps->editcurve->flag & GP_CURVE_SELECT :
+ gps->flag & GP_STROKE_SELECT;
+
+ if (is_stroke_selected) {
BLI_remlink(&gpf->strokes, gps);
/* free stroke memory arrays, then stroke itself */
BKE_gpencil_free_stroke(gps);
@@ -2233,243 +2302,166 @@ static int gpencil_delete_selected_strokes(bContext *C)
/* ----------------------------------- */
-static bool gpencil_dissolve_selected_curve_points(bContext *C,
- bGPdata *gpd,
- eGP_DissolveMode mode)
+static bool gpencil_dissolve_selected_curve_points(bGPdata *gpd,
+ bGPDframe *gpf,
+ bGPDstroke *gps,
+ eGP_DissolveMode mode,
+ const bool do_segments_refit,
+ const float error_threshold)
{
- bool changed = false;
- GP_EDITABLE_CURVES_BEGIN(gps_iter, C, gpl, gps, gpc)
- {
- if (gpc->flag & GP_CURVE_SELECT) {
- int first = 0, last = 0;
- int num_points_remaining = gpc->tot_curve_points;
-
- switch (mode) {
- case GP_DISSOLVE_POINTS:
- for (int i = 0; i < gpc->tot_curve_points; i++) {
- bGPDcurve_point *cpt = &gpc->curve_points[i];
- if (cpt->flag & GP_CURVE_POINT_SELECT) {
- num_points_remaining--;
- }
- }
- break;
- case GP_DISSOLVE_BETWEEN:
- first = -1;
- for (int i = 0; i < gpc->tot_curve_points; i++) {
- bGPDcurve_point *cpt = &gpc->curve_points[i];
- if (cpt->flag & GP_CURVE_POINT_SELECT) {
- if (first < 0) {
- first = i;
- }
- last = i;
- }
- }
+ bGPDcurve *gpc = gps->editcurve;
+ if ((gpc->flag & GP_CURVE_SELECT) == 0) {
+ return false;
+ }
- for (int i = first + 1; i < last; i++) {
- bGPDcurve_point *cpt = &gpc->curve_points[i];
- if ((cpt->flag & GP_CURVE_POINT_SELECT) == 0) {
- num_points_remaining--;
- }
- }
- break;
- case GP_DISSOLVE_UNSELECT:
- for (int i = 0; i < gpc->tot_curve_points; i++) {
- bGPDcurve_point *cpt = &gpc->curve_points[i];
- if ((cpt->flag & GP_CURVE_POINT_SELECT) == 0) {
- num_points_remaining--;
- }
+ int num_points_remaining = gpc->tot_curve_points;
+ int old_num_points = gpc->tot_curve_points;
+ switch (mode) {
+ case GP_DISSOLVE_POINTS: {
+ num_points_remaining = BKE_gpencil_editcurve_dissolve(
+ gps, GP_CURVE_POINT_SELECT, do_segments_refit, error_threshold);
+ break;
+ }
+ case GP_DISSOLVE_BETWEEN: {
+ int first = -1, last = 0;
+ for (int i = 0; i < gpc->tot_curve_points; i++) {
+ bGPDcurve_point *cpt = &gpc->curve_points[i];
+ if (cpt->flag & GP_CURVE_POINT_SELECT) {
+ if (first < 0) {
+ first = i;
}
- break;
- default:
- return false;
- break;
- }
-
- if (num_points_remaining < 1) {
- /* Delete stroke */
- BLI_remlink(&gpf_->strokes, gps);
- BKE_gpencil_free_stroke(gps);
+ last = i;
+ }
}
- else {
- bGPDcurve_point *new_points = MEM_callocN(sizeof(bGPDcurve_point) * num_points_remaining,
- __func__);
-
- int idx = 0;
- switch (mode) {
- case GP_DISSOLVE_POINTS:
- for (int i = 0; i < gpc->tot_curve_points; i++) {
- bGPDcurve_point *cpt = &gpc->curve_points[i];
- bGPDcurve_point *new_cpt = &new_points[idx];
- if ((cpt->flag & GP_CURVE_POINT_SELECT) == 0) {
- *new_cpt = *cpt;
- idx++;
- }
- }
- break;
- case GP_DISSOLVE_BETWEEN:
- for (int i = 0; i < first; i++) {
- bGPDcurve_point *cpt = &gpc->curve_points[i];
- bGPDcurve_point *new_cpt = &new_points[idx];
-
- *new_cpt = *cpt;
- idx++;
- }
-
- for (int i = first; i < last; i++) {
- bGPDcurve_point *cpt = &gpc->curve_points[i];
- bGPDcurve_point *new_cpt = &new_points[idx];
- if (cpt->flag & GP_CURVE_POINT_SELECT) {
- *new_cpt = *cpt;
- idx++;
- }
- }
-
- for (int i = last; i < gpc->tot_curve_points; i++) {
- bGPDcurve_point *cpt = &gpc->curve_points[i];
- bGPDcurve_point *new_cpt = &new_points[idx];
- *new_cpt = *cpt;
- idx++;
- }
- break;
- case GP_DISSOLVE_UNSELECT:
- for (int i = 0; i < gpc->tot_curve_points; i++) {
- bGPDcurve_point *cpt = &gpc->curve_points[i];
- bGPDcurve_point *new_cpt = &new_points[idx];
- if (cpt->flag & GP_CURVE_POINT_SELECT) {
- *new_cpt = *cpt;
- idx++;
- }
- }
- break;
- default:
- return false;
- break;
+ for (int i = first + 1; i < last; i++) {
+ bGPDcurve_point *cpt = &gpc->curve_points[i];
+ if ((cpt->flag & GP_CURVE_POINT_SELECT) == 0) {
+ cpt->flag |= GP_CURVE_POINT_TAG;
}
+ }
- if (gpc->curve_points != NULL) {
- MEM_freeN(gpc->curve_points);
+ num_points_remaining = BKE_gpencil_editcurve_dissolve(
+ gps, GP_CURVE_POINT_TAG, do_segments_refit, error_threshold);
+ break;
+ }
+ case GP_DISSOLVE_UNSELECT: {
+ for (int i = 0; i < gpc->tot_curve_points; i++) {
+ bGPDcurve_point *cpt = &gpc->curve_points[i];
+ if ((cpt->flag & GP_CURVE_POINT_SELECT) == 0) {
+ cpt->flag |= GP_CURVE_POINT_TAG;
}
-
- gpc->curve_points = new_points;
- gpc->tot_curve_points = num_points_remaining;
-
- BKE_gpencil_editcurve_recalculate_handles(gps);
- gps->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
- BKE_gpencil_stroke_geometry_update(gpd, gps);
}
- changed = true;
+ num_points_remaining = BKE_gpencil_editcurve_dissolve(
+ gps, GP_CURVE_POINT_TAG, do_segments_refit, error_threshold);
+
+ break;
}
+ default:
+ return false;
+ break;
}
- GP_EDITABLE_CURVES_END(gps_iter);
- return changed;
+ if (num_points_remaining < 1) {
+ /* Delete stroke */
+ BLI_remlink(&gpf->strokes, gps);
+ BKE_gpencil_free_stroke(gps);
+ return true;
+ }
+ else if (num_points_remaining == old_num_points) {
+ /* Nothing to do so return. */
+ return false;
+ }
+
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
+ return true;
}
-static bool gpencil_dissolve_selected_stroke_points(bContext *C,
- bGPdata *gpd,
+static bool gpencil_dissolve_selected_stroke_points(bGPdata *gpd,
+ bGPDframe *gpf,
+ bGPDstroke *gps,
eGP_DissolveMode mode)
{
bool changed = false;
int first = 0;
int last = 0;
- GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) {
- /* the stroke must have at least one point selected for any operator */
- if (gps->flag & GP_STROKE_SELECT) {
- bGPDspoint *pt;
- MDeformVert *dvert = NULL;
- int i;
+ /* the stroke must have at least one point selected for any operator */
+ if (gps->flag & GP_STROKE_SELECT) {
+ bGPDspoint *pt;
+ MDeformVert *dvert = NULL;
+ int i;
- int tot = gps->totpoints; /* number of points in new buffer */
+ int tot = gps->totpoints; /* number of points in new buffer */
- /* first pass: count points to remove */
- switch (mode) {
- case GP_DISSOLVE_POINTS:
- /* Count how many points are selected (i.e. how many to remove) */
- for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
- if (pt->flag & GP_SPOINT_SELECT) {
- /* selected point - one of the points to remove */
- tot--;
- }
+ /* first pass: count points to remove */
+ switch (mode) {
+ case GP_DISSOLVE_POINTS:
+ /* Count how many points are selected (i.e. how many to remove) */
+ for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
+ if (pt->flag & GP_SPOINT_SELECT) {
+ /* selected point - one of the points to remove */
+ tot--;
}
- break;
- case GP_DISSOLVE_BETWEEN:
- /* need to find first and last point selected */
- first = -1;
- last = 0;
- for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
- if (pt->flag & GP_SPOINT_SELECT) {
- if (first < 0) {
- first = i;
- }
- last = i;
+ }
+ break;
+ case GP_DISSOLVE_BETWEEN:
+ /* need to find first and last point selected */
+ first = -1;
+ last = 0;
+ for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
+ if (pt->flag & GP_SPOINT_SELECT) {
+ if (first < 0) {
+ first = i;
}
+ last = i;
}
- /* count unselected points in the range */
- for (i = first, pt = gps->points + first; i < last; i++, pt++) {
- if ((pt->flag & GP_SPOINT_SELECT) == 0) {
- tot--;
- }
+ }
+ /* count unselected points in the range */
+ for (i = first, pt = gps->points + first; i < last; i++, pt++) {
+ if ((pt->flag & GP_SPOINT_SELECT) == 0) {
+ tot--;
}
- break;
- case GP_DISSOLVE_UNSELECT:
- /* count number of unselected points */
- for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
- if ((pt->flag & GP_SPOINT_SELECT) == 0) {
- tot--;
- }
+ }
+ break;
+ case GP_DISSOLVE_UNSELECT:
+ /* count number of unselected points */
+ for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
+ if ((pt->flag & GP_SPOINT_SELECT) == 0) {
+ tot--;
}
- break;
- default:
- return false;
- break;
- }
+ }
+ break;
+ default:
+ return false;
+ break;
+ }
- /* if no points are left, we simply delete the entire stroke */
- if (tot <= 0) {
- /* remove the entire stroke */
- BLI_remlink(&gpf_->strokes, gps);
- BKE_gpencil_free_stroke(gps);
- }
- else {
- /* just copy all points to keep into a smaller buffer */
- bGPDspoint *new_points = MEM_callocN(sizeof(bGPDspoint) * tot,
- "new gp stroke points copy");
- bGPDspoint *npt = new_points;
+ /* if no points are left, we simply delete the entire stroke */
+ if (tot <= 0) {
+ /* remove the entire stroke */
+ BLI_remlink(&gpf->strokes, gps);
+ BKE_gpencil_free_stroke(gps);
+ }
+ else {
+ /* just copy all points to keep into a smaller buffer */
+ bGPDspoint *new_points = MEM_callocN(sizeof(bGPDspoint) * tot, "new gp stroke points copy");
+ bGPDspoint *npt = new_points;
- MDeformVert *new_dvert = NULL;
- MDeformVert *ndvert = NULL;
+ MDeformVert *new_dvert = NULL;
+ MDeformVert *ndvert = NULL;
- if (gps->dvert != NULL) {
- new_dvert = MEM_callocN(sizeof(MDeformVert) * tot, "new gp stroke weights copy");
- ndvert = new_dvert;
- }
+ if (gps->dvert != NULL) {
+ new_dvert = MEM_callocN(sizeof(MDeformVert) * tot, "new gp stroke weights copy");
+ ndvert = new_dvert;
+ }
- switch (mode) {
- case GP_DISSOLVE_POINTS:
- (gps->dvert != NULL) ? dvert = gps->dvert : NULL;
- for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
- if ((pt->flag & GP_SPOINT_SELECT) == 0) {
- *npt = *pt;
- npt++;
-
- if (gps->dvert != NULL) {
- *ndvert = *dvert;
- ndvert->dw = MEM_dupallocN(dvert->dw);
- ndvert++;
- }
- }
- if (gps->dvert != NULL) {
- dvert++;
- }
- }
- break;
- case GP_DISSOLVE_BETWEEN:
- /* copy first segment */
- (gps->dvert != NULL) ? dvert = gps->dvert : NULL;
- for (i = 0, pt = gps->points; i < first; i++, pt++) {
+ switch (mode) {
+ case GP_DISSOLVE_POINTS:
+ (gps->dvert != NULL) ? dvert = gps->dvert : NULL;
+ for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
+ if ((pt->flag & GP_SPOINT_SELECT) == 0) {
*npt = *pt;
npt++;
@@ -2477,29 +2469,31 @@ static bool gpencil_dissolve_selected_stroke_points(bContext *C,
*ndvert = *dvert;
ndvert->dw = MEM_dupallocN(dvert->dw);
ndvert++;
- dvert++;
}
}
- /* copy segment (selected points) */
- (gps->dvert != NULL) ? dvert = gps->dvert + first : NULL;
- for (i = first, pt = gps->points + first; i < last; i++, pt++) {
- if (pt->flag & GP_SPOINT_SELECT) {
- *npt = *pt;
- npt++;
+ if (gps->dvert != NULL) {
+ dvert++;
+ }
+ }
+ break;
+ case GP_DISSOLVE_BETWEEN:
+ /* copy first segment */
+ (gps->dvert != NULL) ? dvert = gps->dvert : NULL;
+ for (i = 0, pt = gps->points; i < first; i++, pt++) {
+ *npt = *pt;
+ npt++;
- if (gps->dvert != NULL) {
- *ndvert = *dvert;
- ndvert->dw = MEM_dupallocN(dvert->dw);
- ndvert++;
- }
- }
- if (gps->dvert != NULL) {
- dvert++;
- }
+ if (gps->dvert != NULL) {
+ *ndvert = *dvert;
+ ndvert->dw = MEM_dupallocN(dvert->dw);
+ ndvert++;
+ dvert++;
}
- /* copy last segment */
- (gps->dvert != NULL) ? dvert = gps->dvert + last : NULL;
- for (i = last, pt = gps->points + last; i < gps->totpoints; i++, pt++) {
+ }
+ /* copy segment (selected points) */
+ (gps->dvert != NULL) ? dvert = gps->dvert + first : NULL;
+ for (i = first, pt = gps->points + first; i < last; i++, pt++) {
+ if (pt->flag & GP_SPOINT_SELECT) {
*npt = *pt;
npt++;
@@ -2507,85 +2501,125 @@ static bool gpencil_dissolve_selected_stroke_points(bContext *C,
*ndvert = *dvert;
ndvert->dw = MEM_dupallocN(dvert->dw);
ndvert++;
- dvert++;
}
}
+ if (gps->dvert != NULL) {
+ dvert++;
+ }
+ }
+ /* copy last segment */
+ (gps->dvert != NULL) ? dvert = gps->dvert + last : NULL;
+ for (i = last, pt = gps->points + last; i < gps->totpoints; i++, pt++) {
+ *npt = *pt;
+ npt++;
- break;
- case GP_DISSOLVE_UNSELECT:
- /* copy any selected point */
- (gps->dvert != NULL) ? dvert = gps->dvert : NULL;
- for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
- if (pt->flag & GP_SPOINT_SELECT) {
- *npt = *pt;
- npt++;
+ if (gps->dvert != NULL) {
+ *ndvert = *dvert;
+ ndvert->dw = MEM_dupallocN(dvert->dw);
+ ndvert++;
+ dvert++;
+ }
+ }
+
+ break;
+ case GP_DISSOLVE_UNSELECT:
+ /* copy any selected point */
+ (gps->dvert != NULL) ? dvert = gps->dvert : NULL;
+ for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
+ if (pt->flag & GP_SPOINT_SELECT) {
+ *npt = *pt;
+ npt++;
- if (gps->dvert != NULL) {
- *ndvert = *dvert;
- ndvert->dw = MEM_dupallocN(dvert->dw);
- ndvert++;
- }
- }
if (gps->dvert != NULL) {
- dvert++;
+ *ndvert = *dvert;
+ ndvert->dw = MEM_dupallocN(dvert->dw);
+ ndvert++;
}
}
- break;
- }
+ if (gps->dvert != NULL) {
+ dvert++;
+ }
+ }
+ break;
+ }
- /* free the old buffer */
- if (gps->points) {
- MEM_freeN(gps->points);
- }
- if (gps->dvert) {
- BKE_gpencil_free_stroke_weights(gps);
- MEM_freeN(gps->dvert);
- }
+ /* free the old buffer */
+ if (gps->points) {
+ MEM_freeN(gps->points);
+ }
+ if (gps->dvert) {
+ BKE_gpencil_free_stroke_weights(gps);
+ MEM_freeN(gps->dvert);
+ }
- /* save the new buffer */
- gps->points = new_points;
- gps->dvert = new_dvert;
- gps->totpoints = tot;
+ /* save the new buffer */
+ gps->points = new_points;
+ gps->dvert = new_dvert;
+ gps->totpoints = tot;
- /* Calc geometry data. */
- BKE_gpencil_stroke_geometry_update(gpd, gps);
+ /* Calc geometry data. */
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
- /* deselect the stroke, since none of its selected points will still be selected */
- gps->flag &= ~GP_STROKE_SELECT;
- BKE_gpencil_stroke_select_index_reset(gps);
- for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
- pt->flag &= ~GP_SPOINT_SELECT;
- }
+ /* deselect the stroke, since none of its selected points will still be selected */
+ gps->flag &= ~GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_reset(gps);
+ for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
+ pt->flag &= ~GP_SPOINT_SELECT;
}
-
- changed = true;
}
+
+ changed = true;
}
- GP_EDITABLE_STROKES_END(gpstroke_iter);
return changed;
}
/* Delete selected points but keep the stroke */
-static int gpencil_dissolve_selected_points(bContext *C, eGP_DissolveMode mode)
+static int gpencil_dissolve_selected_points(bContext *C,
+ eGP_DissolveMode mode,
+ const bool do_segments_refit,
+ const float error_threshold)
{
Object *ob = CTX_data_active_object(C);
bGPdata *gpd = (bGPdata *)ob->data;
- const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
+ const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
+
bool changed = false;
+ CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
+ bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe;
- if (is_curve_edit) {
- changed = gpencil_dissolve_selected_curve_points(C, gpd, mode);
- }
- else {
- changed = gpencil_dissolve_selected_stroke_points(C, gpd, mode);
+ for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
+ if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) {
+
+ if (gpf == NULL) {
+ continue;
+ }
+
+ /* Use LISTBASE_FOREACH_MUTABLE because strokes might be entirely deleted. */
+ LISTBASE_FOREACH_MUTABLE (bGPDstroke *, gps, &gpf->strokes) {
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps)) {
+ if (gpencil_dissolve_selected_curve_points(
+ gpd, gpf, gps, mode, do_segments_refit, error_threshold)) {
+ changed = true;
+ }
+ }
+ else {
+ if (gpencil_dissolve_selected_stroke_points(gpd, gpf, gps, mode)) {
+ changed = true;
+ }
+ }
+ }
+ }
+ }
}
+ CTX_DATA_END;
if (changed) {
DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
return OPERATOR_FINISHED;
}
+
return OPERATOR_CANCELLED;
}
@@ -2596,7 +2630,6 @@ static int gpencil_delete_selected_points(bContext *C)
{
Object *ob = CTX_data_active_object(C);
bGPdata *gpd = ED_gpencil_data_get_active(C);
- const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
bool changed = false;
@@ -2622,23 +2655,27 @@ static int gpencil_delete_selected_points(bContext *C)
continue;
}
- 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_reset(gps);
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps)) {
+ bGPDcurve *gpc = gps->editcurve;
+ if (gpc->flag & GP_CURVE_SELECT) {
+ gpc->flag &= ~GP_CURVE_SELECT;
+ BKE_gpencil_stroke_select_index_reset(gps);
- if (is_curve_edit) {
- bGPDcurve *gpc = gps->editcurve;
BKE_gpencil_curve_delete_tagged_points(
gpd, gpf, gps, gps->next, gpc, GP_CURVE_POINT_SELECT);
+ changed = true;
}
- else {
+ }
+ else {
+ 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_reset(gps);
/* delete unwanted points by splitting stroke into several smaller ones */
BKE_gpencil_stroke_delete_tagged_points(
gpd, gpf, gps, gps->next, GP_SPOINT_SELECT, false, 0);
+ changed = true;
}
-
- changed = true;
}
}
}
@@ -2732,8 +2769,10 @@ void GPENCIL_OT_delete(wmOperatorType *ot)
static int gpencil_dissolve_exec(bContext *C, wmOperator *op)
{
eGP_DissolveMode mode = RNA_enum_get(op->ptr, "type");
+ const bool do_segments_refit = RNA_boolean_get(op->ptr, "do_segments_refit");
+ const float error_threshold = RNA_float_get(op->ptr, "error_threshold");
- return gpencil_dissolve_selected_points(C, mode);
+ return gpencil_dissolve_selected_points(C, mode, do_segments_refit, error_threshold);
}
void GPENCIL_OT_dissolve(wmOperatorType *ot)
@@ -2769,6 +2808,22 @@ void GPENCIL_OT_dissolve(wmOperatorType *ot)
0,
"Type",
"Method used for dissolving stroke points");
+
+ RNA_def_boolean(ot->srna,
+ "do_segments_refit",
+ false,
+ "Refit Segments",
+ "Try to match the previous shape of bézier stroke segment");
+
+ RNA_def_float(ot->srna,
+ "error_threshold",
+ GP_DEFAULT_CURVE_ERROR,
+ 0.0f,
+ 100.0f,
+ "Threshold",
+ "Bézier curve fitting error threshold",
+ 0.0f,
+ 3.0f);
}
/** \} */
@@ -2799,7 +2854,6 @@ static int gpencil_snap_to_grid(bContext *C, wmOperator *UNUSED(op))
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
Object *obact = CTX_data_active_object(C);
const float gridf = ED_view3d_grid_view_scale(scene, v3d, region, NULL);
- const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
bool changed = false;
LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
@@ -2821,39 +2875,60 @@ static int gpencil_snap_to_grid(bContext *C, wmOperator *UNUSED(op))
continue;
}
- if (is_curve_edit) {
- if (gps->editcurve == NULL) {
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps)) {
+ bGPDcurve *gpc = gps->editcurve;
+ if ((gpc->flag & GP_CURVE_SELECT) == 0) {
continue;
}
float inv_diff_mat[4][4];
invert_m4_m4_safe(inv_diff_mat, diff_mat);
- bGPDcurve *gpc = gps->editcurve;
for (int i = 0; i < gpc->tot_curve_points; i++) {
bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
BezTriple *bezt = &gpc_pt->bezt;
if (gpc_pt->flag & GP_CURVE_POINT_SELECT) {
- float tmp0[3], tmp1[3], tmp2[3], offset[3];
- mul_v3_m4v3(tmp0, diff_mat, bezt->vec[0]);
- mul_v3_m4v3(tmp1, diff_mat, bezt->vec[1]);
- mul_v3_m4v3(tmp2, diff_mat, bezt->vec[2]);
-
- /* calculate the offset vector */
- offset[0] = gridf * floorf(0.5f + tmp1[0] / gridf) - tmp1[0];
- offset[1] = gridf * floorf(0.5f + tmp1[1] / gridf) - tmp1[1];
- offset[2] = gridf * floorf(0.5f + tmp1[2] / gridf) - tmp1[2];
-
- /* shift bezTriple */
- add_v3_v3(bezt->vec[0], offset);
- add_v3_v3(bezt->vec[1], offset);
- add_v3_v3(bezt->vec[2], offset);
-
- mul_v3_m4v3(tmp0, inv_diff_mat, bezt->vec[0]);
- mul_v3_m4v3(tmp1, inv_diff_mat, bezt->vec[1]);
- mul_v3_m4v3(tmp2, inv_diff_mat, bezt->vec[2]);
- copy_v3_v3(bezt->vec[0], tmp0);
- copy_v3_v3(bezt->vec[1], tmp1);
- copy_v3_v3(bezt->vec[2], tmp2);
+ /* We move the entire handle if the control point is selected. */
+ if (bezt->f2 & SELECT) {
+ float tmp[3], offset[3];
+ mul_v3_m4v3(tmp, diff_mat, bezt->vec[1]);
+
+ /* calculate the offset vector */
+ offset[0] = gridf * floorf(0.5f + tmp[0] / gridf) - tmp[0];
+ offset[1] = gridf * floorf(0.5f + tmp[1] / gridf) - tmp[1];
+ offset[2] = gridf * floorf(0.5f + tmp[2] / gridf) - tmp[2];
+
+ /* shift bezTriple */
+ add_v3_v3(bezt->vec[0], offset);
+ add_v3_v3(bezt->vec[1], offset);
+ add_v3_v3(bezt->vec[2], offset);
+
+ mul_v3_m4v3(bezt->vec[0], inv_diff_mat, bezt->vec[0]);
+ mul_v3_m4v3(bezt->vec[1], inv_diff_mat, bezt->vec[1]);
+ mul_v3_m4v3(bezt->vec[2], inv_diff_mat, bezt->vec[2]);
+ }
+ else {
+ /* Move the handles to the grid individually. */
+ float tmp0[3], tmp1[3];
+ mul_v3_m4v3(tmp0, diff_mat, bezt->vec[0]);
+ mul_v3_m4v3(tmp1, diff_mat, bezt->vec[2]);
+
+ /* Calculate nearest point on the grid. */
+ tmp0[0] = gridf * floorf(0.5f + tmp0[0] / gridf);
+ tmp0[1] = gridf * floorf(0.5f + tmp0[1] / gridf);
+ tmp0[2] = gridf * floorf(0.5f + tmp0[2] / gridf);
+
+ tmp1[0] = gridf * floorf(0.5f + tmp1[0] / gridf);
+ tmp1[1] = gridf * floorf(0.5f + tmp1[1] / gridf);
+ tmp1[2] = gridf * floorf(0.5f + tmp1[2] / gridf);
+
+ /* Write to the selected handles. */
+ if (bezt->f1 & SELECT) {
+ mul_v3_m4v3(bezt->vec[0], inv_diff_mat, tmp0);
+ }
+ if (bezt->f3 & SELECT) {
+ mul_v3_m4v3(bezt->vec[2], inv_diff_mat, tmp1);
+ }
+ }
changed = true;
}
@@ -2861,11 +2936,13 @@ static int gpencil_snap_to_grid(bContext *C, wmOperator *UNUSED(op))
if (changed) {
BKE_gpencil_editcurve_recalculate_handles(gps);
- gps->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
- BKE_gpencil_stroke_geometry_update(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
}
}
else {
+ if ((gps->flag & GP_STROKE_SELECT) == 0) {
+ continue;
+ }
/* TODO: if entire stroke is selected, offset entire stroke by same amount? */
for (int i = 0; i < gps->totpoints; i++) {
bGPDspoint *pt = &gps->points[i];
@@ -2924,7 +3001,6 @@ void GPENCIL_OT_snap_to_grid(wmOperatorType *ot)
static int gpencil_snap_to_cursor(bContext *C, wmOperator *op)
{
bGPdata *gpd = ED_gpencil_data_get_active(C);
- const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
Scene *scene = CTX_data_scene(C);
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
Object *obact = CTX_data_active_object(C);
@@ -2933,53 +3009,117 @@ static int gpencil_snap_to_cursor(bContext *C, wmOperator *op)
const float *cursor_global = scene->cursor.location;
bool changed = false;
- if (is_curve_edit) {
- BKE_report(op->reports, RPT_ERROR, "Not implemented!");
- }
- else {
- LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
- /* only editable and visible layers are considered */
- if (BKE_gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) {
- bGPDframe *gpf = gpl->actframe;
- float diff_mat[4][4];
+ LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
+ /* only editable and visible layers are considered */
+ if (BKE_gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) {
+ bGPDframe *gpf = gpl->actframe;
+ float diff_mat[4][4];
- /* calculate difference matrix */
- BKE_gpencil_layer_transform_matrix_get(depsgraph, obact, gpl, diff_mat);
+ /* calculate difference matrix */
+ BKE_gpencil_layer_transform_matrix_get(depsgraph, obact, gpl, diff_mat);
- LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
- bGPDspoint *pt;
- int i;
+ LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
+ /* skip strokes that are invalid for current view */
+ if (ED_gpencil_stroke_can_use(C, gps) == false) {
+ continue;
+ }
+ /* check if the color is editable */
+ if (ED_gpencil_stroke_material_editable(obact, gpl, gps) == false) {
+ continue;
+ }
- /* skip strokes that are invalid for current view */
- if (ED_gpencil_stroke_can_use(C, gps) == false) {
- continue;
+ bool is_stroke_selected = GPENCIL_STROKE_TYPE_BEZIER(gps) ?
+ (bool)(gps->editcurve->flag & GP_CURVE_SELECT) :
+ (bool)(gps->flag & GP_STROKE_SELECT);
+
+ /* only continue if this stroke is selected (editable doesn't guarantee this)... */
+ if ((is_stroke_selected) == 0) {
+ continue;
+ }
+
+ if (use_offset) {
+ /* TODO: Allow using midpoint instead? */
+ float offset[3];
+
+ /* To avoid recalculating the curve, we will offset the curve data first and then all the
+ * stroke points. */
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps)) {
+ float tmp[3];
+ bGPDcurve *gpc = gps->editcurve;
+ /* Calculate offset. */
+ mul_v3_m4v3(tmp, diff_mat, gpc->curve_points->bezt.vec[1]);
+ sub_v3_v3v3(offset, cursor_global, tmp);
+
+ /* Offset points. */
+ for (int i = 0; i < gpc->tot_curve_points; i++) {
+ bGPDcurve_point *cpt = &gpc->curve_points[i];
+ BezTriple *bezt = &cpt->bezt;
+ for (int j = 0; j < 3; j++) {
+ add_v3_v3(bezt->vec[j], offset);
+ }
+ }
}
- /* check if the color is editable */
- if (ED_gpencil_stroke_material_editable(obact, gpl, gps) == false) {
- continue;
+ else {
+ /* compute offset from first point of stroke to cursor */
+ sub_v3_v3v3(offset, cursor_global, &gps->points->x);
}
- /* only continue if this stroke is selected (editable doesn't guarantee this)... */
- if ((gps->flag & GP_STROKE_SELECT) == 0) {
- continue;
+
+ /* apply offset to all points in the stroke */
+ for (int i = 0; i < gps->totpoints; i++) {
+ bGPDspoint *pt = &gps->points[i];
+ add_v3_v3(&pt->x, offset);
}
- if (use_offset) {
- float offset[3];
+ changed = true;
+ }
+ else {
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps)) {
+ float inv_diff_mat[4][4];
+ invert_m4_m4_safe(inv_diff_mat, diff_mat);
- /* compute offset from first point of stroke to cursor */
- /* TODO: Allow using midpoint instead? */
- sub_v3_v3v3(offset, cursor_global, &gps->points->x);
+ bGPDcurve *gpc = gps->editcurve;
+ for (int i = 0; i < gpc->tot_curve_points; i++) {
+ bGPDcurve_point *cpt = &gpc->curve_points[i];
+ BezTriple *bezt = &cpt->bezt;
+ if ((cpt->flag & GP_CURVE_POINT_SELECT) == 0) {
+ continue;
+ }
+
+ float cur[3];
+ mul_v3_m4v3(cur, inv_diff_mat, cursor_global);
+
+ /* If the control point is selected, snap it to the cursor and offset the handles
+ * accordingly. */
+ if (bezt->f2 & SELECT) {
+ float offset[3];
+ sub_v3_v3v3(offset, cur, bezt->vec[1]);
+
+ for (int j = 0; j < 3; j++) {
+ add_v3_v3(bezt->vec[j], offset);
+ }
+ }
+ /* Snap the handles to the cursor. */
+ else {
+ if (bezt->f1 & SELECT) {
+ copy_v3_v3(bezt->vec[0], cur);
+ }
+ if (bezt->f3 & SELECT) {
+ copy_v3_v3(bezt->vec[2], cur);
+ }
+ }
- /* apply offset to all points in the stroke */
- for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
- add_v3_v3(&pt->x, offset);
+ changed = true;
}
- changed = true;
+ if (changed) {
+ BKE_gpencil_editcurve_recalculate_handles(gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
+ }
}
else {
/* affect each selected point */
- for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
+ for (int i = 0; i < gps->totpoints; i++) {
+ bGPDspoint *pt = &gps->points[i];
if (pt->flag & GP_SPOINT_SELECT) {
copy_v3_v3(&pt->x, cursor_global);
gpencil_apply_parent_point(depsgraph, obact, gpl, pt);
@@ -3051,9 +3191,6 @@ static bool gpencil_stroke_points_centroid(Depsgraph *depsgraph,
BKE_gpencil_layer_transform_matrix_get(depsgraph, obact, gpl, diff_mat);
LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
- bGPDspoint *pt;
- int i;
-
/* skip strokes that are invalid for current view */
if (ED_gpencil_stroke_can_use(C, gps) == false) {
continue;
@@ -3062,13 +3199,45 @@ static bool gpencil_stroke_points_centroid(Depsgraph *depsgraph,
if (ED_gpencil_stroke_material_editable(obact, gpl, gps) == false) {
continue;
}
- /* only continue if this stroke is selected (editable doesn't guarantee this)... */
- if ((gps->flag & GP_STROKE_SELECT) == 0) {
- continue;
+
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps)) {
+ bGPDcurve *gpc = gps->editcurve;
+ if ((gpc->flag & GP_CURVE_SELECT) == 0) {
+ continue;
+ }
+
+ for (int i = 0; i < gpc->tot_curve_points; i++) {
+ bGPDcurve_point *cpt = &gpc->curve_points[i];
+ BezTriple *bezt = &cpt->bezt;
+ if ((cpt->flag * GP_CURVE_POINT_SELECT) == 0) {
+ continue;
+ }
+
+ float fpt[3];
+ for (int j = 0; j < 3; j++) {
+ if (BEZT_ISSEL_IDX(bezt, j)) {
+ mul_v3_m4v3(fpt, diff_mat, bezt->vec[j]);
+
+ add_v3_v3(r_centroid, fpt);
+ minmax_v3v3_v3(r_min, r_max, fpt);
+ (*count)++;
+ }
+ }
+ }
+
+ changed = true;
}
+ else {
+ /* only continue if this stroke is selected (editable doesn't guarantee this)... */
+ if ((gps->flag & GP_STROKE_SELECT) == 0) {
+ continue;
+ }
- for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
- if (pt->flag & GP_SPOINT_SELECT) {
+ for (int i = 0; i < gps->totpoints; i++) {
+ bGPDspoint *pt = &gps->points[i];
+ if ((pt->flag & GP_SPOINT_SELECT) == 0) {
+ continue;
+ }
/* apply parent transformations */
float fpt[3];
mul_v3_m4v3(fpt, diff_mat, &pt->x);
@@ -3078,9 +3247,8 @@ static bool gpencil_stroke_points_centroid(Depsgraph *depsgraph,
(*count)++;
}
+ changed = true;
}
-
- changed = true;
}
}
}
@@ -3088,12 +3256,11 @@ static bool gpencil_stroke_points_centroid(Depsgraph *depsgraph,
return changed;
}
-static int gpencil_snap_cursor_to_sel(bContext *C, wmOperator *op)
+static int gpencil_snap_cursor_to_sel(bContext *C, wmOperator *UNUSED(op))
{
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
Object *obact = CTX_data_active_object(C);
bGPdata *gpd = ED_gpencil_data_get_active(C);
- const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
Scene *scene = CTX_data_scene(C);
@@ -3105,12 +3272,7 @@ static int gpencil_snap_cursor_to_sel(bContext *C, wmOperator *op)
INIT_MINMAX(min, max);
bool changed = false;
- if (is_curve_edit) {
- BKE_report(op->reports, RPT_ERROR, "Not implemented!");
- }
- else {
- changed = gpencil_stroke_points_centroid(depsgraph, C, obact, gpd, centroid, min, max, &count);
- }
+ changed = gpencil_stroke_points_centroid(depsgraph, C, obact, gpd, centroid, min, max, &count);
if (changed) {
if (scene->toolsettings->transform_pivot_point == V3D_AROUND_CENTER_BOUNDS) {
@@ -3218,8 +3380,6 @@ static int gpencil_stroke_cyclical_set_exec(bContext *C, wmOperator *op)
const int type = RNA_enum_get(op->ptr, "type");
const bool geometry = RNA_boolean_get(op->ptr, "geometry");
const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
- const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
- bGPDstroke *gps = NULL;
/* sanity checks */
if (ELEM(NULL, gpd)) {
@@ -3237,11 +3397,18 @@ static int gpencil_stroke_cyclical_set_exec(bContext *C, wmOperator *op)
continue;
}
- for (gps = gpf->strokes.first; gps; gps = gps->next) {
+ LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(ob, gps->mat_nr + 1);
/* skip strokes that are not selected or invalid for current view */
- if (((gps->flag & GP_STROKE_SELECT) == 0) ||
- ED_gpencil_stroke_can_use(C, gps) == false) {
+ bool is_stroke_selected = GPENCIL_STROKE_TYPE_BEZIER(gps) ?
+ (bool)(gps->editcurve->flag & GP_CURVE_SELECT) :
+ (bool)(gps->flag & GP_STROKE_SELECT);
+
+ if (!is_stroke_selected) {
+ continue;
+ }
+
+ if (ED_gpencil_stroke_can_use(C, gps) == false) {
continue;
}
/* skip hidden or locked colors */
@@ -3271,14 +3438,13 @@ static int gpencil_stroke_cyclical_set_exec(bContext *C, wmOperator *op)
if (before != (gps->flag & GP_STROKE_CYCLIC)) {
/* Create new geometry. */
- if (is_curve_edit) {
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps)) {
BKE_gpencil_editcurve_recalculate_handles(gps);
- gps->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
- BKE_gpencil_stroke_geometry_update(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
}
else if ((gps->flag & GP_STROKE_CYCLIC) && geometry) {
BKE_gpencil_stroke_close(gps);
- BKE_gpencil_stroke_geometry_update(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
}
changed = true;
@@ -3303,22 +3469,6 @@ static int gpencil_stroke_cyclical_set_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
-static bool gpencil_cyclical_set_curve_edit_poll_property(const bContext *C,
- wmOperator *UNUSED(op),
- const PropertyRNA *prop)
-{
- bGPdata *gpd = ED_gpencil_data_get_active(C);
- if (gpd != NULL && GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd)) {
- const char *prop_id = RNA_property_identifier(prop);
- /* Only show type in curve edit mode */
- if (!STREQ(prop_id, "type")) {
- return false;
- }
- }
-
- return true;
-}
-
/**
* Similar to #CURVE_OT_cyclic_toggle or #MASK_OT_cyclic_toggle, but with
* option to force opened/closed strokes instead of just toggle behavior.
@@ -3342,15 +3492,17 @@ void GPENCIL_OT_stroke_cyclical_set(wmOperatorType *ot)
/* api callbacks */
ot->exec = gpencil_stroke_cyclical_set_exec;
ot->poll = gpencil_active_layer_poll;
- ot->poll_property = gpencil_cyclical_set_curve_edit_poll_property;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
ot->prop = RNA_def_enum(ot->srna, "type", cyclic_type, GP_STROKE_CYCLIC_TOGGLE, "Type", "");
- prop = RNA_def_boolean(
- ot->srna, "geometry", false, "Create Geometry", "Create new geometry for closing stroke");
+ prop = RNA_def_boolean(ot->srna,
+ "geometry",
+ false,
+ "Create Geometry",
+ "Create new geometry for closing stroke (only applies for poly strokes)");
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
}
@@ -3371,6 +3523,7 @@ static int gpencil_stroke_caps_set_exec(bContext *C, wmOperator *op)
{
bGPdata *gpd = ED_gpencil_data_get_active(C);
Object *ob = CTX_data_active_object(C);
+ const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
const int type = RNA_enum_get(op->ptr, "type");
/* sanity checks */
@@ -3381,45 +3534,55 @@ static int gpencil_stroke_caps_set_exec(bContext *C, wmOperator *op)
bool changed = false;
/* loop all selected strokes */
CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
- if (gpl->actframe == NULL) {
- continue;
- }
+ bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe;
- for (bGPDstroke *gps = gpl->actframe->strokes.last; gps; gps = gps->prev) {
- MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(ob, gps->mat_nr + 1);
+ for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
+ if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) {
+ if (gpf == NULL) {
+ continue;
+ }
- /* skip strokes that are not selected or invalid for current view */
- if (((gps->flag & GP_STROKE_SELECT) == 0) || (ED_gpencil_stroke_can_use(C, gps) == false)) {
- continue;
- }
- /* skip hidden or locked colors */
- if (!gp_style || (gp_style->flag & GP_MATERIAL_HIDE) ||
- (gp_style->flag & GP_MATERIAL_LOCKED)) {
- continue;
- }
+ LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
+ MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(ob, gps->mat_nr + 1);
- short prev_first = gps->caps[0];
- short prev_last = gps->caps[1];
+ /* skip strokes that are not selected or invalid for current view */
+ bool is_stroke_selected = GPENCIL_STROKE_TYPE_BEZIER(gps) ?
+ (bool)(gps->editcurve->flag & GP_CURVE_SELECT) :
+ (bool)(gps->flag & GP_STROKE_SELECT);
- if (ELEM(type, GP_STROKE_CAPS_TOGGLE_BOTH, GP_STROKE_CAPS_TOGGLE_START)) {
- ++gps->caps[0];
- if (gps->caps[0] >= GP_STROKE_CAP_MAX) {
- gps->caps[0] = GP_STROKE_CAP_ROUND;
- }
- }
- if (ELEM(type, GP_STROKE_CAPS_TOGGLE_BOTH, GP_STROKE_CAPS_TOGGLE_END)) {
- ++gps->caps[1];
- if (gps->caps[1] >= GP_STROKE_CAP_MAX) {
- gps->caps[1] = GP_STROKE_CAP_ROUND;
- }
- }
- if (type == GP_STROKE_CAPS_TOGGLE_DEFAULT) {
- gps->caps[0] = GP_STROKE_CAP_ROUND;
- gps->caps[1] = GP_STROKE_CAP_ROUND;
- }
+ if (!is_stroke_selected) {
+ continue;
+ }
+ /* skip hidden or locked colors */
+ if (!gp_style || (gp_style->flag & GP_MATERIAL_HIDE) ||
+ (gp_style->flag & GP_MATERIAL_LOCKED)) {
+ continue;
+ }
- if (prev_first != gps->caps[0] || prev_last != gps->caps[1]) {
- changed = true;
+ short prev_first = gps->caps[0];
+ short prev_last = gps->caps[1];
+
+ if (ELEM(type, GP_STROKE_CAPS_TOGGLE_BOTH, GP_STROKE_CAPS_TOGGLE_START)) {
+ ++gps->caps[0];
+ if (gps->caps[0] >= GP_STROKE_CAP_MAX) {
+ gps->caps[0] = GP_STROKE_CAP_ROUND;
+ }
+ }
+ if (ELEM(type, GP_STROKE_CAPS_TOGGLE_BOTH, GP_STROKE_CAPS_TOGGLE_END)) {
+ ++gps->caps[1];
+ if (gps->caps[1] >= GP_STROKE_CAP_MAX) {
+ gps->caps[1] = GP_STROKE_CAP_ROUND;
+ }
+ }
+ if (type == GP_STROKE_CAPS_TOGGLE_DEFAULT) {
+ gps->caps[0] = GP_STROKE_CAP_ROUND;
+ gps->caps[1] = GP_STROKE_CAP_ROUND;
+ }
+
+ if (prev_first != gps->caps[0] || prev_last != gps->caps[1]) {
+ changed = true;
+ }
+ }
}
}
}
@@ -3543,11 +3706,6 @@ static int gpencil_stroke_join_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
- const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
- if (is_curve_edit) {
- return OPERATOR_CANCELLED;
- }
-
if (activegpl->flag & GP_LAYER_LOCKED) {
return OPERATOR_CANCELLED;
}
@@ -3567,29 +3725,34 @@ static int gpencil_stroke_join_exec(bContext *C, wmOperator *op)
/* Add all stroke selected of the frame. */
LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
- if (gps->flag & GP_STROKE_SELECT) {
- /* skip strokes that are invalid for current view */
- if (ED_gpencil_stroke_can_use(C, gps) == false) {
- continue;
- }
- /* check if the color is editable. */
- if (ED_gpencil_stroke_material_editable(ob, gpl, gps) == false) {
- continue;
- }
- elem = &strokes_list[tot_strokes];
- elem->gpf = gpf;
- elem->gps = gps;
- elem->used = false;
-
- tot_strokes++;
- /* Limit the number of strokes. */
- if (tot_strokes == max_join_strokes) {
- BKE_reportf(op->reports,
- RPT_WARNING,
- "Too many strokes selected, only joined first %d strokes",
- max_join_strokes);
- break;
- }
+ bool is_stroke_selected = GPENCIL_STROKE_TYPE_BEZIER(gps) ?
+ (bool)(gps->editcurve->flag & GP_CURVE_SELECT) :
+ (bool)(gps->flag & GP_STROKE_SELECT);
+
+ if (!is_stroke_selected) {
+ continue;
+ }
+ /* skip strokes that are invalid for current view */
+ if (ED_gpencil_stroke_can_use(C, gps) == false) {
+ continue;
+ }
+ /* check if the color is editable. */
+ if (ED_gpencil_stroke_material_editable(ob, gpl, gps) == false) {
+ continue;
+ }
+ elem = &strokes_list[tot_strokes];
+ elem->gpf = gpf;
+ elem->gps = gps;
+ elem->used = false;
+
+ tot_strokes++;
+ /* Limit the number of strokes. */
+ if (tot_strokes == max_join_strokes) {
+ BKE_reportf(op->reports,
+ RPT_WARNING,
+ "Too many strokes selected, only joined first %d strokes",
+ max_join_strokes);
+ break;
}
}
}
@@ -3623,12 +3786,16 @@ static int gpencil_stroke_join_exec(bContext *C, wmOperator *op)
}
/* Calc geometry data for new stroke. */
- BKE_gpencil_stroke_geometry_update(gpd, gps_new);
+ BKE_gpencil_stroke_geometry_update(gpd, gps_new, GP_GEO_UPDATE_DEFAULT);
/* If join only, delete old strokes. */
if (type == GP_STROKE_JOIN) {
for (int i = 0; i < tot_strokes; i++) {
elem = &strokes_list[i];
+ if (GPENCIL_STROKE_TYPE_BEZIER(elem->gps) != GPENCIL_STROKE_TYPE_BEZIER(gps_new)) {
+ continue;
+ }
+
BLI_remlink(&elem->gpf->strokes, elem->gps);
BKE_gpencil_free_stroke(elem->gps);
}
@@ -3679,7 +3846,7 @@ void GPENCIL_OT_stroke_join(wmOperatorType *ot)
/** \name Stroke Flip Operator
* \{ */
-static int gpencil_stroke_flip_exec(bContext *C, wmOperator *op)
+static int gpencil_stroke_flip_exec(bContext *C, wmOperator *UNUSED(op))
{
bGPdata *gpd = ED_gpencil_data_get_active(C);
Object *ob = CTX_data_active_object(C);
@@ -3689,7 +3856,6 @@ static int gpencil_stroke_flip_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
- const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
bool changed = false;
/* read all selected strokes */
CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
@@ -3699,26 +3865,27 @@ static int gpencil_stroke_flip_exec(bContext *C, wmOperator *op)
}
LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
- if (gps->flag & GP_STROKE_SELECT) {
- /* skip strokes that are invalid for current view */
- if (ED_gpencil_stroke_can_use(C, gps) == false) {
- continue;
- }
- /* check if the color is editable */
- if (ED_gpencil_stroke_material_editable(ob, gpl, gps) == false) {
- continue;
- }
+ bool is_stroke_selected = GPENCIL_STROKE_TYPE_BEZIER(gps) ?
+ (bool)(gps->editcurve->flag & GP_CURVE_SELECT) :
+ (bool)(gps->flag & GP_STROKE_SELECT);
- if (is_curve_edit) {
- BKE_report(op->reports, RPT_ERROR, "Not implemented!");
- }
- else {
- /* Flip stroke. */
- BKE_gpencil_stroke_flip(gps);
- }
+ if (!is_stroke_selected) {
+ continue;
+ }
- changed = true;
+ /* skip strokes that are invalid for current view */
+ if (ED_gpencil_stroke_can_use(C, gps) == false) {
+ continue;
}
+ /* check if the color is editable */
+ if (ED_gpencil_stroke_material_editable(ob, gpl, gps) == false) {
+ continue;
+ }
+
+ /* Flip stroke. */
+ BKE_gpencil_stroke_flip(gps);
+
+ changed = true;
}
}
CTX_DATA_END;
@@ -3762,7 +3929,6 @@ static int gpencil_strokes_reproject_exec(bContext *C, wmOperator *op)
int oldframe = (int)DEG_get_ctime(depsgraph);
const eGP_ReprojectModes mode = RNA_enum_get(op->ptr, "type");
const bool keep_original = RNA_boolean_get(op->ptr, "keep_original");
- const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
/* Init snap context for geometry projection. */
@@ -3785,37 +3951,28 @@ static int gpencil_strokes_reproject_exec(bContext *C, wmOperator *op)
continue;
}
for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) {
- /* skip strokes that are invalid for current view */
- if (ED_gpencil_stroke_can_use(C, gps) == false) {
+ bool is_stroke_selected = GPENCIL_STROKE_TYPE_BEZIER(gps) ?
+ (bool)(gps->editcurve->flag & GP_CURVE_SELECT) :
+ (bool)(gps->flag & GP_STROKE_SELECT);
+
+ if (!is_stroke_selected) {
continue;
}
- bool curve_select = false;
- if (is_curve_edit && gps->editcurve != NULL) {
- curve_select = gps->editcurve->flag & GP_CURVE_SELECT;
+ /* update frame to get the new location of objects */
+ if ((mode == GP_REPROJECT_SURFACE) && (cfra_prv != gpf->framenum)) {
+ cfra_prv = gpf->framenum;
+ CFRA = gpf->framenum;
+ BKE_scene_graph_update_for_newframe(depsgraph);
}
- if (gps->flag & GP_STROKE_SELECT || curve_select) {
-
- /* update frame to get the new location of objects */
- if ((mode == GP_REPROJECT_SURFACE) && (cfra_prv != gpf->framenum)) {
- cfra_prv = gpf->framenum;
- CFRA = gpf->framenum;
- BKE_scene_graph_update_for_newframe(depsgraph);
- }
-
- ED_gpencil_stroke_reproject(depsgraph, &gsc, sctx, gpl, gpf, gps, mode, keep_original);
+ ED_gpencil_stroke_reproject(depsgraph, &gsc, sctx, gpl, gpf, gps, mode, keep_original);
- 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(gpd, gps, gps->editcurve);
+ /* TODO: Reproject curve data and regenerate stroke.
+ * Right now we are using the projected points to regenerate the curve. This will most
+ * likely change the handles which is usually not wanted.*/
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
- gps->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
- BKE_gpencil_stroke_geometry_update(gpd, gps);
- }
-
- changed = true;
- }
+ changed = true;
}
}
/* If not multi-edit, exit loop. */
@@ -3909,7 +4066,8 @@ static int gpencil_recalc_geometry_exec(bContext *C, wmOperator *UNUSED(op))
LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
- BKE_gpencil_stroke_geometry_update(gpd, gps);
+ /* TODO: maybe add an option to only include selected strokes? */
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
}
}
}
@@ -3957,7 +4115,7 @@ static void gpencil_smooth_stroke(bContext *C, wmOperator *op)
}
GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) {
- if (gps->flag & GP_STROKE_SELECT) {
+ if (gps->flag & GP_STROKE_SELECT && GPENCIL_STROKE_TYPE_POLY(gps)) {
for (int r = 0; r < repeat; r++) {
for (int i = 0; i < gps->totpoints; i++) {
bGPDspoint *pt = &gps->points[i];
@@ -3984,6 +4142,22 @@ static void gpencil_smooth_stroke(bContext *C, wmOperator *op)
}
}
}
+ else if (GPENCIL_STROKE_TYPE_BEZIER(gps)) {
+ bGPDcurve *gpc = gps->editcurve;
+ if (gpc->flag & GP_CURVE_SELECT) {
+ BKE_gpencil_editcurve_smooth(gps,
+ factor,
+ 2,
+ repeat,
+ only_selected,
+ false,
+ smooth_position,
+ smooth_thickness,
+ smooth_strength);
+ BKE_gpencil_editcurve_recalculate_handles(gps);
+ BKE_gpencil_stroke_geometry_update(gpd_, gps, GP_GEO_UPDATE_DEFAULT);
+ }
+ }
}
GP_EDITABLE_STROKES_END(gpstroke_iter);
}
@@ -4164,38 +4338,33 @@ static int gpencil_stroke_subdivide_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
- const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
-
bool changed = false;
- if (is_curve_edit) {
- GP_EDITABLE_CURVES_BEGIN(gps_iter, C, gpl, gps, gpc)
- {
+
+ /* Go through each editable + selected stroke */
+ GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) {
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps)) {
+ bGPDcurve *gpc = gps->editcurve;
if (gpc->flag & GP_CURVE_SELECT) {
BKE_gpencil_editcurve_subdivide(gps, cuts);
BKE_gpencil_editcurve_recalculate_handles(gps);
- gps->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
- BKE_gpencil_stroke_geometry_update(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
changed = true;
}
}
- GP_EDITABLE_CURVES_END(gps_iter);
- }
- else {
- /* Go through each editable + selected stroke */
- GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) {
+ else {
if (gps->flag & GP_STROKE_SELECT) {
gpencil_stroke_subdivide(gps, cuts);
/* Calc geometry data. */
- BKE_gpencil_stroke_geometry_update(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
changed = true;
}
}
- GP_EDITABLE_STROKES_END(gpstroke_iter);
+ }
+ GP_EDITABLE_STROKES_END(gpstroke_iter);
- if (changed) {
- /* smooth stroke */
- gpencil_smooth_stroke(C, op);
- }
+ if (changed) {
+ /* smooth stroke */
+ gpencil_smooth_stroke(C, op);
}
if (changed) {
@@ -4207,22 +4376,6 @@ static int gpencil_stroke_subdivide_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
-static bool gpencil_subdivide_curve_edit_poll_property(const bContext *C,
- wmOperator *UNUSED(op),
- const PropertyRNA *prop)
-{
- bGPdata *gpd = ED_gpencil_data_get_active(C);
- if (gpd != NULL && GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd)) {
- const char *prop_id = RNA_property_identifier(prop);
- /* Only show number_cuts in curve edit mode */
- if (!STREQ(prop_id, "number_cuts")) {
- return false;
- }
- }
-
- return true;
-}
-
void GPENCIL_OT_stroke_subdivide(wmOperatorType *ot)
{
PropertyRNA *prop;
@@ -4238,7 +4391,6 @@ void GPENCIL_OT_stroke_subdivide(wmOperatorType *ot)
/* api callbacks */
ot->exec = gpencil_stroke_subdivide_exec;
ot->poll = gpencil_active_layer_poll;
- ot->poll_property = gpencil_subdivide_curve_edit_poll_property;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -4274,23 +4426,24 @@ static int gpencil_stroke_simplify_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
- const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
-
bool changed = false;
- if (is_curve_edit) {
- BKE_report(op->reports, RPT_ERROR, "Not implemented!");
- }
- else {
- /* Go through each editable + selected stroke */
- GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) {
- if (gps->flag & GP_STROKE_SELECT) {
- /* simplify stroke using Ramer-Douglas-Peucker algorithm */
- BKE_gpencil_stroke_simplify_adaptive(gpd, gps, factor);
+ /* Go through each editable + selected stroke */
+ GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) {
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps)) {
+ if (gps->editcurve->flag & GP_CURVE_SELECT) {
+ BKE_gpencil_editcurve_simplify_adaptive(gps, factor);
+ BKE_gpencil_editcurve_recalculate_handles(gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
changed = true;
}
}
- GP_EDITABLE_STROKES_END(gpstroke_iter);
+ else if (gps->flag & GP_STROKE_SELECT) {
+ /* simplify stroke using Ramer-Douglas-Peucker algorithm */
+ BKE_gpencil_stroke_simplify_adaptive(gpd, gps, factor);
+ changed = true;
+ }
}
+ GP_EDITABLE_STROKES_END(gpstroke_iter);
if (changed) {
/* notifiers */
@@ -4334,24 +4487,26 @@ static int gpencil_stroke_simplify_fixed_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
- const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
-
bool changed = false;
- if (is_curve_edit) {
- BKE_report(op->reports, RPT_ERROR, "Not implemented!");
- }
- else {
- /* Go through each editable + selected stroke */
- GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) {
- if (gps->flag & GP_STROKE_SELECT) {
- changed |= true;
- for (int i = 0; i < steps; i++) {
- BKE_gpencil_stroke_simplify_fixed(gpd, gps);
- }
+ /* Go through each editable + selected stroke */
+ GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) {
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps)) {
+ bGPDcurve *gpc = gps->editcurve;
+ if (gpc->flag & GP_CURVE_SELECT) {
+ BKE_gpencil_editcurve_simplify_fixed(gps, steps);
+ BKE_gpencil_editcurve_recalculate_handles(gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
+ changed = true;
}
}
- GP_EDITABLE_STROKES_END(gpstroke_iter);
+ else if (gps->flag & GP_STROKE_SELECT) {
+ for (int i = 0; i < steps; i++) {
+ BKE_gpencil_stroke_simplify_fixed(gpd, gps);
+ }
+ changed = true;
+ }
}
+ GP_EDITABLE_STROKES_END(gpstroke_iter);
if (changed) {
/* notifiers */
@@ -4538,14 +4693,13 @@ static int gpencil_stroke_separate_exec(bContext *C, wmOperator *op)
bGPdata *gpd_dst = NULL;
bGPDlayer *gpl_dst = NULL;
bGPDframe *gpf_dst = NULL;
- bGPDspoint *pt;
Material *ma = NULL;
- int i, idx;
+ int idx;
eGP_SeparateModes mode = RNA_enum_get(op->ptr, "mode");
+ const bool keep_ends = RNA_boolean_get(op->ptr, "keep_ends");
const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd_src);
- const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd_src);
/* sanity checks */
if (ELEM(NULL, gpd_src)) {
@@ -4604,6 +4758,13 @@ static int gpencil_stroke_separate_exec(bContext *C, wmOperator *op)
gpf_dst = NULL;
LISTBASE_FOREACH_MUTABLE (bGPDstroke *, gps, &gpf->strokes) {
+ bool is_stroke_selected = GPENCIL_STROKE_TYPE_BEZIER(gps) ?
+ (bool)(gps->editcurve->flag & GP_CURVE_SELECT) :
+ (bool)(gps->flag & GP_STROKE_SELECT);
+
+ if (!is_stroke_selected) {
+ continue;
+ }
/* skip strokes that are invalid for current view */
if (ED_gpencil_stroke_can_use(C, gps) == false) {
@@ -4613,70 +4774,202 @@ static int gpencil_stroke_separate_exec(bContext *C, wmOperator *op)
if (ED_gpencil_stroke_material_editable(ob, gpl, gps) == false) {
continue;
}
+
/* Separate selected strokes. */
- if (gps->flag & GP_STROKE_SELECT) {
- /* add layer if not created before */
- if (gpl_dst == NULL) {
- gpl_dst = BKE_gpencil_layer_addnew(gpd_dst, gpl->info, false, false);
- BKE_gpencil_layer_copy_settings(gpl, gpl_dst);
- /* Copy masks. */
- BKE_gpencil_layer_mask_copy(gpl, gpl_dst);
- }
+ if (gpl_dst == NULL) {
+ gpl_dst = BKE_gpencil_layer_addnew(gpd_dst, gpl->info, false, false);
+ BKE_gpencil_layer_copy_settings(gpl, gpl_dst);
+ /* Copy masks. */
+ BKE_gpencil_layer_mask_copy(gpl, gpl_dst);
+ }
- /* add frame if not created before */
- if (gpf_dst == NULL) {
- gpf_dst = BKE_gpencil_layer_frame_get(gpl_dst, gpf->framenum, GP_GETFRAME_ADD_NEW);
- }
+ /* add frame if not created before */
+ if (gpf_dst == NULL) {
+ gpf_dst = BKE_gpencil_layer_frame_get(gpl_dst, gpf->framenum, GP_GETFRAME_ADD_NEW);
+ }
- /* add duplicate materials */
+ /* add duplicate materials */
- /* XXX same material can be in multiple slots. */
- ma = BKE_gpencil_material(ob, gps->mat_nr + 1);
+ /* XXX same material can be in multiple slots. */
+ ma = BKE_gpencil_material(ob, gps->mat_nr + 1);
- idx = BKE_gpencil_object_material_ensure(bmain, ob_dst, ma);
+ idx = BKE_gpencil_object_material_ensure(bmain, ob_dst, ma);
- /* selected points mode */
- if (mode == GP_SEPARATE_POINT) {
- if (is_curve_edit) {
- BKE_report(op->reports, RPT_ERROR, "Not implemented!");
+ /* selected points mode */
+ if (mode == GP_SEPARATE_POINT) {
+ /* make copy of source stroke */
+ bGPDstroke *gps_dst = BKE_gpencil_stroke_duplicate(gps, true, true);
+
+ /* Reassign material. */
+ gps_dst->mat_nr = idx;
+
+ /* link to destination frame */
+ BLI_addtail(&gpf_dst->strokes, gps_dst);
+
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps)) {
+ bGPDcurve *gpc_src = gps->editcurve;
+ bGPDcurve *gpc_dst = gps_dst->editcurve;
+
+ /* Flip the selection */
+ for (int i = 0; i < gpc_dst->tot_curve_points; i++) {
+ bGPDcurve_point *cpt = &gpc_dst->curve_points[i];
+ BezTriple *bezt = &cpt->bezt;
+ if (cpt->flag & GP_CURVE_POINT_SELECT) {
+ cpt->flag &= ~GP_CURVE_POINT_SELECT;
+ BEZT_DESEL_ALL(bezt);
+ }
+ else {
+ cpt->flag |= GP_CURVE_POINT_SELECT;
+ BEZT_SEL_ALL(bezt);
+ }
+ }
+
+ if (keep_ends) {
+ /* Shrink the selection in the original stroke to keep the connecting points. */
+ int tot_selected = 0, num_deselected = 0;
+
+ bool prev_sel = false;
+ int i;
+ for (i = 0; i < gpc_src->tot_curve_points; i++) {
+ bGPDcurve_point *gpc_pt = &gpc_src->curve_points[i];
+ BezTriple *bezt = &gpc_pt->bezt;
+ if (gpc_pt->flag & GP_CURVE_POINT_SELECT) {
+ /* shrink if previous wasn't selected */
+ if (prev_sel == false) {
+ gpc_pt->flag &= ~GP_CURVE_POINT_SELECT;
+ BEZT_DESEL_ALL(bezt);
+ num_deselected++;
+ }
+ prev_sel = true;
+ tot_selected++;
+ }
+ else {
+ /* mark previous as being unselected - and hence, is trigger for shrinking */
+ prev_sel = false;
+ }
+ }
+
+ /* Second Pass: Go in reverse order, doing the same as before (except in opposite
+ * order)
+ * - This pass covers the "before" edges of selection islands
+ */
+ prev_sel = false;
+ for (i = gpc_src->tot_curve_points - 1; i > 0; i--) {
+ bGPDcurve_point *gpc_pt = &gpc_src->curve_points[i];
+ BezTriple *bezt = &gpc_pt->bezt;
+ if (gpc_pt->flag & GP_CURVE_POINT_SELECT) {
+ /* shrink if previous wasn't selected */
+ if (prev_sel == false) {
+ gpc_pt->flag &= ~GP_CURVE_POINT_SELECT;
+ BEZT_DESEL_ALL(bezt);
+ num_deselected++;
+ }
+ prev_sel = true;
+ }
+ else {
+ /* mark previous as being unselected - and hence, is trigger for shrinking */
+ prev_sel = false;
+ }
+ }
+
+ /* Deselect curve if all points are deselected. */
+ if (tot_selected - num_deselected == 0) {
+ gpc_src->flag &= ~GP_CURVE_SELECT;
+ }
}
- else {
- /* make copy of source stroke */
- bGPDstroke *gps_dst = BKE_gpencil_stroke_duplicate(gps, true, true);
- /* Reassign material. */
- gps_dst->mat_nr = idx;
+ BKE_gpencil_curve_delete_tagged_points(
+ gpd_dst, gpf_dst, gps_dst, NULL, gpc_dst, GP_CURVE_POINT_SELECT);
+
+ BKE_gpencil_curve_delete_tagged_points(
+ gpd_src, gpf, gps, gps->next, gpc_src, GP_CURVE_POINT_SELECT);
+ }
+ else {
- /* link to destination frame */
- BLI_addtail(&gpf_dst->strokes, gps_dst);
+ /* Invert selection status of all points in destination stroke */
+ for (int i = 0; i < gps_dst->totpoints; i++) {
+ bGPDspoint *pt = &gps_dst->points[i];
+ pt->flag ^= GP_SPOINT_SELECT;
+ }
- /* Invert selection status of all points in destination stroke */
- for (i = 0, pt = gps_dst->points; i < gps_dst->totpoints; i++, pt++) {
- pt->flag ^= GP_SPOINT_SELECT;
+ if (keep_ends) {
+ bGPDspoint *pt;
+ int i, tot_selected = 0, num_deselected = 0;
+ bool prev_sel;
+
+ /* First Pass: Go in forward order, shrinking selection
+ * if previous was not selected (pre changes).
+ * - This pass covers the "after" edges of selection islands
+ */
+ prev_sel = false;
+ for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
+ if (pt->flag & GP_SPOINT_SELECT) {
+ /* shrink if previous wasn't selected */
+ if (prev_sel == false) {
+ pt->flag &= ~GP_SPOINT_SELECT;
+ num_deselected++;
+ }
+ prev_sel = true;
+ tot_selected++;
+ }
+ else {
+ /* mark previous as being unselected - and hence, is trigger for shrinking */
+ prev_sel = false;
+ }
}
- /* delete selected points from destination stroke */
- BKE_gpencil_stroke_delete_tagged_points(
- gpd_dst, gpf_dst, gps_dst, NULL, GP_SPOINT_SELECT, false, 0);
+ /* Second Pass: Go in reverse order, doing the same as before (except in opposite
+ * order)
+ * - This pass covers the "before" edges of selection islands
+ */
+ prev_sel = false;
+ for (pt -= 1; i > 0; i--, pt--) {
+ if (pt->flag & GP_SPOINT_SELECT) {
+ /* shrink if previous wasn't selected */
+ if (prev_sel == false) {
+ pt->flag &= ~GP_SPOINT_SELECT;
+ num_deselected++;
+ }
+ prev_sel = true;
+ }
+ else {
+ /* mark previous as being unselected - and hence, is trigger for shrinking */
+ prev_sel = false;
+ }
+ }
- /* delete selected points from origin stroke */
- BKE_gpencil_stroke_delete_tagged_points(
- gpd_src, gpf, gps, gps->next, GP_SPOINT_SELECT, false, 0);
+ /* Deselect stroke if all points are deselected. */
+ if (tot_selected - num_deselected == 0) {
+ gps->flag &= ~GP_STROKE_SELECT;
+ }
}
+
+ /* delete selected points from destination stroke */
+ BKE_gpencil_stroke_delete_tagged_points(
+ gpd_dst, gpf_dst, gps_dst, NULL, GP_SPOINT_SELECT, false, 0);
+
+ /* delete selected points from origin stroke */
+ BKE_gpencil_stroke_delete_tagged_points(
+ gpd_src, gpf, gps, gps->next, GP_SPOINT_SELECT, false, 0);
+ }
+ }
+ /* selected strokes mode */
+ else if (mode == GP_SEPARATE_STROKE) {
+ /* deselect old stroke */
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps)) {
+ gps->editcurve->flag &= ~GP_CURVE_SELECT;
}
- /* selected strokes mode */
- else if (mode == GP_SEPARATE_STROKE) {
- /* deselect old stroke */
+ else {
gps->flag &= ~GP_STROKE_SELECT;
- BKE_gpencil_stroke_select_index_reset(gps);
- /* unlink from source frame */
- BLI_remlink(&gpf->strokes, gps);
- gps->prev = gps->next = NULL;
- /* relink to destination frame */
- BLI_addtail(&gpf_dst->strokes, gps);
- /* Reassign material. */
- gps->mat_nr = idx;
}
+ BKE_gpencil_stroke_select_index_reset(gps);
+ /* unlink from source frame */
+ BLI_remlink(&gpf->strokes, gps);
+ gps->prev = gps->next = NULL;
+ /* relink to destination frame */
+ BLI_addtail(&gpf_dst->strokes, gps);
+ /* Reassign material. */
+ gps->mat_nr = idx;
}
}
}
@@ -4754,6 +5047,24 @@ static int gpencil_stroke_separate_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
+static bool gpencil_stroke_separate_poll_property(const bContext *UNUSED(C),
+ wmOperator *op,
+ const PropertyRNA *prop)
+{
+ const char *prop_id = RNA_property_identifier(prop);
+
+ /* Only show connect keep_ends in GP_SEPARATE_POINT mode. */
+ if (STREQ(prop_id, "keep_ends")) {
+ const int type = RNA_enum_get(op->ptr, "mode");
+ if (type == GP_SEPARATE_POINT) {
+ return true;
+ }
+ return false;
+ }
+
+ return true;
+}
+
void GPENCIL_OT_stroke_separate(wmOperatorType *ot)
{
static const EnumPropertyItem separate_type[] = {
@@ -4772,12 +5083,19 @@ void GPENCIL_OT_stroke_separate(wmOperatorType *ot)
ot->invoke = WM_menu_invoke;
ot->exec = gpencil_stroke_separate_exec;
ot->poll = gpencil_strokes_edit3d_poll;
+ ot->poll_property = gpencil_stroke_separate_poll_property;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
ot->prop = RNA_def_enum(ot->srna, "mode", separate_type, GP_SEPARATE_POINT, "Mode", "");
+
+ RNA_def_boolean(ot->srna,
+ "keep_ends",
+ false,
+ "Keep Ends",
+ "Seperate the selected points, but keep the ends in both");
}
/** \} */
@@ -4786,19 +5104,17 @@ void GPENCIL_OT_stroke_separate(wmOperatorType *ot)
/** \name Stroke Split Operator
* \{ */
-static int gpencil_stroke_split_exec(bContext *C, wmOperator *op)
+static int gpencil_stroke_split_exec(bContext *C, wmOperator *UNUSED(op))
{
Object *ob = CTX_data_active_object(C);
bGPdata *gpd = ED_gpencil_data_get_active(C);
- bGPDspoint *pt;
- int i;
/* sanity checks */
if (ELEM(NULL, gpd)) {
return OPERATOR_CANCELLED;
}
const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
- const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
+ bool changed = false;
/* loop strokes and split parts */
CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
@@ -4811,8 +5127,9 @@ static int gpencil_stroke_split_exec(bContext *C, wmOperator *op)
continue;
}
- LISTBASE_FOREACH_MUTABLE (bGPDstroke *, gps, &gpf->strokes) {
-
+ /* We are deleting the strokes we are iterating over and adding new strokes to the end of
+ * the frame. So we need to use backwards mutable iteration here. */
+ LISTBASE_FOREACH_BACKWARD_MUTABLE (bGPDstroke *, gps, &gpf->strokes) {
/* skip strokes that are invalid for current view */
if (ED_gpencil_stroke_can_use(C, gps) == false) {
continue;
@@ -4821,42 +5138,66 @@ static int gpencil_stroke_split_exec(bContext *C, wmOperator *op)
if (ED_gpencil_stroke_material_editable(ob, gpl, gps) == false) {
continue;
}
+
/* Split selected strokes. */
- if (gps->flag & GP_STROKE_SELECT) {
- if (is_curve_edit) {
- BKE_report(op->reports, RPT_ERROR, "Not implemented!");
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps)) {
+ bGPDcurve *gpc = gps->editcurve;
+ if ((gpc->flag & GP_CURVE_SELECT) == 0) {
+ continue;
}
- else {
- /* make copy of source stroke */
- bGPDstroke *gps_dst = BKE_gpencil_stroke_duplicate(gps, true, true);
- /* link to same frame */
- BLI_addtail(&gpf->strokes, gps_dst);
+ bGPDstroke *gps_dst = BKE_gpencil_stroke_duplicate(gps, true, true);
+ BLI_addtail(&gpf->strokes, gps_dst);
- /* invert selection status of all points in destination stroke */
- for (i = 0, pt = gps_dst->points; i < gps_dst->totpoints; i++, pt++) {
- pt->flag ^= GP_SPOINT_SELECT;
+ bGPDcurve *gpc_dst = gps_dst->editcurve;
+ for (int i = 0; i < gpc_dst->tot_curve_points; i++) {
+ bGPDcurve_point *cpt = &gpc_dst->curve_points[i];
+ if (cpt->flag & GP_CURVE_POINT_SELECT) {
+ cpt->flag &= ~GP_CURVE_POINT_TAG;
+ }
+ else {
+ cpt->flag |= GP_CURVE_POINT_TAG;
}
+ }
- /* delete selected points from destination stroke */
- BKE_gpencil_stroke_delete_tagged_points(
- gpd, gpf, gps_dst, NULL, GP_SPOINT_SELECT, true, 0);
+ BKE_gpencil_curve_delete_tagged_points(
+ gpd, gpf, gps_dst, NULL, gpc_dst, GP_CURVE_POINT_TAG);
- /* delete selected points from origin stroke */
- BKE_gpencil_stroke_delete_tagged_points(
- gpd, gpf, gps, gps->next, GP_SPOINT_SELECT, false, 0);
- }
+ BKE_gpencil_curve_delete_tagged_points(
+ gpd, gpf, gps, gps->next, gpc, GP_CURVE_POINT_SELECT);
}
- }
- /* select again tagged points */
- LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
- bGPDspoint *ptn = gps->points;
- for (int i2 = 0; i2 < gps->totpoints; i2++, ptn++) {
- if (ptn->flag & GP_SPOINT_TAG) {
- ptn->flag |= GP_SPOINT_SELECT;
- ptn->flag &= ~GP_SPOINT_TAG;
+ else {
+ if ((gps->flag & GP_STROKE_SELECT) == 0) {
+ continue;
}
+
+ /* Make copy of source stroke. */
+ bGPDstroke *gps_dst = BKE_gpencil_stroke_duplicate(gps, true, false);
+
+ /* Link to same frame. */
+ BLI_addtail(&gpf->strokes, gps_dst);
+
+ /* Tag the unselected points. */
+ for (int i = 0; i < gps_dst->totpoints; i++) {
+ bGPDspoint *pt = &gps_dst->points[i];
+ if (pt->flag & GP_SPOINT_SELECT) {
+ pt->flag &= ~GP_SPOINT_TAG;
+ }
+ else {
+ pt->flag |= GP_SPOINT_TAG;
+ }
+ }
+
+ /* Delete tagged points from destination stroke. */
+ BKE_gpencil_stroke_delete_tagged_points(
+ gpd, gpf, gps_dst, NULL, GP_SPOINT_TAG, false, 0);
+
+ /* Delete selected points from origin stroke. */
+ BKE_gpencil_stroke_delete_tagged_points(
+ gpd, gpf, gps, gps->next, GP_SPOINT_SELECT, false, 0);
}
+
+ changed = true;
}
}
@@ -4868,9 +5209,10 @@ static int gpencil_stroke_split_exec(bContext *C, wmOperator *op)
}
CTX_DATA_END;
- DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
-
- WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+ if (changed) {
+ DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+ }
return OPERATOR_FINISHED;
}
@@ -4934,7 +5276,7 @@ void GPENCIL_OT_stroke_smooth(wmOperatorType *ot)
prop = RNA_def_int(ot->srna, "repeat", 1, 1, 50, "Repeat", "", 1, 20);
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
- RNA_def_float(ot->srna, "factor", 0.5f, 0.0f, 2.0f, "Factor", "", 0.0f, 2.0f);
+ RNA_def_float(ot->srna, "factor", 0.5f, 0.0f, 2.0f, "Factor", "", 0.0f, 1.0f);
RNA_def_boolean(ot->srna,
"only_selected",
true,
@@ -5305,24 +5647,31 @@ static int gpencil_merge_by_distance_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
- const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
-
- if (is_curve_edit) {
- /* TODO: merge curve points by distance */
- }
- else {
- /* Go through each editable selected stroke */
- GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) {
- if (gps->flag & GP_STROKE_SELECT) {
- BKE_gpencil_stroke_merge_distance(gpd, gpf_, gps, threshold, unselected);
+ bool changed = false;
+ /* Go through each editable selected stroke */
+ GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) {
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps)) {
+ bGPDcurve *gpc = gps->editcurve;
+ if (gpc->flag & GP_CURVE_SELECT) {
+ /* TODO: don't hardcode the refit and threshold. Figure out how to set these. */
+ if (BKE_gpencil_editcurve_merge_distance(gps, threshold, unselected, false, 0.0f)) {
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
+ changed = true;
+ }
}
}
- GP_EDITABLE_STROKES_END(gpstroke_iter);
+ else if (gps->flag & GP_STROKE_SELECT) {
+ BKE_gpencil_stroke_merge_distance(gpd, gpf_, gps, threshold, unselected);
+ changed = true;
+ }
}
+ GP_EDITABLE_STROKES_END(gpstroke_iter);
- /* notifiers */
- DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
- WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+ if (changed) {
+ /* notifiers */
+ DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+ }
return OPERATOR_FINISHED;
}
@@ -5474,8 +5823,7 @@ static int gpencil_stroke_normalize_exec(bContext *C, wmOperator *op)
}
}
- gps->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
- BKE_gpencil_stroke_geometry_update(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
}
}
/* If not multi-edit, exit loop. */
diff --git a/source/blender/editors/gpencil/gpencil_edit_curve.c b/source/blender/editors/gpencil/gpencil_edit_curve.c
index e766a410889..925375d7103 100644
--- a/source/blender/editors/gpencil/gpencil_edit_curve.c
+++ b/source/blender/editors/gpencil/gpencil_edit_curve.c
@@ -35,6 +35,7 @@
#include "BKE_gpencil.h"
#include "BKE_gpencil_curve.h"
#include "BKE_gpencil_geom.h"
+#include "BKE_report.h"
#include "BLI_listbase.h"
#include "BLI_math.h"
@@ -49,91 +50,14 @@
#include "DEG_depsgraph.h"
-#include "gpencil_intern.h"
-
-/* Poll callback for checking if there is an active layer and we are in curve edit mode. */
-static bool gpencil_curve_edit_mode_poll(bContext *C)
-{
- Object *ob = CTX_data_active_object(C);
- if ((ob == NULL) || (ob->type != OB_GPENCIL)) {
- return false;
- }
- bGPdata *gpd = (bGPdata *)ob->data;
- if (!GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd)) {
- return false;
- }
-
- bGPDlayer *gpl = BKE_gpencil_layer_active_get(gpd);
- return (gpl != NULL);
-}
-
-static int gpencil_stroke_enter_editcurve_mode_exec(bContext *C, wmOperator *op)
-{
- Object *ob = CTX_data_active_object(C);
- bGPdata *gpd = ob->data;
-
- float error_threshold = RNA_float_get(op->ptr, "error_threshold");
- gpd->curve_edit_threshold = error_threshold;
+#include "UI_interface.h"
+#include "UI_resources.h"
- if (ELEM(NULL, gpd)) {
- return OPERATOR_CANCELLED;
- }
-
- LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
- LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
- if (gpf == gpl->actframe) {
- LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
- /* only allow selected and non-converted strokes to be transformed */
- if ((gps->flag & GP_STROKE_SELECT && gps->editcurve == NULL) ||
- (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(gpd, gps, gps->editcurve);
- gps->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
- BKE_gpencil_stroke_geometry_update(gpd, gps);
- }
- }
- }
- }
- }
-
- gpd->flag |= GP_DATA_CURVE_EDIT_MODE;
-
- /* notifiers */
- DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
- WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
-
- return OPERATOR_FINISHED;
-}
-
-void GPENCIL_OT_stroke_enter_editcurve_mode(wmOperatorType *ot)
-{
- PropertyRNA *prop;
-
- /* identifiers */
- ot->name = "Enter curve edit mode";
- ot->idname = "GPENCIL_OT_stroke_enter_editcurve_mode";
- ot->description = "Called to transform a stroke into a curve";
-
- /* api callbacks */
- ot->exec = gpencil_stroke_enter_editcurve_mode_exec;
- ot->poll = gpencil_active_layer_poll;
-
- /* flags */
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+#include "gpencil_intern.h"
- /* properties */
- prop = RNA_def_float(ot->srna,
- "error_threshold",
- 0.1f,
- FLT_MIN,
- 100.0f,
- "Error Threshold",
- "Threshold on the maximum deviation from the actual stroke",
- FLT_MIN,
- 10.0f);
- RNA_def_property_ui_range(prop, FLT_MIN, 10.0f, 0.1f, 5);
-}
+/* -------------------------------------------------------------------- */
+/** \name Set handle type operator
+ * \{ */
static int gpencil_editcurve_set_handle_type_exec(bContext *C, wmOperator *op)
{
@@ -169,8 +93,7 @@ static int gpencil_editcurve_set_handle_type_exec(bContext *C, wmOperator *op)
}
BKE_gpencil_editcurve_recalculate_handles(gps);
- gps->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
- BKE_gpencil_stroke_geometry_update(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
}
GP_EDITABLE_CURVES_END(gps_iter);
@@ -199,7 +122,7 @@ void GPENCIL_OT_stroke_editcurve_set_handle_type(wmOperatorType *ot)
/* api callbacks */
ot->invoke = WM_menu_invoke;
ot->exec = gpencil_editcurve_set_handle_type_exec;
- ot->poll = gpencil_curve_edit_mode_poll;
+ ot->poll = gpencil_active_layer_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -207,5 +130,151 @@ void GPENCIL_OT_stroke_editcurve_set_handle_type(wmOperatorType *ot)
/* properties */
ot->prop = RNA_def_enum(ot->srna, "type", editcurve_handle_type_items, 1, "Type", "Spline type");
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Set stroke type operator
+ * \{ */
+
+static int gpencil_stroke_set_type_exec(bContext *C, wmOperator *op)
+{
+ Object *ob = CTX_data_active_object(C);
+ bGPdata *gpd = ob->data;
+ eGPStrokeType type = RNA_enum_get(op->ptr, "type");
+ const bool only_selected = RNA_boolean_get(op->ptr, "only_selected");
+
+ if (ELEM(NULL, gpd)) {
+ return OPERATOR_CANCELLED;
+ }
+
+ bool changed = false;
+ switch (type) {
+ case STROKE_POLY: {
+ GP_EDITABLE_CURVES_BEGIN(gps_iter, C, gpl, gps, gpc)
+ {
+ if (only_selected && (gpc->flag & GP_CURVE_SELECT) == 0) {
+ continue;
+ }
+ BKE_gpencil_stroke_editcurve_sync_selection(gpd, gps, gpc);
+ BKE_gpencil_free_stroke_editcurve(gps);
+ changed = true;
+ }
+ GP_EDITABLE_CURVES_END(gps_iter);
+ } break;
+
+ case STROKE_BEZIER: {
+ const float threshold = RNA_float_get(op->ptr, "threshold");
+ const float corner_angle = RNA_float_get(op->ptr, "corner_angle");
+
+ GP_EDITABLE_STROKES_BEGIN (gps_iter, C, gpl, gps) {
+ if (!GPENCIL_STROKE_TYPE_BEZIER(gps)) {
+ if (only_selected && (gps->flag & GP_STROKE_SELECT) == 0) {
+ continue;
+ }
+
+ BKE_gpencil_stroke_refit_curve(gps, threshold, corner_angle, GP_GEO_UPDATE_DEFAULT);
+ if (gps->editcurve != NULL) {
+ bGPDcurve *gpc = gps->editcurve;
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_POLYLINE_REGENERATE_ALL);
+
+ /* Select all curve points. */
+ for (uint32_t i = 0; i < gpc->tot_curve_points; i++) {
+ bGPDcurve_point *pt = &gpc->curve_points[i];
+ pt->flag |= GP_CURVE_POINT_SELECT;
+ BEZT_SEL_ALL(&pt->bezt);
+ }
+ gpc->flag |= GP_CURVE_SELECT;
+
+ /* Deselect stroke points. */
+ for (uint32_t i = 0; i < gps->totpoints; i++) {
+ bGPDspoint *pt = &gps->points[i];
+ pt->flag &= ~GP_SPOINT_SELECT;
+ }
+ gps->flag &= ~GP_STROKE_SELECT;
+ changed = true;
+ }
+ }
+ }
+ GP_EDITABLE_STROKES_END(gps_iter);
+ } break;
+ default: {
+ BKE_report(op->reports, RPT_ERROR, "Unknown stroke type");
+ return OPERATOR_CANCELLED;
+ }
+ }
+
+ if (changed) {
+ /* notifiers */
+ DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+ }
+
+ return OPERATOR_FINISHED;
+}
+
+static void gpencil_stroke_set_type_ui(bContext *UNUSED(C), wmOperator *op)
+{
+ uiLayout *layout = op->layout;
+ PointerRNA ptr;
+ uiLayoutSetPropSep(layout, true);
+ RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr);
+
+ uiItemR(layout, &ptr, "only_selected", 0, NULL, ICON_NONE);
+ eGPStrokeType type = RNA_enum_get(&ptr, "type");
+ if (type == STROKE_BEZIER) {
+ uiItemR(layout, &ptr, "threshold", 0, NULL, ICON_NONE);
+ uiItemR(layout, &ptr, "corner_angle", 0, NULL, ICON_NONE);
+ }
+}
+
+void GPENCIL_OT_stroke_set_type(wmOperatorType *ot)
+{
+ PropertyRNA *prop;
+ static const EnumPropertyItem stroke_types[] = {
+ {STROKE_POLY, "POLY", 0, "Poly", ""},
+ {STROKE_BEZIER, "BEZIER", 0, "Bezier", ""},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ /* identifiers */
+ ot->name = "Set Stroke Type";
+ ot->idname = "GPENCIL_OT_stroke_set_type";
+ ot->description = "Set the type of the stroke";
+
+ /* api callbacks */
+ ot->exec = gpencil_stroke_set_type_exec;
+ ot->poll = gpencil_active_layer_poll;
+ ot->ui = gpencil_stroke_set_type_ui;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ prop = RNA_def_enum(ot->srna, "type", stroke_types, STROKE_POLY, "Type", "Stroke type");
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+
+ prop = RNA_def_boolean(
+ ot->srna, "only_selected", true, "Selected Only", "Only set the type for selected strokes");
+
+ prop = RNA_def_float(ot->srna,
+ "threshold",
+ GP_DEFAULT_CURVE_ERROR,
+ 0.0f,
+ 100.0f,
+ "Threshold",
+ "Bézier curve fitting error threshold",
+ 0.0f,
+ 3.0f);
+
+ prop = RNA_def_float_distance(ot->srna,
+ "corner_angle",
+ GP_DEFAULT_CURVE_EDIT_CORNER_ANGLE,
+ 0.0f,
+ M_PI,
+ "Corner Angle",
+ "Angle threshold to be treated as corners",
+ 0.0f,
+ M_PI);
+ RNA_def_property_subtype(prop, PROP_ANGLE);
+}
/** \} */
diff --git a/source/blender/editors/gpencil/gpencil_fill.c b/source/blender/editors/gpencil/gpencil_fill.c
index 091ff2c16b0..b1c83a2634e 100644
--- a/source/blender/editors/gpencil/gpencil_fill.c
+++ b/source/blender/editors/gpencil/gpencil_fill.c
@@ -1599,7 +1599,7 @@ static void gpencil_stroke_from_buffer(tGPDfill *tgpf)
}
/* Calc geometry data. */
- BKE_gpencil_stroke_geometry_update(tgpf->gpd, gps);
+ BKE_gpencil_stroke_geometry_update(tgpf->gpd, gps, GP_GEO_UPDATE_DEFAULT);
}
/* ----------------------- */
diff --git a/source/blender/editors/gpencil/gpencil_intern.h b/source/blender/editors/gpencil/gpencil_intern.h
index d1a1e417d9e..49ca83c511c 100644
--- a/source/blender/editors/gpencil/gpencil_intern.h
+++ b/source/blender/editors/gpencil/gpencil_intern.h
@@ -227,6 +227,12 @@ typedef struct tGPDprimitive {
} tGPDprimitive;
+/* Stroke types enum definition. */
+typedef enum eGPStrokeType {
+ STROKE_POLY = 0,
+ STROKE_BEZIER = 1,
+} eGPStrokeType;
+
bool gpencil_stroke_inside_circle(const float mval[2], int rad, int x0, int y0, int x1, int y1);
void gpencil_point_conversion_init(struct bContext *C, GP_SpaceConversion *r_gsc);
@@ -397,8 +403,8 @@ void GPENCIL_OT_recalc_geometry(struct wmOperatorType *ot);
/* stroke editcurve */
-void GPENCIL_OT_stroke_enter_editcurve_mode(struct wmOperatorType *ot);
void GPENCIL_OT_stroke_editcurve_set_handle_type(struct wmOperatorType *ot);
+void GPENCIL_OT_stroke_set_type(struct wmOperatorType *ot);
/* stroke sculpting -- */
@@ -519,6 +525,9 @@ void GPENCIL_OT_primitive_polyline(struct wmOperatorType *ot);
void GPENCIL_OT_primitive_circle(struct wmOperatorType *ot);
void GPENCIL_OT_primitive_curve(struct wmOperatorType *ot);
+wmKeyMap *gpencil_curve_draw_modal_keymap(wmKeyConfig *keyconf);
+void GPENCIL_OT_draw_curve(struct wmOperatorType *ot);
+
/* vertex groups ------------ */
void GPENCIL_OT_vertex_group_assign(struct wmOperatorType *ot);
void GPENCIL_OT_vertex_group_remove_from(struct wmOperatorType *ot);
@@ -639,10 +648,10 @@ struct GP_EditableStrokes_Iter {
/* skip strokes that are invalid for current view */ \
if (ED_gpencil_stroke_can_use(C, gps) == false) \
continue; \
- if (gps->editcurve == NULL) \
+ if (!GPENCIL_STROKE_TYPE_BEZIER(gps)) \
continue; \
bGPDcurve *gpc = gps->editcurve; \
- /* ... Do Stuff With Strokes ... */
+ /* ... Do Stuff With Curves ... */
#define GP_EDITABLE_CURVES_END(gpstroke_iter) \
} \
diff --git a/source/blender/editors/gpencil/gpencil_interpolate.c b/source/blender/editors/gpencil/gpencil_interpolate.c
index 0062e363cdf..a5fc76ed041 100644
--- a/source/blender/editors/gpencil/gpencil_interpolate.c
+++ b/source/blender/editors/gpencil/gpencil_interpolate.c
@@ -424,7 +424,7 @@ static void gpencil_interpolate_update_strokes(bContext *C, tGPDinterpolate *tgp
gpencil_interpolate_update_points(gps_from, gps_to, new_stroke, factor);
/* Calc geometry data. */
- BKE_gpencil_stroke_geometry_update(gpd, new_stroke);
+ BKE_gpencil_stroke_geometry_update(gpd, new_stroke, GP_GEO_UPDATE_DEFAULT);
/* Add to strokes. */
BLI_addtail(&tgpil->interFrame->strokes, new_stroke);
@@ -569,7 +569,7 @@ static void gpencil_interpolate_set_points(bContext *C, tGPDinterpolate *tgpi)
gpencil_interpolate_smooth_stroke(new_stroke, tgpi->smooth_factor, tgpi->smooth_steps);
/* Calc geometry data. */
- BKE_gpencil_stroke_geometry_update(gpd, new_stroke);
+ BKE_gpencil_stroke_geometry_update(gpd, new_stroke, GP_GEO_UPDATE_DEFAULT);
/* add to strokes */
BLI_addtail(&tgpil->interFrame->strokes, new_stroke);
}
@@ -844,7 +844,7 @@ static int gpencil_interpolate_modal(bContext *C, wmOperator *op, const wmEvent
gps_dst->flag &= ~GP_STROKE_TAG;
/* Calc geometry data. */
- BKE_gpencil_stroke_geometry_update(tgpi->gpd, gps_dst);
+ BKE_gpencil_stroke_geometry_update(tgpi->gpd, gps_dst, GP_GEO_UPDATE_DEFAULT);
BLI_addtail(&gpf_dst->strokes, gps_dst);
}
@@ -1398,7 +1398,7 @@ static int gpencil_interpolate_seq_exec(bContext *C, wmOperator *op)
gpencil_interpolate_smooth_stroke(new_stroke, smooth_factor, smooth_steps);
/* Calc geometry data. */
- BKE_gpencil_stroke_geometry_update(gpd, new_stroke);
+ BKE_gpencil_stroke_geometry_update(gpd, new_stroke, GP_GEO_UPDATE_DEFAULT);
/* Add strokes to frame. */
bGPDframe *interFrame = BKE_gpencil_layer_frame_get(gpl, cframe, GP_GETFRAME_ADD_NEW);
diff --git a/source/blender/editors/gpencil/gpencil_merge.c b/source/blender/editors/gpencil/gpencil_merge.c
index 259b2882589..ef31ed6c3da 100644
--- a/source/blender/editors/gpencil/gpencil_merge.c
+++ b/source/blender/editors/gpencil/gpencil_merge.c
@@ -523,7 +523,7 @@ static int gpencil_stroke_merge_exec(bContext *C, wmOperator *op)
gpencil_dissolve_points(C);
}
- BKE_gpencil_stroke_geometry_update(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
/* free memory */
MEM_SAFE_FREE(original_array);
diff --git a/source/blender/editors/gpencil/gpencil_ops.c b/source/blender/editors/gpencil/gpencil_ops.c
index 35640cf3b66..08543cce5bd 100644
--- a/source/blender/editors/gpencil/gpencil_ops.c
+++ b/source/blender/editors/gpencil/gpencil_ops.c
@@ -64,13 +64,6 @@ static bool gpencil_stroke_editmode_poll(bContext *C)
return (gpd && (gpd->flag & GP_DATA_STROKE_EDITMODE));
}
-/* Poll callback for stroke curve editing mode */
-static bool gpencil_stroke_editmode_curve_poll(bContext *C)
-{
- bGPdata *gpd = CTX_data_gpencil_data(C);
- return (GPENCIL_EDIT_MODE(gpd) && GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd));
-}
-
/* Poll callback for stroke painting mode */
static bool gpencil_stroke_paintmode_poll(bContext *C)
{
@@ -175,6 +168,13 @@ static bool gpencil_stroke_paintmode_tint_poll(bContext *C)
return gpencil_stroke_paintmode_poll_with_tool(C, GPAINT_TOOL_TINT);
}
+#if 0
+static bool gpencil_stroke_paintmode_curve_poll(bContext *C)
+{
+ return gpencil_stroke_paintmode_poll_with_tool(C, GPAINT_TOOL_CURVE);
+}
+#endif
+
/* Poll callback for stroke sculpting mode */
static bool gpencil_stroke_sculptmode_poll(bContext *C)
{
@@ -317,15 +317,6 @@ static void ed_keymap_gpencil_editing(wmKeyConfig *keyconf)
keymap->poll = gpencil_stroke_editmode_poll;
}
-/* Stroke Curve Editing Keymap - Only when editmode is enabled and in curve edit mode */
-static void ed_keymap_gpencil_curve_editing(wmKeyConfig *keyconf)
-{
- wmKeyMap *keymap = WM_keymap_ensure(keyconf, "Grease Pencil Stroke Curve Edit Mode", 0, 0);
-
- /* set poll callback - so that this keymap only gets enabled when curve editmode is enabled */
- keymap->poll = gpencil_stroke_editmode_curve_poll;
-}
-
/* keys for draw with a drawing brush (no fill) */
static void ed_keymap_gpencil_painting_draw(wmKeyConfig *keyconf)
{
@@ -354,6 +345,15 @@ static void ed_keymap_gpencil_painting_tint(wmKeyConfig *keyconf)
keymap->poll = gpencil_stroke_paintmode_tint_poll;
}
+#if 0
+/* keys for draw with a curve brush */
+static void ed_keymap_gpencil_painting_curve(wmKeyConfig *keyconf)
+{
+ wmKeyMap *keymap = WM_keymap_ensure(keyconf, "Grease Pencil Stroke Paint (Curve)", 0, 0);
+ keymap->poll = gpencil_stroke_paintmode_curve_poll;
+}
+#endif
+
/* Stroke Painting Keymap - Only when paintmode is enabled */
static void ed_keymap_gpencil_painting(wmKeyConfig *keyconf)
{
@@ -482,13 +482,16 @@ static void ed_keymap_gpencil_weightpainting_draw(wmKeyConfig *keyconf)
void ED_keymap_gpencil(wmKeyConfig *keyconf)
{
ed_keymap_gpencil_general(keyconf);
- ed_keymap_gpencil_curve_editing(keyconf);
ed_keymap_gpencil_editing(keyconf);
ed_keymap_gpencil_painting(keyconf);
ed_keymap_gpencil_painting_draw(keyconf);
ed_keymap_gpencil_painting_erase(keyconf);
ed_keymap_gpencil_painting_fill(keyconf);
ed_keymap_gpencil_painting_tint(keyconf);
+#if 0
+ ed_keymap_gpencil_painting_curve(keyconf);
+#endif
+ gpencil_curve_draw_modal_keymap(keyconf);
ed_keymap_gpencil_sculpting(keyconf);
ed_keymap_gpencil_sculptpainting_smooth(keyconf);
ed_keymap_gpencil_sculptpainting_thickness(keyconf);
@@ -580,10 +583,10 @@ void ED_operatortypes_gpencil(void)
WM_operatortype_append(GPENCIL_OT_sculpt_paint);
WM_operatortype_append(GPENCIL_OT_weight_paint);
- /* Edit stroke editcurve */
+ /* Editcurve */
- WM_operatortype_append(GPENCIL_OT_stroke_enter_editcurve_mode);
WM_operatortype_append(GPENCIL_OT_stroke_editcurve_set_handle_type);
+ WM_operatortype_append(GPENCIL_OT_stroke_set_type);
/* Editing (Buttons) ------------ */
@@ -695,6 +698,8 @@ void ED_operatortypes_gpencil(void)
WM_operatortype_append(GPENCIL_OT_primitive_circle);
WM_operatortype_append(GPENCIL_OT_primitive_curve);
+ WM_operatortype_append(GPENCIL_OT_draw_curve);
+
/* convert old 2.7 files to 2.8 */
WM_operatortype_append(GPENCIL_OT_convert_old_files);
diff --git a/source/blender/editors/gpencil/gpencil_paint.c b/source/blender/editors/gpencil/gpencil_paint.c
index 67d4b7726b5..fa867bcd4c2 100644
--- a/source/blender/editors/gpencil/gpencil_paint.c
+++ b/source/blender/editors/gpencil/gpencil_paint.c
@@ -54,6 +54,7 @@
#include "BKE_deform.h"
#include "BKE_global.h"
#include "BKE_gpencil.h"
+#include "BKE_gpencil_curve.h"
#include "BKE_gpencil_geom.h"
#include "BKE_layer.h"
#include "BKE_main.h"
@@ -944,6 +945,7 @@ static void gpencil_stroke_newfrombuffer(tGPsdata *p)
const bool is_depth = (bool)(align_flag & (GP_PROJECT_DEPTH_VIEW | GP_PROJECT_DEPTH_STROKE));
const bool is_lock_axis_view = (bool)(ts->gp_sculpt.lock_axis == 0);
const bool is_camera = is_lock_axis_view && (rv3d->persp == RV3D_CAMOB) && (!is_depth);
+ const bool is_bezier_mode = ts->gpencil_flags & GP_TOOL_FLAG_BEZIER_MODE;
int totelem;
/* For very low pressure at the end, truncate stroke. */
@@ -951,7 +953,7 @@ static void gpencil_stroke_newfrombuffer(tGPsdata *p)
int last_i = gpd->runtime.sbuffer_used - 1;
while (last_i > 0) {
ptc = (tGPspoint *)gpd->runtime.sbuffer + last_i;
- if (ptc->pressure > 0.001f) {
+ if (ptc->pressure > 0.0f) {
break;
}
gpd->runtime.sbuffer_used = last_i - 1;
@@ -1197,8 +1199,21 @@ static void gpencil_stroke_newfrombuffer(tGPsdata *p)
}
}
+ /* Convert to bezier stroke when we are in bezier mode. */
+ if (is_bezier_mode) {
+ /* The refitting algorithm assumes that we have a bounding box calculated. */
+ BKE_gpencil_stroke_boundingbox_calc(gps);
+ BKE_gpencil_stroke_refit_curve(gps,
+ ts->gpencil_curve_fit_threshold,
+ ts->gpencil_curve_fit_corner_angle,
+ GP_GEO_UPDATE_CURVE_REFIT_ALL);
+ }
+
/* subdivide and smooth the stroke */
- if ((brush->gpencil_settings->flag & GP_BRUSH_GROUP_SETTINGS) && (subdivide > 0)) {
+ if ((brush->gpencil_settings->flag & GP_BRUSH_GROUP_SETTINGS) &&
+ (subdivide > 0)
+ /* XXX: For now, don't subdivide in bezier mode. */
+ && !(is_bezier_mode)) {
gpencil_subdivide_stroke(gpd, gps, subdivide);
}
@@ -1207,6 +1222,17 @@ static void gpencil_stroke_newfrombuffer(tGPsdata *p)
* without changing too much the original stroke. */
if ((brush->gpencil_settings->flag & GP_BRUSH_GROUP_SETTINGS) &&
(brush->gpencil_settings->draw_smoothfac > 0.0f)) {
+ if (is_bezier_mode) {
+ BKE_gpencil_editcurve_smooth(gps,
+ brush->gpencil_settings->draw_smoothfac,
+ 2,
+ brush->gpencil_settings->draw_smoothlvl,
+ false,
+ true,
+ true,
+ false,
+ true);
+ }
float reduce = 0.0f;
for (int r = 0; r < brush->gpencil_settings->draw_smoothlvl; r++) {
for (i = 0; i < gps->totpoints - 1; i++) {
@@ -1230,15 +1256,24 @@ static void gpencil_stroke_newfrombuffer(tGPsdata *p)
/* Simplify adaptive */
if ((brush->gpencil_settings->flag & GP_BRUSH_GROUP_SETTINGS) &&
(brush->gpencil_settings->simplify_f > 0.0f)) {
- BKE_gpencil_stroke_simplify_adaptive(gpd, gps, brush->gpencil_settings->simplify_f);
+ if (is_bezier_mode) {
+ BKE_gpencil_editcurve_simplify_adaptive(gps, brush->gpencil_settings->simplify_f);
+ }
+ else {
+ BKE_gpencil_stroke_simplify_adaptive(gpd, gps, brush->gpencil_settings->simplify_f);
+ }
+ }
+
+ if (!is_bezier_mode) {
+ /* reproject to plane (only in 3d space) */
+ gpencil_reproject_toplane(p, gps);
}
- /* reproject to plane (only in 3d space) */
- gpencil_reproject_toplane(p, gps);
/* change position relative to parent object */
gpencil_apply_parent(depsgraph, obact, gpl, gps);
/* If camera view or view projection, reproject flat to view to avoid perspective effect. */
- if ((!is_depth) && (((align_flag & GP_PROJECT_VIEWSPACE) && is_lock_axis_view) || is_camera)) {
+ if ((!is_bezier_mode) && (!is_depth) &&
+ (((align_flag & GP_PROJECT_VIEWSPACE) && is_lock_axis_view) || is_camera)) {
ED_gpencil_project_stroke_to_view(p->C, p->gpl, gps);
}
@@ -1282,12 +1317,12 @@ static void gpencil_stroke_newfrombuffer(tGPsdata *p)
/* post process stroke */
if ((p->brush->gpencil_settings->flag & GP_BRUSH_GROUP_SETTINGS) &&
- p->brush->gpencil_settings->flag & GP_BRUSH_TRIM_STROKE) {
+ (p->brush->gpencil_settings->flag & GP_BRUSH_TRIM_STROKE) && (!is_bezier_mode)) {
BKE_gpencil_stroke_trim(gpd, gps);
}
/* Join with existing strokes. */
- if (ts->gpencil_flags & GP_TOOL_FLAG_AUTOMERGE_STROKE) {
+ if ((ts->gpencil_flags & GP_TOOL_FLAG_AUTOMERGE_STROKE) && (!is_bezier_mode)) {
if (gps->prev != NULL) {
int pt_index = 0;
bool doit = true;
@@ -1306,7 +1341,7 @@ static void gpencil_stroke_newfrombuffer(tGPsdata *p)
}
/* Calc geometry data. */
- BKE_gpencil_stroke_geometry_update(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
/* In Multiframe mode, duplicate the stroke in other frames. */
if (GPENCIL_MULTIEDIT_SESSIONS_ON(p->gpd)) {
diff --git a/source/blender/editors/gpencil/gpencil_primitive.c b/source/blender/editors/gpencil/gpencil_primitive.c
index a2b4e5dee64..d7d0cd2c05b 100644
--- a/source/blender/editors/gpencil/gpencil_primitive.c
+++ b/source/blender/editors/gpencil/gpencil_primitive.c
@@ -1093,7 +1093,7 @@ static void gpencil_primitive_update_strokes(bContext *C, tGPDprimitive *tgpi)
}
/* Calc geometry data. */
- BKE_gpencil_stroke_geometry_update(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
/* Update evaluated data. */
ED_gpencil_sbuffer_update_eval(tgpi->gpd, tgpi->ob_eval);
@@ -1351,7 +1351,7 @@ static void gpencil_primitive_interaction_end(bContext *C,
copy_v2_v2(gps->aspect_ratio, brush_settings->aspect_ratio);
/* Calc geometry data. */
- BKE_gpencil_stroke_geometry_update(tgpi->gpd, gps);
+ BKE_gpencil_stroke_geometry_update(tgpi->gpd, gps, GP_GEO_UPDATE_DEFAULT);
}
/* transfer stroke from temporary buffer to the actual frame */
@@ -1394,7 +1394,7 @@ static void gpencil_primitive_interaction_end(bContext *C,
}
ED_gpencil_stroke_close_by_distance(gps, 0.02f);
}
- BKE_gpencil_stroke_geometry_update(tgpi->gpd, gps);
+ BKE_gpencil_stroke_geometry_update(tgpi->gpd, gps, GP_GEO_UPDATE_DEFAULT);
}
/* In Multiframe mode, duplicate the stroke in other frames. */
diff --git a/source/blender/editors/gpencil/gpencil_sculpt_paint.c b/source/blender/editors/gpencil/gpencil_sculpt_paint.c
index efd0f86df03..9ea4f176585 100644
--- a/source/blender/editors/gpencil/gpencil_sculpt_paint.c
+++ b/source/blender/editors/gpencil/gpencil_sculpt_paint.c
@@ -54,6 +54,7 @@
#include "BKE_context.h"
#include "BKE_deform.h"
#include "BKE_gpencil.h"
+#include "BKE_gpencil_curve.h"
#include "BKE_gpencil_geom.h"
#include "BKE_gpencil_modifier.h"
#include "BKE_main.h"
@@ -96,6 +97,8 @@ typedef struct tGP_BrushEditData {
ScrArea *area;
ARegion *region;
+ ToolSettings *ts;
+
/* Current GPencil datablock */
bGPdata *gpd;
@@ -291,7 +294,7 @@ static void gpencil_recalc_geometry_tag(bGPDstroke *gps)
}
/* Recalc any stroke tagged. */
-static void gpencil_update_geometry(bGPdata *gpd)
+static void gpencil_update_geometry(bGPdata *gpd, ToolSettings *ts)
{
if (gpd == NULL) {
return;
@@ -305,7 +308,11 @@ static void gpencil_update_geometry(bGPdata *gpd)
LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
if (gps->flag & GP_STROKE_TAG) {
- BKE_gpencil_stroke_geometry_update(gpd, gps);
+ BKE_gpencil_stroke_refit_curve(gps,
+ ts->gpencil_curve_fit_threshold,
+ ts->gpencil_curve_fit_corner_angle,
+ GP_GEO_UPDATE_CURVE_REFIT_ALL);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_POLYLINE_REGENERATE_ALL);
gps->flag &= ~GP_STROKE_TAG;
}
}
@@ -1161,6 +1168,7 @@ static bool gpencil_sculpt_brush_init(bContext *C, wmOperator *op)
gso->bmain = CTX_data_main(C);
/* store state */
gso->settings = gpencil_sculpt_get_settings(scene);
+ gso->ts = ts;
/* Random generator, only init once. */
uint rng_seed = (uint)(PIL_check_seconds_timer_i() & UINT_MAX);
@@ -1304,7 +1312,7 @@ static void gpencil_sculpt_brush_exit(bContext *C, wmOperator *op)
gso->brush->gpencil_settings->sculpt_flag &= ~GP_SCULPT_FLAG_TMP_INVERT;
/* Update geometry data for tagged strokes. */
- gpencil_update_geometry(gso->gpd);
+ gpencil_update_geometry(gso->gpd, gso->ts);
/* free operator data */
MEM_freeN(gso);
@@ -1449,6 +1457,7 @@ static bool gpencil_sculpt_brush_do_stroke(tGP_BrushEditData *gso,
bool include_last = false;
bool changed = false;
float rot_eval = 0.0f;
+ const bool is_curve = GPENCIL_STROKE_TYPE_BEZIER(gps_active);
if (gps->totpoints == 1) {
bGPDspoint pt_temp;
@@ -1465,6 +1474,9 @@ static bool gpencil_sculpt_brush_do_stroke(tGP_BrushEditData *gso,
if (len_v2v2_int(mval_i, pc1) <= radius) {
/* apply operation to this point */
if (pt_active != NULL) {
+ if (is_curve) {
+ pt_active->flag |= GP_SPOINT_TAG;
+ }
rot_eval = gpencil_sculpt_rotation_eval_get(gso, gps, pt, 0);
changed = apply(gso, gps_active, rot_eval, 0, radius, pc1);
}
@@ -1508,7 +1520,7 @@ static bool gpencil_sculpt_brush_do_stroke(tGP_BrushEditData *gso,
/* To each point individually... */
pt = &gps->points[i];
- if ((pt->runtime.pt_orig == NULL) && (tool != GPSCULPT_TOOL_GRAB)) {
+ if (!is_curve && (pt->runtime.pt_orig == NULL) && (tool != GPSCULPT_TOOL_GRAB)) {
continue;
}
pt_active = (pt->runtime.pt_orig) ? pt->runtime.pt_orig : pt;
@@ -1517,8 +1529,14 @@ static bool gpencil_sculpt_brush_do_stroke(tGP_BrushEditData *gso,
((pt_active->flag & GP_SPOINT_SELECT) == 0)) {
continue;
}
+
index = (pt->runtime.pt_orig) ? pt->runtime.idx_orig : i;
if ((pt_active != NULL) && (index < gps_active->totpoints)) {
+ if (is_curve) {
+ /* Tag points that will be transformed for curve update. */
+ pt_active->flag |= GP_SPOINT_TAG;
+ }
+
rot_eval = gpencil_sculpt_rotation_eval_get(gso, gps, pt, i);
ok = apply(gso, gps_active, rot_eval, index, radius, pc1);
}
@@ -1690,7 +1708,7 @@ static bool gpencil_sculpt_brush_do_frame(bContext *C,
MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(ob, gps->mat_nr + 1);
/* Update active frame now, only if material has fill. */
if (gp_style->flag & GP_MATERIAL_FILL_SHOW) {
- BKE_gpencil_stroke_geometry_update(gpd, gps_active);
+ BKE_gpencil_stroke_geometry_update(gpd, gps_active, GP_GEO_UPDATE_DEFAULT);
}
else {
gpencil_recalc_geometry_tag(gps_active);
diff --git a/source/blender/editors/gpencil/gpencil_select.c b/source/blender/editors/gpencil/gpencil_select.c
index c33b43247fd..e4f397be20e 100644
--- a/source/blender/editors/gpencil/gpencil_select.c
+++ b/source/blender/editors/gpencil/gpencil_select.c
@@ -132,27 +132,6 @@ static bool gpencil_select_poll(bContext *C)
return false;
}
-static bool gpencil_3d_point_to_screen_space(ARegion *region,
- const float diff_mat[4][4],
- const float co[3],
- int r_co[2])
-{
- float parent_co[3];
- mul_v3_m4v3(parent_co, diff_mat, co);
- int screen_co[2];
- if (ED_view3d_project_int_global(
- region, parent_co, screen_co, V3D_PROJ_RET_CLIP_BB | V3D_PROJ_RET_CLIP_WIN) ==
- V3D_PROJ_RET_OK) {
- if (!ELEM(V2D_IS_CLIPPED, screen_co[0], screen_co[1])) {
- copy_v2_v2_int(r_co, screen_co);
- return true;
- }
- }
- r_co[0] = V2D_IS_CLIPPED;
- r_co[1] = V2D_IS_CLIPPED;
- return false;
-}
-
/* helper to deselect all selected strokes/points */
static void deselect_all_selected(bContext *C)
{
@@ -162,33 +141,32 @@ static void deselect_all_selected(bContext *C)
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) {
- bGPDspoint *pt;
- int i;
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps)) {
+ bGPDcurve *gpc = gps->editcurve;
- /* deselect points */
- for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
- pt->flag &= ~GP_SPOINT_SELECT;
+ if (gpc->flag & GP_CURVE_SELECT) {
+ /* Deselect the curve points. */
+ for (uint32_t i = 0; i < gpc->tot_curve_points; i++) {
+ bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
+ BezTriple *bezt = &gpc_pt->bezt;
+ gpc_pt->flag &= ~GP_CURVE_POINT_SELECT;
+ BEZT_DESEL_ALL(bezt);
+ }
+ gpc->flag &= ~GP_CURVE_SELECT;
}
-
- /* deselect stroke itself too */
- gps->flag &= ~GP_STROKE_SELECT;
- BKE_gpencil_stroke_select_index_reset(gps);
}
-
- /* deselect curve and curve points */
- if (gps->editcurve != NULL) {
- bGPDcurve *gpc = gps->editcurve;
- for (int j = 0; j < gpc->tot_curve_points; j++) {
- bGPDcurve_point *gpc_pt = &gpc->curve_points[j];
- BezTriple *bezt = &gpc_pt->bezt;
- gpc_pt->flag &= ~GP_CURVE_POINT_SELECT;
- BEZT_DESEL_ALL(bezt);
+ else {
+ if (gps->flag & GP_STROKE_SELECT) {
+ /* Deselect the points. */
+ for (uint32_t i = 0; i < gps->totpoints; i++) {
+ bGPDspoint *pt = &gps->points[i];
+ pt->flag &= ~GP_SPOINT_SELECT;
+ }
+ gps->flag &= ~GP_STROKE_SELECT;
}
-
- gpc->flag &= ~GP_CURVE_SELECT;
}
+
+ BKE_gpencil_stroke_select_index_reset(gps);
}
CTX_DATA_END;
}
@@ -210,12 +188,10 @@ static void select_all_curve_points(bGPdata *gpd, bGPDstroke *gps, bGPDcurve *gp
if (deselect == false) {
gpc->flag |= GP_CURVE_SELECT;
- gps->flag |= GP_STROKE_SELECT;
BKE_gpencil_stroke_select_index_set(gpd, gps);
}
else {
gpc->flag &= ~GP_CURVE_SELECT;
- gps->flag &= ~GP_STROKE_SELECT;
BKE_gpencil_stroke_select_index_reset(gps);
}
}
@@ -244,7 +220,6 @@ static int gpencil_select_all_exec(bContext *C, wmOperator *op)
{
bGPdata *gpd = ED_gpencil_data_get_active(C);
int action = RNA_enum_get(op->ptr, "action");
- const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
if (gpd == NULL) {
BKE_report(op->reports, RPT_ERROR, "No Grease Pencil data");
@@ -264,12 +239,7 @@ static int gpencil_select_all_exec(bContext *C, wmOperator *op)
}
}
- if (is_curve_edit) {
- ED_gpencil_select_curve_toggle_all(C, action);
- }
- else {
- ED_gpencil_select_toggle_all(C, action);
- }
+ ED_gpencil_select_toggle_all(C, action);
/* updates */
DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY);
@@ -308,7 +278,6 @@ void GPENCIL_OT_select_all(wmOperatorType *ot)
static int gpencil_select_linked_exec(bContext *C, wmOperator *op)
{
bGPdata *gpd = ED_gpencil_data_get_active(C);
- const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
if (gpd == NULL) {
BKE_report(op->reports, RPT_ERROR, "No Grease Pencil data");
@@ -320,9 +289,11 @@ static int gpencil_select_linked_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
- if (is_curve_edit) {
- GP_EDITABLE_CURVES_BEGIN(gps_iter, C, gpl, gps, gpc)
- {
+ bool changed = false;
+ /* select all points in selected strokes */
+ CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) {
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps)) {
+ bGPDcurve *gpc = gps->editcurve;
if (gpc->flag & GP_CURVE_SELECT) {
for (int i = 0; i < gpc->tot_curve_points; i++) {
bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
@@ -330,13 +301,10 @@ static int gpencil_select_linked_exec(bContext *C, wmOperator *op)
gpc_pt->flag |= GP_CURVE_POINT_SELECT;
BEZT_SEL_ALL(bezt);
}
+ changed = true;
}
}
- GP_EDITABLE_CURVES_END(gps_iter);
- }
- else {
- /* select all points in selected strokes */
- CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) {
+ else {
if (gps->flag & GP_STROKE_SELECT) {
bGPDspoint *pt;
int i;
@@ -344,19 +312,24 @@ static int gpencil_select_linked_exec(bContext *C, wmOperator *op)
for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
pt->flag |= GP_SPOINT_SELECT;
}
+
+ changed = true;
}
}
- CTX_DATA_END;
}
+ CTX_DATA_END;
- /* updates */
- DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY);
+ if (changed) {
+ /* updates */
+ DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY);
- /* copy on write tag is needed, or else no refresh happens */
- DEG_id_tag_update(&gpd->id, ID_RECALC_COPY_ON_WRITE);
+ /* copy on write tag is needed, or else no refresh happens */
+ DEG_id_tag_update(&gpd->id, ID_RECALC_COPY_ON_WRITE);
+
+ WM_event_add_notifier(C, NC_GPENCIL | NA_SELECTED, NULL);
+ WM_event_add_notifier(C, NC_GEOM | ND_SELECT, NULL);
+ }
- WM_event_add_notifier(C, NC_GPENCIL | NA_SELECTED, NULL);
- WM_event_add_notifier(C, NC_GEOM | ND_SELECT, NULL);
return OPERATOR_FINISHED;
}
@@ -385,7 +358,6 @@ static int gpencil_select_alternate_exec(bContext *C, wmOperator *op)
{
bGPdata *gpd = ED_gpencil_data_get_active(C);
const bool unselect_ends = RNA_boolean_get(op->ptr, "unselect_ends");
- const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
if (gpd == NULL) {
BKE_report(op->reports, RPT_ERROR, "No Grease Pencil data");
@@ -398,10 +370,10 @@ static int gpencil_select_alternate_exec(bContext *C, wmOperator *op)
}
bool changed = false;
- if (is_curve_edit) {
- GP_EDITABLE_CURVES_BEGIN(gps_iter, C, gpl, gps, gpc)
- {
- if ((gps->flag & GP_STROKE_SELECT) && (gps->totpoints > 1)) {
+ CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) {
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps)) {
+ bGPDcurve *gpc = gps->editcurve;
+ if ((gpc->flag & GP_CURVE_SELECT) && (gpc->tot_curve_points > 1)) {
int idx = 0;
int start = 0;
if (unselect_ends) {
@@ -435,11 +407,7 @@ static int gpencil_select_alternate_exec(bContext *C, wmOperator *op)
changed = true;
}
}
- GP_EDITABLE_CURVES_END(gps_iter);
- }
- else {
- /* select all points in selected strokes */
- CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) {
+ else {
if ((gps->flag & GP_STROKE_SELECT) && (gps->totpoints > 1)) {
bGPDspoint *pt;
int row = 0;
@@ -471,8 +439,8 @@ static int gpencil_select_alternate_exec(bContext *C, wmOperator *op)
changed = true;
}
}
- CTX_DATA_END;
}
+ CTX_DATA_END;
if (changed) {
/* updates */
@@ -535,7 +503,6 @@ static bool gpencil_select_same_layer(bContext *C)
{
Scene *scene = CTX_data_scene(C);
bGPdata *gpd = ED_gpencil_data_get_active(C);
- const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
bool changed = false;
CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
@@ -549,49 +516,46 @@ static bool gpencil_select_same_layer(bContext *C)
/* Search for a selected stroke */
for (gps = gpf->strokes.first; gps; gps = gps->next) {
- if (ED_gpencil_stroke_can_use(C, gps)) {
- if (gps->flag & GP_STROKE_SELECT) {
- found = true;
- break;
- }
+ if (!ED_gpencil_stroke_can_use(C, gps)) {
+ continue;
+ }
+
+ if (gps->flag & GP_STROKE_SELECT ||
+ (GPENCIL_STROKE_TYPE_BEZIER(gps) && gps->editcurve->flag & GP_CURVE_SELECT)) {
+ found = true;
+ break;
}
}
/* Select all if found */
if (found) {
- if (is_curve_edit) {
- for (gps = gpf->strokes.first; gps; gps = gps->next) {
- if (gps->editcurve != NULL && ED_gpencil_stroke_can_use(C, gps)) {
- bGPDcurve *gpc = gps->editcurve;
- for (int i = 0; i < gpc->tot_curve_points; i++) {
- bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
- gpc_pt->flag |= GP_CURVE_POINT_SELECT;
- BEZT_SEL_ALL(&gpc_pt->bezt);
- }
- gpc->flag |= GP_CURVE_SELECT;
- gps->flag |= GP_STROKE_SELECT;
- BKE_gpencil_stroke_select_index_set(gpd, gps);
+ for (gps = gpf->strokes.first; gps; gps = gps->next) {
+ if (!ED_gpencil_stroke_can_use(C, gps)) {
+ continue;
+ }
- changed = true;
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps)) {
+ bGPDcurve *gpc = gps->editcurve;
+ for (int i = 0; i < gpc->tot_curve_points; i++) {
+ bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
+ gpc_pt->flag |= GP_CURVE_POINT_SELECT;
+ BEZT_SEL_ALL(&gpc_pt->bezt);
}
+ gpc->flag |= GP_CURVE_SELECT;
}
- }
- else {
- for (gps = gpf->strokes.first; gps; gps = gps->next) {
- if (ED_gpencil_stroke_can_use(C, gps)) {
- bGPDspoint *pt;
- int i;
-
- for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
- pt->flag |= GP_SPOINT_SELECT;
- }
-
- gps->flag |= GP_STROKE_SELECT;
- BKE_gpencil_stroke_select_index_set(gpd, gps);
+ else {
+ bGPDspoint *pt;
+ int i;
- changed = true;
+ for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
+ pt->flag |= GP_SPOINT_SELECT;
}
+
+ gps->flag |= GP_STROKE_SELECT;
}
+
+ BKE_gpencil_stroke_select_index_set(gpd, gps);
+ changed = true;
}
}
}
@@ -604,14 +568,12 @@ static bool gpencil_select_same_layer(bContext *C)
static bool gpencil_select_same_material(bContext *C)
{
bGPdata *gpd = ED_gpencil_data_get_active(C);
- const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
/* First, build set containing all the colors of selected strokes */
GSet *selected_colors = BLI_gset_str_new("GP Selected Colors");
- bool changed = false;
-
CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) {
- if (gps->flag & GP_STROKE_SELECT) {
+ if (gps->flag & GP_STROKE_SELECT ||
+ (GPENCIL_STROKE_TYPE_BEZIER(gps) && gps->editcurve->flag & GP_CURVE_SELECT)) {
/* add instead of insert here, otherwise the uniqueness check gets skipped,
* and we get many duplicate entries...
*/
@@ -620,28 +582,22 @@ static bool gpencil_select_same_material(bContext *C)
}
CTX_DATA_END;
+ bool changed = false;
+
/* Second, select any visible stroke that uses these colors */
- if (is_curve_edit) {
- CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) {
- if (gps->editcurve != NULL && BLI_gset_haskey(selected_colors, &gps->mat_nr)) {
+ CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) {
+ if (BLI_gset_haskey(selected_colors, &gps->mat_nr)) {
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps)) {
bGPDcurve *gpc = gps->editcurve;
for (int i = 0; i < gpc->tot_curve_points; i++) {
bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
gpc_pt->flag |= GP_CURVE_POINT_SELECT;
BEZT_SEL_ALL(&gpc_pt->bezt);
}
- gpc->flag |= GP_CURVE_SELECT;
- gps->flag |= GP_STROKE_SELECT;
- BKE_gpencil_stroke_select_index_set(gpd, gps);
- changed = true;
+ gpc->flag |= GP_CURVE_SELECT;
}
- }
- CTX_DATA_END;
- }
- else {
- CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) {
- if (BLI_gset_haskey(selected_colors, &gps->mat_nr)) {
+ else {
/* select this stroke */
bGPDspoint *pt;
int i;
@@ -651,13 +607,13 @@ static bool gpencil_select_same_material(bContext *C)
}
gps->flag |= GP_STROKE_SELECT;
- BKE_gpencil_stroke_select_index_set(gpd, gps);
-
- changed = true;
}
+
+ BKE_gpencil_stroke_select_index_set(gpd, gps);
+ changed = true;
}
- CTX_DATA_END;
}
+ CTX_DATA_END;
/* Free memory. */
if (selected_colors != NULL) {
@@ -741,7 +697,6 @@ void GPENCIL_OT_select_grouped(wmOperatorType *ot)
static int gpencil_select_first_exec(bContext *C, wmOperator *op)
{
bGPdata *gpd = ED_gpencil_data_get_active(C);
- const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
/* If not edit/sculpt mode, the event has been caught but not processed. */
if (GPENCIL_NONE_EDIT_MODE(gpd)) {
@@ -754,36 +709,33 @@ static int gpencil_select_first_exec(bContext *C, wmOperator *op)
bool changed = false;
CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) {
/* skip stroke if we're only manipulating selected strokes */
- if (only_selected && !(gps->flag & GP_STROKE_SELECT)) {
+ if (only_selected &&
+ !((gps->flag & GP_STROKE_SELECT) ||
+ (GPENCIL_STROKE_TYPE_BEZIER(gps) && gps->editcurve->flag & GP_CURVE_SELECT))) {
continue;
}
/* select first point */
BLI_assert(gps->totpoints >= 1);
- if (is_curve_edit) {
- if (gps->editcurve != NULL) {
- bGPDcurve *gpc = gps->editcurve;
- gpc->curve_points[0].flag |= GP_CURVE_POINT_SELECT;
- 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);
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps)) {
+ bGPDcurve *gpc = gps->editcurve;
- if ((extend == false) && (gps->totpoints > 1)) {
- for (int i = 1; i < gpc->tot_curve_points; i++) {
- bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
- gpc_pt->flag &= ~GP_CURVE_POINT_SELECT;
- BEZT_DESEL_ALL(&gpc_pt->bezt);
- }
+ gpc->curve_points[0].flag |= GP_CURVE_POINT_SELECT;
+ BEZT_SEL_ALL(&gpc->curve_points[0].bezt);
+ gpc->flag |= GP_CURVE_SELECT;
+
+ if ((extend == false) && (gpc->tot_curve_points > 1)) {
+ for (int i = 1; i < gpc->tot_curve_points; i++) {
+ bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
+ gpc_pt->flag &= ~GP_CURVE_POINT_SELECT;
+ BEZT_DESEL_ALL(&gpc_pt->bezt);
}
- changed = true;
}
}
else {
gps->points->flag |= GP_SPOINT_SELECT;
gps->flag |= GP_STROKE_SELECT;
- BKE_gpencil_stroke_select_index_set(gpd, gps);
/* deselect rest? */
if ((extend == false) && (gps->totpoints > 1)) {
@@ -795,8 +747,10 @@ static int gpencil_select_first_exec(bContext *C, wmOperator *op)
pt->flag &= ~GP_SPOINT_SELECT;
}
}
- changed = true;
}
+
+ BKE_gpencil_stroke_select_index_set(gpd, gps);
+ changed = true;
}
CTX_DATA_END;
@@ -851,7 +805,6 @@ void GPENCIL_OT_select_first(wmOperatorType *ot)
static int gpencil_select_last_exec(bContext *C, wmOperator *op)
{
bGPdata *gpd = ED_gpencil_data_get_active(C);
- const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
/* If not edit/sculpt mode, the event has been caught but not processed. */
if (GPENCIL_NONE_EDIT_MODE(gpd)) {
@@ -864,35 +817,33 @@ static int gpencil_select_last_exec(bContext *C, wmOperator *op)
bool changed = false;
CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) {
/* skip stroke if we're only manipulating selected strokes */
- if (only_selected && !(gps->flag & GP_STROKE_SELECT)) {
+ if (only_selected &&
+ !((gps->flag & GP_STROKE_SELECT) ||
+ (GPENCIL_STROKE_TYPE_BEZIER(gps) && gps->editcurve->flag & GP_CURVE_SELECT))) {
continue;
}
/* select last point */
BLI_assert(gps->totpoints >= 1);
- if (is_curve_edit) {
- if (gps->editcurve != NULL) {
- bGPDcurve *gpc = gps->editcurve;
- gpc->curve_points[gpc->tot_curve_points - 1].flag |= GP_CURVE_POINT_SELECT;
- 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);
- 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];
- gpc_pt->flag &= ~GP_CURVE_POINT_SELECT;
- BEZT_DESEL_ALL(&gpc_pt->bezt);
- }
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps)) {
+ bGPDcurve *gpc = gps->editcurve;
+
+ gpc->curve_points[gpc->tot_curve_points - 1].flag |= GP_CURVE_POINT_SELECT;
+ BEZT_SEL_ALL(&gpc->curve_points[gpc->tot_curve_points - 1].bezt);
+ gpc->flag |= GP_CURVE_SELECT;
+ BKE_gpencil_stroke_select_index_set(gpd, gps);
+ 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];
+ gpc_pt->flag &= ~GP_CURVE_POINT_SELECT;
+ BEZT_DESEL_ALL(&gpc_pt->bezt);
}
- changed = true;
}
}
else {
gps->points[gps->totpoints - 1].flag |= GP_SPOINT_SELECT;
gps->flag |= GP_STROKE_SELECT;
- BKE_gpencil_stroke_select_index_set(gpd, gps);
/* deselect rest? */
if ((extend == false) && (gps->totpoints > 1)) {
@@ -904,9 +855,10 @@ static int gpencil_select_last_exec(bContext *C, wmOperator *op)
pt->flag &= ~GP_SPOINT_SELECT;
}
}
-
- changed = true;
}
+
+ BKE_gpencil_stroke_select_index_set(gpd, gps);
+ changed = true;
}
CTX_DATA_END;
@@ -961,106 +913,98 @@ void GPENCIL_OT_select_last(wmOperatorType *ot)
static int gpencil_select_more_exec(bContext *C, wmOperator *UNUSED(op))
{
bGPdata *gpd = ED_gpencil_data_get_active(C);
- const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
/* If not edit/sculpt mode, the event has been caught but not processed. */
if (GPENCIL_NONE_EDIT_MODE(gpd)) {
return OPERATOR_CANCELLED;
}
bool changed = false;
- if (is_curve_edit) {
- GP_EDITABLE_STROKES_BEGIN (gp_iter, C, gpl, gps) {
- if (gps->editcurve != NULL && gps->flag & GP_STROKE_SELECT) {
- bGPDcurve *editcurve = gps->editcurve;
-
- bool prev_sel = false;
- for (int i = 0; i < editcurve->tot_curve_points; i++) {
- bGPDcurve_point *gpc_pt = &editcurve->curve_points[i];
- BezTriple *bezt = &gpc_pt->bezt;
- if (gpc_pt->flag & GP_CURVE_POINT_SELECT) {
- /* selected point - just set flag for next point */
- prev_sel = true;
- }
- else {
- /* unselected point - expand selection if previous was selected... */
- if (prev_sel) {
- gpc_pt->flag |= GP_CURVE_POINT_SELECT;
- BEZT_SEL_ALL(bezt);
- changed = true;
- }
- prev_sel = false;
+ CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) {
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps) && (gps->editcurve->flag & GP_CURVE_SELECT)) {
+ bGPDcurve *gpc = gps->editcurve;
+
+ bool prev_sel = false;
+ for (int i = 0; i < gpc->tot_curve_points; i++) {
+ bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
+ BezTriple *bezt = &gpc_pt->bezt;
+ if (gpc_pt->flag & GP_CURVE_POINT_SELECT) {
+ /* selected point - just set flag for next point */
+ prev_sel = true;
+ }
+ else {
+ /* unselected point - expand selection if previous was selected... */
+ if (prev_sel) {
+ gpc_pt->flag |= GP_CURVE_POINT_SELECT;
+ BEZT_SEL_ALL(bezt);
+ changed = true;
}
+ prev_sel = false;
}
+ }
- prev_sel = false;
- for (int i = editcurve->tot_curve_points - 1; i >= 0; i--) {
- bGPDcurve_point *gpc_pt = &editcurve->curve_points[i];
- BezTriple *bezt = &gpc_pt->bezt;
- if (gpc_pt->flag & GP_CURVE_POINT_SELECT) {
- prev_sel = true;
- }
- else {
- /* unselected point - expand selection if previous was selected... */
- if (prev_sel) {
- gpc_pt->flag |= GP_CURVE_POINT_SELECT;
- BEZT_SEL_ALL(bezt);
- changed = true;
- }
- prev_sel = false;
+ prev_sel = false;
+ for (int i = gpc->tot_curve_points - 1; i >= 0; i--) {
+ bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
+ BezTriple *bezt = &gpc_pt->bezt;
+ if (gpc_pt->flag & GP_CURVE_POINT_SELECT) {
+ prev_sel = true;
+ }
+ else {
+ /* unselected point - expand selection if previous was selected... */
+ if (prev_sel) {
+ gpc_pt->flag |= GP_CURVE_POINT_SELECT;
+ BEZT_SEL_ALL(bezt);
+ changed = true;
}
+ prev_sel = false;
}
}
}
- GP_EDITABLE_STROKES_END(gp_iter);
- }
- else {
- CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) {
- if (gps->flag & GP_STROKE_SELECT) {
- bGPDspoint *pt;
- int i;
- bool prev_sel;
+ else if (gps->flag & GP_STROKE_SELECT) {
+ bGPDspoint *pt;
+ int i;
+ bool prev_sel;
- /* First Pass: Go in forward order,
- * expanding selection if previous was selected (pre changes).
- * - This pass covers the "after" edges of selection islands
- */
- prev_sel = false;
- for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
- if (pt->flag & GP_SPOINT_SELECT) {
- /* selected point - just set flag for next point */
- prev_sel = true;
- }
- else {
- /* unselected point - expand selection if previous was selected... */
- if (prev_sel) {
- pt->flag |= GP_SPOINT_SELECT;
- changed = true;
- }
- prev_sel = false;
+ /* First Pass: Go in forward order,
+ * expanding selection if previous was selected (pre changes).
+ * - This pass covers the "after" edges of selection islands
+ */
+ prev_sel = false;
+ for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
+ if (pt->flag & GP_SPOINT_SELECT) {
+ /* selected point - just set flag for next point */
+ prev_sel = true;
+ }
+ else {
+ /* unselected point - expand selection if previous was selected... */
+ if (prev_sel) {
+ pt->flag |= GP_SPOINT_SELECT;
+ changed = true;
}
+ prev_sel = false;
}
+ }
- /* Second Pass: Go in reverse order, doing the same as before (except in opposite order)
- * - This pass covers the "before" edges of selection islands
- */
- prev_sel = false;
- for (pt -= 1; i > 0; i--, pt--) {
- if (pt->flag & GP_SPOINT_SELECT) {
- prev_sel = true;
- }
- else {
- /* unselected point - expand selection if previous was selected... */
- if (prev_sel) {
- pt->flag |= GP_SPOINT_SELECT;
- changed = true;
- }
- prev_sel = false;
+ /* Second Pass: Go in reverse order, doing the same as before (except in opposite order)
+ * - This pass covers the "before" edges of selection islands
+ */
+ prev_sel = false;
+ for (pt -= 1; i > 0; i--, pt--) {
+ if (pt->flag & GP_SPOINT_SELECT) {
+ prev_sel = true;
+ }
+ else {
+ /* unselected point - expand selection if previous was selected... */
+ if (prev_sel) {
+ pt->flag |= GP_SPOINT_SELECT;
+ changed = true;
}
+ prev_sel = false;
}
}
}
- CTX_DATA_END;
}
+ CTX_DATA_END;
if (changed) {
/* updates */
@@ -1100,7 +1044,6 @@ void GPENCIL_OT_select_more(wmOperatorType *ot)
static int gpencil_select_less_exec(bContext *C, wmOperator *UNUSED(op))
{
bGPdata *gpd = ED_gpencil_data_get_active(C);
- const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
/* If not edit/sculpt mode, the event has been caught but not processed. */
if (GPENCIL_NONE_EDIT_MODE(gpd)) {
@@ -1108,105 +1051,115 @@ static int gpencil_select_less_exec(bContext *C, wmOperator *UNUSED(op))
}
bool changed = false;
- if (is_curve_edit) {
- GP_EDITABLE_STROKES_BEGIN (gp_iter, C, gpl, gps) {
- if (gps->editcurve != NULL && gps->flag & GP_STROKE_SELECT) {
- bGPDcurve *editcurve = gps->editcurve;
- int i;
+ CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) {
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps) && (gps->editcurve->flag & GP_CURVE_SELECT)) {
+ bGPDcurve *gpc = gps->editcurve;
+ int tot_selected = 0, num_deselected = 0;
- bool prev_sel = false;
- for (i = 0; i < editcurve->tot_curve_points; i++) {
- bGPDcurve_point *gpc_pt = &editcurve->curve_points[i];
- BezTriple *bezt = &gpc_pt->bezt;
- if (gpc_pt->flag & GP_CURVE_POINT_SELECT) {
- /* shrink if previous wasn't selected */
- if (prev_sel == false) {
- gpc_pt->flag &= ~GP_CURVE_POINT_SELECT;
- BEZT_DESEL_ALL(bezt);
- changed = true;
- }
- prev_sel = true;
- }
- else {
- /* mark previous as being unselected - and hence, is trigger for shrinking */
- prev_sel = false;
+ bool prev_sel = false;
+ int i;
+ for (i = 0; i < gpc->tot_curve_points; i++) {
+ bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
+ BezTriple *bezt = &gpc_pt->bezt;
+ if (gpc_pt->flag & GP_CURVE_POINT_SELECT) {
+ /* shrink if previous wasn't selected */
+ if (prev_sel == false) {
+ gpc_pt->flag &= ~GP_CURVE_POINT_SELECT;
+ BEZT_DESEL_ALL(bezt);
+ changed = true;
+ num_deselected++;
}
+ prev_sel = true;
+ tot_selected++;
+ }
+ else {
+ /* mark previous as being unselected - and hence, is trigger for shrinking */
+ prev_sel = false;
}
+ }
- /* Second Pass: Go in reverse order, doing the same as before (except in opposite order)
- * - This pass covers the "before" edges of selection islands
- */
- prev_sel = false;
- for (i = editcurve->tot_curve_points - 1; i > 0; i--) {
- bGPDcurve_point *gpc_pt = &editcurve->curve_points[i];
- BezTriple *bezt = &gpc_pt->bezt;
- if (gpc_pt->flag & GP_CURVE_POINT_SELECT) {
- /* shrink if previous wasn't selected */
- if (prev_sel == false) {
- gpc_pt->flag &= ~GP_CURVE_POINT_SELECT;
- BEZT_DESEL_ALL(bezt);
- changed = true;
- }
- prev_sel = true;
- }
- else {
- /* mark previous as being unselected - and hence, is trigger for shrinking */
- prev_sel = false;
+ /* Second Pass: Go in reverse order, doing the same as before (except in opposite order)
+ * - This pass covers the "before" edges of selection islands
+ */
+ prev_sel = false;
+ for (i = gpc->tot_curve_points - 1; i > 0; i--) {
+ bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
+ BezTriple *bezt = &gpc_pt->bezt;
+ if (gpc_pt->flag & GP_CURVE_POINT_SELECT) {
+ /* shrink if previous wasn't selected */
+ if (prev_sel == false) {
+ gpc_pt->flag &= ~GP_CURVE_POINT_SELECT;
+ BEZT_DESEL_ALL(bezt);
+ changed = true;
+ num_deselected++;
}
+ prev_sel = true;
+ }
+ else {
+ /* mark previous as being unselected - and hence, is trigger for shrinking */
+ prev_sel = false;
}
}
+
+ /* Deselect curve if all points are deselected. */
+ if (tot_selected - num_deselected == 0) {
+ gpc->flag &= ~GP_CURVE_SELECT;
+ }
}
- GP_EDITABLE_STROKES_END(gp_iter);
- }
- else {
- CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) {
- if (gps->flag & GP_STROKE_SELECT) {
- bGPDspoint *pt;
- int i;
- bool prev_sel;
+ else if (gps->flag & GP_STROKE_SELECT) {
+ bGPDspoint *pt;
+ int i, tot_selected = 0, num_deselected = 0;
+ bool prev_sel;
- /* First Pass: Go in forward order, shrinking selection
- * if previous was not selected (pre changes).
- * - This pass covers the "after" edges of selection islands
- */
- prev_sel = false;
- for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
- if (pt->flag & GP_SPOINT_SELECT) {
- /* shrink if previous wasn't selected */
- if (prev_sel == false) {
- pt->flag &= ~GP_SPOINT_SELECT;
- changed = true;
- }
- prev_sel = true;
- }
- else {
- /* mark previous as being unselected - and hence, is trigger for shrinking */
- prev_sel = false;
+ /* First Pass: Go in forward order, shrinking selection
+ * if previous was not selected (pre changes).
+ * - This pass covers the "after" edges of selection islands
+ */
+ prev_sel = false;
+ for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
+ if (pt->flag & GP_SPOINT_SELECT) {
+ /* shrink if previous wasn't selected */
+ if (prev_sel == false) {
+ pt->flag &= ~GP_SPOINT_SELECT;
+ changed = true;
+ num_deselected++;
}
+ prev_sel = true;
+ tot_selected++;
+ }
+ else {
+ /* mark previous as being unselected - and hence, is trigger for shrinking */
+ prev_sel = false;
}
+ }
- /* Second Pass: Go in reverse order, doing the same as before (except in opposite order)
- * - This pass covers the "before" edges of selection islands
- */
- prev_sel = false;
- for (pt -= 1; i > 0; i--, pt--) {
- if (pt->flag & GP_SPOINT_SELECT) {
- /* shrink if previous wasn't selected */
- if (prev_sel == false) {
- pt->flag &= ~GP_SPOINT_SELECT;
- changed = true;
- }
- prev_sel = true;
- }
- else {
- /* mark previous as being unselected - and hence, is trigger for shrinking */
- prev_sel = false;
+ /* Second Pass: Go in reverse order, doing the same as before (except in opposite order)
+ * - This pass covers the "before" edges of selection islands
+ */
+ prev_sel = false;
+ for (pt -= 1; i > 0; i--, pt--) {
+ if (pt->flag & GP_SPOINT_SELECT) {
+ /* shrink if previous wasn't selected */
+ if (prev_sel == false) {
+ pt->flag &= ~GP_SPOINT_SELECT;
+ changed = true;
+ num_deselected++;
}
+ prev_sel = true;
+ }
+ else {
+ /* mark previous as being unselected - and hence, is trigger for shrinking */
+ prev_sel = false;
}
}
+
+ /* Deselect curve if all points are deselected. */
+ if (tot_selected - num_deselected == 0) {
+ gps->flag &= ~GP_STROKE_SELECT;
+ }
}
- CTX_DATA_END;
}
+ CTX_DATA_END;
if (changed) {
/* updates */
@@ -1261,8 +1214,7 @@ static bool gpencil_stroke_do_circle_sel(bGPdata *gpd,
rcti *rect,
const float diff_mat[4][4],
const int selectmode,
- const float scale,
- const bool is_curve_edit)
+ const float scale)
{
bGPDspoint *pt = NULL;
int x0 = 0, y0 = 0;
@@ -1328,23 +1280,13 @@ static bool gpencil_stroke_do_circle_sel(bGPdata *gpd,
}
}
- /* If curve edit mode, generate the curve. */
- if (is_curve_edit && hit && gps_active->editcurve == NULL) {
- 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(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(gpd, gps_active);
return changed;
}
-static bool gpencil_do_curve_circle_sel(bContext *C,
+static bool gpencil_curve_do_circle_sel(bContext *C,
bGPDstroke *gps,
bGPDcurve *gpc,
const int mx,
@@ -1366,6 +1308,9 @@ static bool gpencil_do_curve_circle_sel(bContext *C,
for (int i = 0; i < gpc->tot_curve_points; i++) {
bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
BezTriple *bezt = &gpc_pt->bezt;
+ bGPDcurve_point *gpc_active_pt = (gpc_pt->runtime.gpc_pt_orig) ? gpc_pt->runtime.gpc_pt_orig :
+ gpc_pt;
+ BezTriple *bezt_active = &gpc_active_pt->bezt;
if (bezt->hide == 1) {
continue;
@@ -1403,13 +1348,13 @@ static bool gpencil_do_curve_circle_sel(bContext *C,
hit = true;
/* change selection */
if (select) {
- gpc_pt->flag |= GP_CURVE_POINT_SELECT;
- BEZT_SEL_IDX(bezt, j);
+ gpc_active_pt->flag |= GP_CURVE_POINT_SELECT;
+ BEZT_SEL_IDX(bezt_active, j);
}
else {
- BEZT_DESEL_IDX(bezt, j);
- if (!BEZT_ISSEL_ANY(bezt)) {
- gpc_pt->flag &= ~GP_CURVE_POINT_SELECT;
+ BEZT_DESEL_IDX(bezt_active, j);
+ if (!BEZT_ISSEL_ANY(bezt_active)) {
+ gpc_active_pt->flag &= ~GP_CURVE_POINT_SELECT;
}
}
}
@@ -1420,20 +1365,31 @@ static bool gpencil_do_curve_circle_sel(bContext *C,
if (hit && (selectmode == GP_SELECTMODE_STROKE)) {
for (int i = 0; i < gpc->tot_curve_points; i++) {
bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
- BezTriple *bezt = &gpc_pt->bezt;
+ bGPDcurve_point *gpc_active_pt = (gpc_pt->runtime.gpc_pt_orig) ?
+ gpc_pt->runtime.gpc_pt_orig :
+ gpc_pt;
+ BezTriple *bezt_active = &gpc_active_pt->bezt;
if (select) {
- gpc_pt->flag |= GP_CURVE_POINT_SELECT;
- BEZT_SEL_ALL(bezt);
+ gpc_active_pt->flag |= GP_CURVE_POINT_SELECT;
+ BEZT_SEL_ALL(bezt_active);
}
else {
- gpc_pt->flag &= ~GP_CURVE_POINT_SELECT;
- BEZT_DESEL_ALL(bezt);
+ gpc_active_pt->flag &= ~GP_CURVE_POINT_SELECT;
+ BEZT_DESEL_ALL(bezt_active);
}
}
- }
- BKE_gpencil_curve_sync_selection(gpd, gps);
+ if (select) {
+ gpc->flag |= GP_CURVE_SELECT;
+ }
+ else {
+ gpc->flag &= ~GP_CURVE_SELECT;
+ }
+ }
+ else {
+ BKE_gpencil_curve_sync_selection(gpd, gps);
+ }
return hit;
}
@@ -1443,7 +1399,6 @@ static int gpencil_circle_select_exec(bContext *C, wmOperator *op)
bGPdata *gpd = ED_gpencil_data_get_active(C);
ToolSettings *ts = CTX_data_tool_settings(C);
Object *ob = CTX_data_active_object(C);
- const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
int selectmode;
if (ob && ob->mode == OB_MODE_SCULPT_GPENCIL) {
@@ -1487,32 +1442,24 @@ static int gpencil_circle_select_exec(bContext *C, wmOperator *op)
rect.xmax = mx + radius;
rect.ymax = my + radius;
- if (is_curve_edit) {
- if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
- ED_gpencil_select_curve_toggle_all(C, SEL_DESELECT);
- changed = true;
- }
+ GP_SpaceConversion gsc = {NULL};
+ /* init space conversion stuff */
+ gpencil_point_conversion_init(C, &gsc);
- GP_EDITABLE_CURVES_BEGIN(gps_iter, C, gpl, gps, gpc)
- {
- changed |= gpencil_do_curve_circle_sel(
- C, gps, gpc, mx, my, radius, select, &rect, gps_iter.diff_mat, selectmode);
- }
- GP_EDITABLE_CURVES_END(gps_iter);
+ if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
+ // ED_gpencil_select_curve_toggle_all(C, SEL_DESELECT);
+ ED_gpencil_select_toggle_all(C, SEL_DESELECT);
+ changed = true;
}
- if (changed == false) {
- GP_SpaceConversion gsc = {NULL};
- /* init space conversion stuff */
- gpencil_point_conversion_init(C, &gsc);
-
- if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
- ED_gpencil_select_toggle_all(C, SEL_DESELECT);
- changed = true;
+ /* find visible strokes, and select if hit */
+ GP_EVALUATED_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) {
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps)) {
+ bGPDcurve *gpc = gps->editcurve;
+ changed |= gpencil_curve_do_circle_sel(
+ C, gps, gpc, mx, my, radius, select, &rect, gpstroke_iter.diff_mat, selectmode);
}
-
- /* find visible strokes, and select if hit */
- GP_EVALUATED_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) {
+ else {
changed |= gpencil_stroke_do_circle_sel(gpd,
gpl,
gps,
@@ -1524,11 +1471,10 @@ static int gpencil_circle_select_exec(bContext *C, wmOperator *op)
&rect,
gpstroke_iter.diff_mat,
selectmode,
- scale,
- is_curve_edit);
+ scale);
}
- GP_EVALUATED_STROKES_END(gpstroke_iter);
}
+ GP_EVALUATED_STROKES_END(gpstroke_iter);
/* updates */
if (changed) {
@@ -1604,7 +1550,7 @@ static bool gpencil_stroke_fill_isect_rect(ARegion *region,
int *pt2d = points2d[i];
int screen_co[2];
- gpencil_3d_point_to_screen_space(region, diff_mat, &pt->x, screen_co);
+ ED_gpencil_3d_point_to_screen_space(region, NULL, diff_mat, &pt->x, screen_co);
DO_MINMAX2(screen_co, min, max);
copy_v2_v2_int(pt2d, screen_co);
@@ -1654,278 +1600,251 @@ static bool gpencil_stroke_fill_isect_rect(ARegion *region,
static bool gpencil_generic_curve_select(bContext *C,
Object *ob,
+ bGPdata *gpd,
+ bGPDstroke *gps,
+ bGPDcurve *gpc,
+ struct GP_EditableStrokes_Iter *gpstroke_iter,
+ GP_SpaceConversion *gsc,
GPencilTestFn is_inside_fn,
- rcti UNUSED(box),
+ rcti box,
GP_SelectUserData *user_data,
const bool strokemode,
const eSelectOp sel_op)
{
- ARegion *region = CTX_wm_region(C);
View3D *v3d = CTX_wm_view3d(C);
- bGPdata *gpd = ob->data;
+ bGPDstroke *gps_active = (gps->runtime.gps_orig) ? gps->runtime.gps_orig : gps;
+
const bool handle_only_selected = (v3d->overlay.handle_display == CURVE_HANDLE_SELECTED);
const bool handle_all = (v3d->overlay.handle_display == CURVE_HANDLE_ALL);
bool hit = false;
bool changed = false;
bool whole = false;
+ bool any_select = false;
+ for (int i = 0; i < gpc->tot_curve_points; i++) {
+ bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
+ BezTriple *bezt = &gpc_pt->bezt;
- GP_EDITABLE_CURVES_BEGIN(gps_iter, C, gpl, gps, gpc)
- {
- bool any_select = false;
- for (int i = 0; i < gpc->tot_curve_points; i++) {
- bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
- BezTriple *bezt = &gpc_pt->bezt;
+ bGPDcurve_point *gpc_pt_active = (gpc_pt->runtime.gpc_pt_orig) ? gpc_pt->runtime.gpc_pt_orig :
+ gpc_pt;
+ BezTriple *bezt_active = &gpc_pt_active->bezt;
- if (bezt->hide == 1) {
- continue;
- }
+ if (bezt_active->hide == 1) {
+ continue;
+ }
- const bool handles_visible = (handle_all || (handle_only_selected &&
- (gpc_pt->flag & GP_CURVE_POINT_SELECT)));
+ const bool handles_visible = (handle_all || (handle_only_selected &&
+ (gpc_pt->flag & GP_CURVE_POINT_SELECT)));
- if (handles_visible) {
- for (int j = 0; j < 3; j++) {
- const bool is_select = BEZT_ISSEL_IDX(bezt, j);
- bool is_inside = is_inside_fn(region, gps_iter.diff_mat, bezt->vec[j], user_data);
- if (strokemode) {
- if (is_inside) {
- hit = true;
- any_select = true;
- break;
- }
- }
- else {
- const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside);
- if (sel_op_result != -1) {
- if (sel_op_result) {
- gpc_pt->flag |= GP_CURVE_POINT_SELECT;
- BEZT_SEL_IDX(bezt, j);
- any_select = true;
- }
- else {
- gpc_pt->flag &= ~GP_CURVE_POINT_SELECT;
- BEZT_DESEL_IDX(bezt, j);
- }
- changed = true;
- hit = true;
- }
- else {
- if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
- gpc_pt->flag &= ~GP_CURVE_POINT_SELECT;
- BEZT_DESEL_IDX(bezt, j);
- }
- }
- }
- }
- }
- /* If the handles are not visible only check ctrl point (vec[1]). */
- else {
- const bool is_select = bezt->f2;
- bool is_inside = is_inside_fn(region, gps_iter.diff_mat, bezt->vec[1], user_data);
+ if (handles_visible) {
+ for (int j = 0; j < 3; j++) {
+ const bool is_select = BEZT_ISSEL_IDX(bezt_active, j);
+ bool is_inside = is_inside_fn(
+ gsc->region, gpstroke_iter->diff_mat, bezt->vec[j], user_data);
if (strokemode) {
if (is_inside) {
hit = true;
any_select = true;
+ break;
}
}
else {
const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside);
if (sel_op_result != -1) {
if (sel_op_result) {
- gpc_pt->flag |= GP_CURVE_POINT_SELECT;
- bezt->f2 |= SELECT;
+ gpc_pt_active->flag |= GP_CURVE_POINT_SELECT;
+ BEZT_SEL_IDX(bezt_active, j);
any_select = true;
}
else {
- gpc_pt->flag &= ~GP_CURVE_POINT_SELECT;
- bezt->f2 &= ~SELECT;
+ gpc_pt_active->flag &= ~GP_CURVE_POINT_SELECT;
+ BEZT_DESEL_IDX(bezt_active, j);
}
changed = true;
hit = true;
}
else {
if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
- gpc_pt->flag &= ~GP_CURVE_POINT_SELECT;
- bezt->f2 &= ~SELECT;
+ gpc_pt_active->flag &= ~GP_CURVE_POINT_SELECT;
+ BEZT_DESEL_IDX(bezt_active, j);
}
}
}
}
}
-
- /* TODO: Fix selection for filled in curves. */
-#if 0
- if (!hit) {
- /* check if we selected the inside of a filled curve */
- MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(ob, gps->mat_nr + 1);
- if ((gp_style->flag & GP_MATERIAL_FILL_SHOW) == 0) {
- continue;
+ /* if the handles are not visible only check ctrl point (vec[1])*/
+ else {
+ const bool is_select = bezt_active->f2;
+ bool is_inside = is_inside_fn(gsc->region, gpstroke_iter->diff_mat, bezt->vec[1], user_data);
+ if (strokemode) {
+ if (is_inside) {
+ hit = true;
+ any_select = true;
+ }
}
-
- whole = gpencil_stroke_fill_isect_rect(region, gps, gps_iter.diff_mat, box);
- }
-#endif
- /* select the entire curve */
- if (strokemode || whole) {
- const int sel_op_result = ED_select_op_action_deselected(sel_op, any_select, hit || whole);
- if (sel_op_result != -1) {
- for (int i = 0; i < gpc->tot_curve_points; i++) {
- bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
- BezTriple *bezt = &gpc_pt->bezt;
-
+ else {
+ const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside);
+ if (sel_op_result != -1) {
if (sel_op_result) {
- gpc_pt->flag |= GP_CURVE_POINT_SELECT;
- BEZT_SEL_ALL(bezt);
+ gpc_pt_active->flag |= GP_CURVE_POINT_SELECT;
+ bezt_active->f2 |= SELECT;
+ any_select = true;
}
else {
- gpc_pt->flag &= ~GP_CURVE_POINT_SELECT;
- BEZT_DESEL_ALL(bezt);
+ gpc_pt_active->flag &= ~GP_CURVE_POINT_SELECT;
+ bezt_active->f2 &= ~SELECT;
+ }
+ changed = true;
+ hit = true;
+ }
+ else {
+ if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
+ gpc_pt_active->flag &= ~GP_CURVE_POINT_SELECT;
+ bezt_active->f2 &= ~SELECT;
}
}
+ }
+ }
+ }
+
+ if (!hit) {
+ /* check if we selected the inside of a filled curve */
+ MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(ob, gps_active->mat_nr + 1);
+ if ((gp_style->flag & GP_MATERIAL_FILL_SHOW) == 0) {
+ return changed;
+ }
+ int mval[2];
+ mval[0] = (box.xmax + box.xmin) / 2;
+ mval[1] = (box.ymax + box.ymin) / 2;
+
+ whole = ED_gpencil_stroke_point_is_inside(gps_active, gsc, mval, gpstroke_iter->diff_mat);
+ }
+
+ /* select the entire curve */
+ if (strokemode || whole) {
+ const int sel_op_result = ED_select_op_action_deselected(sel_op, any_select, hit || whole);
+ if (sel_op_result != -1) {
+ for (int i = 0; i < gpc->tot_curve_points; i++) {
+ bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
+ bGPDcurve_point *gpc_pt_active = (gpc_pt->runtime.gpc_pt_orig) ?
+ gpc_pt->runtime.gpc_pt_orig :
+ gpc_pt;
+ BezTriple *bezt_active = &gpc_pt_active->bezt;
if (sel_op_result) {
- gpc->flag |= GP_CURVE_SELECT;
+ gpc_pt_active->flag |= GP_CURVE_POINT_SELECT;
+ BEZT_SEL_ALL(bezt_active);
}
else {
- gpc->flag &= ~GP_CURVE_SELECT;
+ gpc_pt_active->flag &= ~GP_CURVE_POINT_SELECT;
+ BEZT_DESEL_ALL(bezt_active);
}
- changed = true;
}
- }
- BKE_gpencil_curve_sync_selection(gpd, gps);
+ if (sel_op_result) {
+ gpc->flag |= GP_CURVE_SELECT;
+ }
+ else {
+ gpc->flag &= ~GP_CURVE_SELECT;
+ }
+ changed = true;
+ }
+ }
+ else {
+ BKE_gpencil_curve_sync_selection(gpd, gps_active);
}
- GP_EDITABLE_CURVES_END(gps_iter);
return changed;
}
-static bool gpencil_generic_stroke_select(bContext *C,
- Object *ob,
+static bool gpencil_generic_stroke_select(Object *ob,
bGPdata *gpd,
+ bGPDlayer *gpl,
+ bGPDstroke *gps,
+ struct GP_EditableStrokes_Iter *gpstroke_iter,
+ GP_SpaceConversion *gsc,
GPencilTestFn is_inside_fn,
rcti box,
GP_SelectUserData *user_data,
const bool strokemode,
const bool segmentmode,
const eSelectOp sel_op,
- const float scale,
- const bool is_curve_edit)
+ const float scale)
{
- GP_SpaceConversion gsc = {NULL};
bool changed = false;
- /* init space conversion stuff */
- gpencil_point_conversion_init(C, &gsc);
-
- /* 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;
- int i;
-
- for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
- pt->flag &= ~GP_SPOINT_SELECT;
- }
-
- gps->flag &= ~GP_STROKE_SELECT;
- BKE_gpencil_stroke_select_index_reset(gps);
- }
- CTX_DATA_END;
-
- changed = true;
- }
-
- /* select/deselect points */
- GP_EVALUATED_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) {
- bGPDstroke *gps_active = (gps->runtime.gps_orig) ? gps->runtime.gps_orig : gps;
- bool whole = false;
+ bool whole = false;
+ bGPDstroke *gps_active = (gps->runtime.gps_orig) ? gps->runtime.gps_orig : gps;
- bGPDspoint *pt;
- int i;
- bool hit = false;
- for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
- bGPDspoint *pt_active = (pt->runtime.pt_orig) ? pt->runtime.pt_orig : pt;
+ bGPDspoint *pt;
+ int i;
+ bool hit = false;
+ for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
+ bGPDspoint *pt_active = (pt->runtime.pt_orig) ? pt->runtime.pt_orig : pt;
- /* convert point coords to screenspace */
- const bool is_inside = is_inside_fn(gsc.region, gpstroke_iter.diff_mat, &pt->x, user_data);
- if (strokemode == false) {
- const bool is_select = (pt_active->flag & GP_SPOINT_SELECT) != 0;
- const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside);
- if (sel_op_result != -1) {
- SET_FLAG_FROM_TEST(pt_active->flag, sel_op_result, GP_SPOINT_SELECT);
- changed = true;
- hit = true;
+ /* convert point coords to screenspace */
+ const bool is_inside = is_inside_fn(gsc->region, gpstroke_iter->diff_mat, &pt->x, user_data);
+ if (strokemode == false) {
+ const bool is_select = (pt_active->flag & GP_SPOINT_SELECT) != 0;
+ const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside);
+ if (sel_op_result != -1) {
+ SET_FLAG_FROM_TEST(pt_active->flag, sel_op_result, GP_SPOINT_SELECT);
+ changed = true;
+ hit = true;
- /* Expand selection to segment. */
- if (segmentmode) {
- bool hit_select = (bool)(pt_active->flag & GP_SPOINT_SELECT);
- float r_hita[3], r_hitb[3];
- ED_gpencil_select_stroke_segment(
- gpd, gpl, gps_active, pt_active, hit_select, false, scale, r_hita, r_hitb);
- }
- }
- }
- else {
- if (is_inside) {
- hit = true;
- break;
+ /* Expand selection to segment. */
+ if (segmentmode) {
+ bool hit_select = (bool)(pt_active->flag & GP_SPOINT_SELECT);
+ float r_hita[3], r_hitb[3];
+ ED_gpencil_select_stroke_segment(
+ gpd, gpl, gps_active, pt_active, hit_select, false, scale, r_hita, r_hitb);
}
}
}
-
- /* If nothing hit, check if the mouse is inside a filled stroke using the center or
- * Box or lasso area. */
- if (!hit) {
- /* Only check filled strokes. */
- MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(ob, gps->mat_nr + 1);
- if ((gp_style->flag & GP_MATERIAL_FILL_SHOW) == 0) {
- continue;
+ else {
+ if (is_inside) {
+ hit = true;
+ break;
}
- int mval[2];
- mval[0] = (box.xmax + box.xmin) / 2;
- mval[1] = (box.ymax + box.ymin) / 2;
+ }
+ }
- whole = ED_gpencil_stroke_point_is_inside(gps, &gsc, mval, gpstroke_iter.diff_mat);
+ /* If nothing hit, check if the mouse is inside a filled stroke using the center or
+ * Box or lasso area. */
+ if (!hit) {
+ /* Only check filled strokes. */
+ MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(ob, gps_active->mat_nr + 1);
+ if ((gp_style->flag & GP_MATERIAL_FILL_SHOW) == 0) {
+ return changed;
}
+ int mval[2];
+ mval[0] = (box.xmax + box.xmin) / 2;
+ mval[1] = (box.ymax + box.ymin) / 2;
- /* if stroke mode expand selection. */
- if ((strokemode) || (whole)) {
- const bool is_select = BKE_gpencil_stroke_select_check(gps_active) || whole;
- const bool is_inside = hit || whole;
- const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside);
- if (sel_op_result != -1) {
- for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
- bGPDspoint *pt_active = (pt->runtime.pt_orig) ? pt->runtime.pt_orig : pt;
+ whole = ED_gpencil_stroke_point_is_inside(gps_active, gsc, mval, gpstroke_iter->diff_mat);
+ }
- if (sel_op_result) {
- pt_active->flag |= GP_SPOINT_SELECT;
- }
- else {
- pt_active->flag &= ~GP_SPOINT_SELECT;
- }
+ /* if stroke mode expand selection. */
+ if ((strokemode) || (whole)) {
+ const bool is_select = BKE_gpencil_stroke_select_check(gps_active) || whole;
+ const bool is_inside = hit || whole;
+ const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside);
+ if (sel_op_result != -1) {
+ for (i = 0, pt = gps_active->points; i < gps_active->totpoints; i++, pt++) {
+ bGPDspoint *pt_active = (pt->runtime.pt_orig) ? pt->runtime.pt_orig : pt;
+
+ if (sel_op_result) {
+ pt_active->flag |= GP_SPOINT_SELECT;
+ }
+ else {
+ pt_active->flag &= ~GP_SPOINT_SELECT;
}
- changed = true;
}
- }
-
- /* If curve edit mode, generate the curve. */
- if (is_curve_edit && (hit || whole) && gps_active->editcurve == NULL) {
- 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(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(gpd, gps_active);
}
- GP_EVALUATED_STROKES_END(gpstroke_iter);
+
+ /* Ensure that stroke selection is in sync with its points */
+ BKE_gpencil_stroke_sync_selection(gpd, gps_active);
return changed;
}
@@ -1940,7 +1859,6 @@ static int gpencil_generic_select_exec(bContext *C,
bGPdata *gpd = ED_gpencil_data_get_active(C);
ToolSettings *ts = CTX_data_tool_settings(C);
ScrArea *area = CTX_wm_area(C);
- const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
int selectmode;
if (ob && ob->mode == OB_MODE_SCULPT_GPENCIL) {
@@ -1961,38 +1879,69 @@ static int gpencil_generic_select_exec(bContext *C,
const eSelectOp sel_op = RNA_enum_get(op->ptr, "mode");
const float scale = ts->gp_sculpt.isect_threshold;
- bool changed = false;
-
/* sanity checks */
if (area == NULL) {
BKE_report(op->reports, RPT_ERROR, "No active area");
return OPERATOR_CANCELLED;
}
- if (is_curve_edit) {
- changed = gpencil_generic_curve_select(
- C, ob, is_inside_fn, box, user_data, strokemode, sel_op);
+ bool changed = false;
+ GP_SpaceConversion gsc = {NULL};
+ /* init space conversion stuff */
+ gpencil_point_conversion_init(C, &gsc);
+
+ /* 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;
+
+ deselect_all_selected(C);
+ changed = true;
}
- if (changed == false) {
- changed = gpencil_generic_stroke_select(C,
- ob,
- gpd,
- is_inside_fn,
- box,
- user_data,
- strokemode,
- segmentmode,
- sel_op,
- scale,
- is_curve_edit);
+ GP_EVALUATED_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) {
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps)) {
+ bGPDcurve *gpc = gps->editcurve;
+ if (gpencil_generic_curve_select(C,
+ ob,
+ gpd,
+ gps,
+ gpc,
+ &gpstroke_iter,
+ &gsc,
+ is_inside_fn,
+ box,
+ user_data,
+ strokemode,
+ sel_op)) {
+ changed = true;
+ }
+ }
+ else {
+ if (gpencil_generic_stroke_select(ob,
+ gpd,
+ gpl,
+ gps,
+ &gpstroke_iter,
+ &gsc,
+ is_inside_fn,
+ box,
+ user_data,
+ strokemode,
+ segmentmode,
+ sel_op,
+ scale)) {
+ changed = true;
+ }
+ }
}
+ GP_EVALUATED_STROKES_END(gpstroke_iter);
/* if paint mode,delete selected points */
if (GPENCIL_PAINT_MODE(gpd)) {
gpencil_delete_selected_point_wrap(C);
- changed = true;
DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
+ changed = true;
}
/* updates */
@@ -2020,7 +1969,7 @@ static bool gpencil_test_box(ARegion *region,
GP_SelectUserData *user_data)
{
int co[2] = {0};
- if (gpencil_3d_point_to_screen_space(region, diff_mat, pt, co)) {
+ if (ED_gpencil_3d_point_to_screen_space(region, NULL, diff_mat, pt, co)) {
return BLI_rcti_isect_pt(&user_data->rect, co[0], co[1]);
}
return false;
@@ -2069,7 +2018,7 @@ static bool gpencil_test_lasso(ARegion *region,
GP_SelectUserData *user_data)
{
int co[2] = {0};
- if (gpencil_3d_point_to_screen_space(region, diff_mat, pt, co)) {
+ if (ED_gpencil_3d_point_to_screen_space(region, NULL, diff_mat, pt, co)) {
/* test if in lasso boundbox + within the lasso noose */
return (BLI_rcti_isect_pt(&user_data->rect, co[0], co[1]) &&
BLI_lasso_is_point_inside(
@@ -2125,56 +2074,93 @@ void GPENCIL_OT_select_lasso(wmOperatorType *ot)
/** \name Mouse Pick Select Operator
* \{ */
-static void gpencil_select_curve_point(bContext *C,
- const int mval[2],
- const int radius_squared,
- bGPDlayer **r_gpl,
- bGPDstroke **r_gps,
- bGPDcurve **r_gpc,
- bGPDcurve_point **r_pt,
- char *handle)
+static bool gpencil_select_curve_point_closest(bContext *C,
+ bGPDcurve *gpc,
+ struct GP_EditableStrokes_Iter *gps_iter,
+ const int mval[2],
+ const int radius_squared,
+ bGPDcurve_point **r_pt,
+ int *handle_idx,
+ int *hit_distance)
{
+ bool hit = false;
ARegion *region = CTX_wm_region(C);
View3D *v3d = CTX_wm_view3d(C);
const bool only_selected = (v3d->overlay.handle_display == CURVE_HANDLE_SELECTED);
- int hit_distance = radius_squared;
+ for (int i = 0; i < gpc->tot_curve_points; i++) {
+ bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
+ BezTriple *bezt = &gpc_pt->bezt;
- GP_EDITABLE_CURVES_BEGIN(gps_iter, C, gpl, gps, gpc)
- {
- for (int i = 0; i < gpc->tot_curve_points; i++) {
- bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
- BezTriple *bezt = &gpc_pt->bezt;
+ bGPDcurve_point *gpc_pt_active = (gpc_pt->runtime.gpc_pt_orig) ? gpc_pt->runtime.gpc_pt_orig :
+ gpc_pt;
+ BezTriple *bezt_active = &gpc_pt_active->bezt;
- if (bezt->hide == 1) {
- continue;
+ if (bezt_active->hide == 1) {
+ continue;
+ }
+
+ const bool handles_visible = (v3d->overlay.handle_display != CURVE_HANDLE_NONE) &&
+ (!only_selected || BEZT_ISSEL_ANY(bezt));
+
+ /* if the handles are not visible only check ctrl point (vec[1])*/
+ int from = (!handles_visible) ? 1 : 0;
+ int to = (!handles_visible) ? 2 : 3;
+
+ for (int j = from; j < to; j++) {
+ int screen_co[2];
+ if (ED_gpencil_3d_point_to_screen_space(
+ region, NULL, gps_iter->diff_mat, bezt->vec[j], screen_co)) {
+ const int pt_distance = len_manhattan_v2v2_int(mval, screen_co);
+
+ if (pt_distance <= radius_squared && pt_distance < *hit_distance) {
+ *r_pt = gpc_pt_active;
+ *handle_idx = j;
+ *hit_distance = pt_distance;
+ hit = true;
+ }
}
+ }
+ }
- const bool handles_visible = (v3d->overlay.handle_display != CURVE_HANDLE_NONE) &&
- (!only_selected || BEZT_ISSEL_ANY(bezt));
+ return hit;
+}
+
+static bool gpencil_select_stroke_point_closest(bGPDstroke *gps,
+ struct GP_EditableStrokes_Iter *gps_iter,
+ GP_SpaceConversion *gsc,
+ const int mval[2],
+ const int radius_squared,
+ bGPDspoint **r_pt,
+ int *hit_distance)
+{
+ bool hit = false;
+ bGPDstroke *gps_active = (gps->runtime.gps_orig) ? gps->runtime.gps_orig : gps;
- /* If the handles are not visible only check control point (vec[1]). */
- int from = (!handles_visible) ? 1 : 0;
- int to = (!handles_visible) ? 2 : 3;
+ for (int i = 0; i < gps->totpoints; i++) {
+ bGPDspoint *pt = &gps->points[i];
+ bGPDspoint *pt_active = (pt->runtime.pt_orig) ? pt->runtime.pt_orig : pt;
+ int xy[2];
- for (int j = from; j < to; j++) {
- int screen_co[2];
- if (gpencil_3d_point_to_screen_space(region, gps_iter.diff_mat, bezt->vec[j], screen_co)) {
- const int pt_distance = len_manhattan_v2v2_int(mval, screen_co);
+ bGPDspoint pt2;
+ gpencil_point_to_parent_space(pt, gps_iter->diff_mat, &pt2);
+ gpencil_point_to_xy(gsc, gps_active, &pt2, &xy[0], &xy[1]);
- if (pt_distance <= radius_squared && pt_distance < hit_distance) {
- *r_gpl = gpl;
- *r_gps = gps;
- *r_gpc = gpc;
- *r_pt = gpc_pt;
- *handle = j;
- hit_distance = pt_distance;
- }
- }
+ /* do boundbox check first */
+ if (!ELEM(V2D_IS_CLIPPED, xy[0], xy[1])) {
+ const int pt_distance = len_manhattan_v2v2_int(mval, xy);
+
+ /* check if point is inside */
+ if (pt_distance <= radius_squared && pt_distance < *hit_distance) {
+ /* only use this point if it is a better match than the current hit - T44685 */
+ *r_pt = pt_active;
+ *hit_distance = pt_distance;
+ hit = true;
}
}
}
- GP_EDITABLE_CURVES_END(gps_iter);
+
+ return hit;
}
static int gpencil_select_exec(bContext *C, wmOperator *op)
@@ -2184,7 +2170,6 @@ static int gpencil_select_exec(bContext *C, wmOperator *op)
bGPdata *gpd = ED_gpencil_data_get_active(C);
ToolSettings *ts = CTX_data_tool_settings(C);
const float scale = ts->gp_sculpt.isect_threshold;
- const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
/* "radius" is simply a threshold (screen space) to make it easier to test with a tolerance */
const float radius = 0.4f * U.widget_unit;
@@ -2201,14 +2186,12 @@ static int gpencil_select_exec(bContext *C, wmOperator *op)
/* get mouse location */
RNA_int_get_array(op->ptr, "location", mval);
- GP_SpaceConversion gsc = {NULL};
-
bGPDlayer *hit_layer = NULL;
bGPDstroke *hit_stroke = NULL;
bGPDspoint *hit_point = NULL;
bGPDcurve *hit_curve = NULL;
bGPDcurve_point *hit_curve_point = NULL;
- char hit_curve_handle = 0;
+ int hit_curve_handle_idx = 0;
int hit_distance = radius_squared;
/* sanity checks */
@@ -2230,62 +2213,45 @@ static int gpencil_select_exec(bContext *C, wmOperator *op)
whole = (bool)(ts->gpencil_selectmode_edit == GP_SELECTMODE_STROKE);
}
- if (is_curve_edit) {
- gpencil_select_curve_point(C,
- mval,
- radius_squared,
- &hit_layer,
- &hit_stroke,
- &hit_curve,
- &hit_curve_point,
- &hit_curve_handle);
- }
-
- if (hit_curve == NULL) {
- /* init space conversion stuff */
- gpencil_point_conversion_init(C, &gsc);
-
- /* First Pass: Find stroke point which gets hit */
- GP_EVALUATED_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) {
- bGPDstroke *gps_active = (gps->runtime.gps_orig) ? gps->runtime.gps_orig : gps;
- bGPDspoint *pt;
- int i;
+ GP_SpaceConversion gsc = {NULL};
+ /* init space conversion stuff */
+ gpencil_point_conversion_init(C, &gsc);
- /* firstly, check for hit-point */
- for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
- int xy[2];
-
- bGPDspoint pt2;
- gpencil_point_to_parent_space(pt, gpstroke_iter.diff_mat, &pt2);
- gpencil_point_to_xy(&gsc, gps_active, &pt2, &xy[0], &xy[1]);
-
- /* do boundbox check first */
- if (!ELEM(V2D_IS_CLIPPED, xy[0], xy[1])) {
- const int pt_distance = len_manhattan_v2v2_int(mval, xy);
-
- /* check if point is inside */
- if (pt_distance <= radius_squared) {
- /* only use this point if it is a better match than the current hit - T44685 */
- if (pt_distance < hit_distance) {
- hit_layer = gpl;
- hit_stroke = gps_active;
- hit_point = (pt->runtime.pt_orig) ? pt->runtime.pt_orig : pt;
- hit_distance = pt_distance;
- }
- }
- }
+ /* First Pass: Find point which gets hit */
+ GP_EVALUATED_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) {
+ bGPDstroke *gps_active = (gps->runtime.gps_orig) ? gps->runtime.gps_orig : gps;
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps)) {
+ bGPDcurve *gpc = gps->editcurve;
+ if (gpencil_select_curve_point_closest(C,
+ gpc,
+ &gpstroke_iter,
+ mval,
+ radius_squared,
+ &hit_curve_point,
+ &hit_curve_handle_idx,
+ &hit_distance)) {
+ hit_layer = gpl;
+ hit_stroke = gps_active;
+ hit_point = &gps_active->points[hit_curve_point->point_index];
+ hit_curve = gps_active->editcurve;
+ }
+ }
+ else {
+ if (gpencil_select_stroke_point_closest(
+ gps, &gpstroke_iter, &gsc, mval, radius_squared, &hit_point, &hit_distance)) {
+ hit_layer = gpl;
+ hit_stroke = gps_active;
}
}
- GP_EVALUATED_STROKES_END(gpstroke_iter);
}
+ GP_EVALUATED_STROKES_END(gpstroke_iter);
/* Abort if nothing hit... */
- if (!hit_curve && !hit_curve_point && !hit_point && !hit_stroke) {
-
+ if ((hit_curve == NULL && hit_curve_point == NULL) &&
+ (hit_point == NULL && hit_stroke == NULL)) {
if (deselect_all) {
/* since left mouse select change, deselect all if click outside any hit */
deselect_all_selected(C);
-
/* copy on write tag is needed, or else no refresh happens */
DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY);
DEG_id_tag_update(&gpd->id, ID_RECALC_COPY_ON_WRITE);
@@ -2298,23 +2264,17 @@ static int gpencil_select_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
- /* select all handles if the click was on the curve but not on a handle */
- if (is_curve_edit && hit_point != NULL) {
- whole = true;
- hit_curve = hit_stroke->editcurve;
- }
-
/* adjust selection behavior - for toggle option */
if (toggle) {
if (hit_curve_point != NULL) {
BezTriple *bezt = &hit_curve_point->bezt;
- if ((bezt->f1 & SELECT) && (hit_curve_handle == 0)) {
+ if ((bezt->f1 & SELECT) && (hit_curve_handle_idx == 0)) {
deselect = true;
}
- if ((bezt->f2 & SELECT) && (hit_curve_handle == 1)) {
+ if ((bezt->f2 & SELECT) && (hit_curve_handle_idx == 1)) {
deselect = true;
}
- if ((bezt->f3 & SELECT) && (hit_curve_handle == 2)) {
+ if ((bezt->f3 & SELECT) && (hit_curve_handle_idx == 2)) {
deselect = true;
}
}
@@ -2330,13 +2290,6 @@ static int gpencil_select_exec(bContext *C, wmOperator *op)
/* Perform selection operations... */
if (whole) {
- /* Generate editcurve if it does not exist */
- if (is_curve_edit && hit_curve == NULL) {
- BKE_gpencil_stroke_editcurve_update(gpd, hit_layer, hit_stroke);
- hit_stroke->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
- BKE_gpencil_stroke_geometry_update(gpd, hit_stroke);
- hit_curve = hit_stroke->editcurve;
- }
/* select all curve points */
if (hit_curve != NULL) {
select_all_curve_points(gpd, hit_stroke, hit_curve, deselect);
@@ -2371,9 +2324,8 @@ static int gpencil_select_exec(bContext *C, wmOperator *op)
if (deselect == false) {
if (hit_curve_point != NULL) {
hit_curve_point->flag |= GP_CURVE_POINT_SELECT;
- BEZT_SEL_IDX(&hit_curve_point->bezt, hit_curve_handle);
+ BEZT_SEL_IDX(&hit_curve_point->bezt, hit_curve_handle_idx);
hit_curve->flag |= GP_CURVE_SELECT;
- hit_stroke->flag |= GP_STROKE_SELECT;
BKE_gpencil_stroke_select_index_set(gpd, hit_stroke);
}
else {
@@ -2404,7 +2356,7 @@ static int gpencil_select_exec(bContext *C, wmOperator *op)
}
else {
if (hit_curve_point != NULL) {
- BEZT_DESEL_IDX(&hit_curve_point->bezt, hit_curve_handle);
+ BEZT_DESEL_IDX(&hit_curve_point->bezt, hit_curve_handle_idx);
if (!BEZT_ISSEL_ANY(&hit_curve_point->bezt)) {
hit_curve_point->flag &= ~GP_CURVE_POINT_SELECT;
}
@@ -2487,8 +2439,12 @@ void GPENCIL_OT_select(wmOperatorType *ot)
prop = RNA_def_boolean(ot->srna, "use_shift_extend", false, "Extend", "");
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Select by Vertex Color
+ * \{ */
-/* Select by Vertex Color. */
/* Helper to create a hash of colors. */
static void gpencil_selected_hue_table(bContext *C,
Object *ob,
@@ -2508,22 +2464,43 @@ static void gpencil_selected_hue_table(bContext *C,
if (ED_gpencil_stroke_material_editable(ob, gpl, gps) == false) {
continue;
}
- if ((gps->flag & GP_STROKE_SELECT) == 0) {
- continue;
- }
/* Read all points to get all colors selected. */
- bGPDspoint *pt;
- int i;
- for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
- if (((pt->flag & GP_SPOINT_SELECT) == 0) || (pt->vert_color[3] == 0.0f)) {
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps)) {
+ bGPDcurve *gpc = gps->editcurve;
+ if ((gpc->flag & GP_CURVE_SELECT) == 0) {
+ continue;
+ }
+
+ for (int i = 0; i < gpc->tot_curve_points; i++) {
+ bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
+ if (((gpc_pt->flag & GP_CURVE_POINT_SELECT) == 0) || (gpc_pt->vert_color[3] == 0.0f)) {
+ continue;
+ }
+ /* Round Hue value. */
+ rgb_to_hsv_compat_v(gpc_pt->vert_color, hsv);
+ uint key = truncf(hsv[0] * range);
+ if (!BLI_ghash_haskey(hue_table, POINTER_FROM_INT(key))) {
+ BLI_ghash_insert(hue_table, POINTER_FROM_INT(key), POINTER_FROM_INT(key));
+ }
+ }
+ }
+ else {
+ if ((gps->flag & GP_STROKE_SELECT) == 0) {
continue;
}
- /* Round Hue value. */
- rgb_to_hsv_compat_v(pt->vert_color, hsv);
- uint key = truncf(hsv[0] * range);
- if (!BLI_ghash_haskey(hue_table, POINTER_FROM_INT(key))) {
- BLI_ghash_insert(hue_table, POINTER_FROM_INT(key), POINTER_FROM_INT(key));
+
+ for (int i = 0; i < gps->totpoints; i++) {
+ bGPDspoint *pt = &gps->points[i];
+ if (((pt->flag & GP_SPOINT_SELECT) == 0) || (pt->vert_color[3] == 0.0f)) {
+ continue;
+ }
+ /* Round Hue value. */
+ rgb_to_hsv_compat_v(pt->vert_color, hsv);
+ uint key = truncf(hsv[0] * range);
+ if (!BLI_ghash_haskey(hue_table, POINTER_FROM_INT(key))) {
+ BLI_ghash_insert(hue_table, POINTER_FROM_INT(key), POINTER_FROM_INT(key));
+ }
}
}
}
@@ -2577,39 +2554,77 @@ static int gpencil_select_vertex_color_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
+ deselect_all_selected(C);
+
/* Select any visible stroke that uses any of these colors. */
CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) {
- bGPDspoint *pt;
- int i;
- bool gps_selected = false;
- /* Check all stroke points. */
- for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
- if (pt->vert_color[3] == 0.0f) {
- continue;
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps)) {
+ bGPDcurve *gpc = gps->editcurve;
+ bool gpc_selected = false;
+ for (int i = 0; i < gpc->tot_curve_points; i++) {
+ bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
+ if (gpc_pt->vert_color[3] == 0.0f) {
+ continue;
+ }
+
+ /* Only check Hue to get value and saturation full ranges. */
+ float hsv[3];
+ /* Round Hue value. */
+ rgb_to_hsv_compat_v(gpc_pt->vert_color, hsv);
+ uint key = truncf(hsv[0] * range);
+
+ if (BLI_ghash_haskey(hue_table, POINTER_FROM_INT(key))) {
+ gpc_pt->flag |= GP_CURVE_POINT_SELECT;
+ BEZT_SEL_ALL(&gpc_pt->bezt);
+ gpc_selected = true;
+ changed = true;
+ }
}
- /* Only check Hue to get value and saturation full ranges. */
- float hsv[3];
- /* Round Hue value. */
- rgb_to_hsv_compat_v(pt->vert_color, hsv);
- uint key = truncf(hsv[0] * range);
+ if (gpc_selected) {
+ gpc->flag |= GP_CURVE_SELECT;
+ BKE_gpencil_stroke_select_index_set(gpd, gps);
- if (BLI_ghash_haskey(hue_table, POINTER_FROM_INT(key))) {
- pt->flag |= GP_SPOINT_SELECT;
- gps_selected = true;
+ /* Extend selection. */
+ if (selectmode == GP_SELECTMODE_STROKE) {
+ select_all_curve_points(gpd, gps, gpc, false);
+ }
}
}
+ else {
+ bGPDspoint *pt;
+ int i;
+ bool gps_selected = false;
+ /* Check all stroke points. */
+ for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
+ if (pt->vert_color[3] == 0.0f) {
+ continue;
+ }
- if (gps_selected) {
- gps->flag |= GP_STROKE_SELECT;
- BKE_gpencil_stroke_select_index_set(gpd, gps);
+ /* Only check Hue to get value and saturation full ranges. */
+ float hsv[3];
+ /* Round Hue value. */
+ rgb_to_hsv_compat_v(pt->vert_color, hsv);
+ uint key = truncf(hsv[0] * range);
- /* Extend stroke selection. */
- if (selectmode == GP_SELECTMODE_STROKE) {
- bGPDspoint *pt1 = NULL;
+ if (BLI_ghash_haskey(hue_table, POINTER_FROM_INT(key))) {
+ pt->flag |= GP_SPOINT_SELECT;
+ gps_selected = true;
+ changed = true;
+ }
+ }
- for (i = 0, pt1 = gps->points; i < gps->totpoints; i++, pt1++) {
- pt1->flag |= GP_SPOINT_SELECT;
+ if (gps_selected) {
+ gps->flag |= GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_set(gpd, gps);
+
+ /* Extend stroke selection. */
+ if (selectmode == GP_SELECTMODE_STROKE) {
+ bGPDspoint *pt1 = NULL;
+
+ for (i = 0, pt1 = gps->points; i < gps->totpoints; i++, pt1++) {
+ pt1->flag |= GP_SPOINT_SELECT;
+ }
}
}
}
diff --git a/source/blender/editors/gpencil/gpencil_trace_utils.c b/source/blender/editors/gpencil/gpencil_trace_utils.c
index 970afc3ff6b..35d1c69d709 100644
--- a/source/blender/editors/gpencil/gpencil_trace_utils.c
+++ b/source/blender/editors/gpencil/gpencil_trace_utils.c
@@ -365,7 +365,7 @@ void ED_gpencil_trace_data_to_strokes(Main *bmain,
BKE_gpencil_stroke_sample(gpd, gps, sample, false);
}
else {
- BKE_gpencil_stroke_geometry_update(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
}
}
else {
diff --git a/source/blender/editors/gpencil/gpencil_utils.c b/source/blender/editors/gpencil/gpencil_utils.c
index e6488cfe454..f9659a4061b 100644
--- a/source/blender/editors/gpencil/gpencil_utils.c
+++ b/source/blender/editors/gpencil/gpencil_utils.c
@@ -713,9 +713,6 @@ void gpencil_point_to_parent_space(const bGPDspoint *pt,
*/
void gpencil_apply_parent(Depsgraph *depsgraph, Object *obact, bGPDlayer *gpl, bGPDstroke *gps)
{
- bGPDspoint *pt;
- int i;
-
/* undo matrix */
float diff_mat[4][4];
float inverse_diff_mat[4][4];
@@ -724,8 +721,20 @@ void gpencil_apply_parent(Depsgraph *depsgraph, Object *obact, bGPDlayer *gpl, b
BKE_gpencil_layer_transform_matrix_get(depsgraph, obact, gpl, diff_mat);
invert_m4_m4(inverse_diff_mat, diff_mat);
- for (i = 0; i < gps->totpoints; i++) {
- pt = &gps->points[i];
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps)) {
+ bGPDcurve *gpc = gps->editcurve;
+ for (int i = 0; i < gpc->tot_curve_points; i++) {
+ bGPDcurve_point *pt = &gpc->curve_points[i];
+ BezTriple *bezt = &pt->bezt;
+ for (int j = 0; j < 3; j++) {
+ mul_v3_m4v3(fpt, inverse_diff_mat, bezt->vec[j]);
+ copy_v3_v3(bezt->vec[j], fpt);
+ }
+ }
+ }
+
+ for (int i = 0; i < gps->totpoints; i++) {
+ bGPDspoint *pt = &gps->points[i];
mul_v3_m4v3(fpt, inverse_diff_mat, &pt->x);
copy_v3_v3(&pt->x, fpt);
}
@@ -1497,7 +1506,7 @@ void gpencil_subdivide_stroke(bGPdata *gpd, bGPDstroke *gps, const int subdivide
MEM_SAFE_FREE(temp_points);
}
/* Calc geometry data. */
- BKE_gpencil_stroke_geometry_update(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
}
/* Reset parent matrix for all layers. */
@@ -1622,7 +1631,21 @@ void ED_gpencil_vgroup_assign(bContext *C, Object *ob, float weight)
continue;
}
- if (gps->flag & GP_STROKE_SELECT) {
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps) && (gps->editcurve->flag & GP_CURVE_SELECT)) {
+ BKE_gpencil_dvert_ensure(gps);
+ bGPDcurve *gpc = gps->editcurve;
+ for (int i = 0; i < gpc->tot_curve_points; i++) {
+ bGPDcurve_point *cpt = &gpc->curve_points[i];
+ MDeformVert *dvert = &gpc->dvert[i];
+ if (cpt->flag & GP_CURVE_POINT_SELECT) {
+ MDeformWeight *dw = BKE_defvert_ensure_index(dvert, def_nr);
+ if (dw != NULL) {
+ dw->weight = weight;
+ }
+ }
+ }
+ }
+ else if (gps->flag & GP_STROKE_SELECT) {
/* verify the weight array is created */
BKE_gpencil_dvert_ensure(gps);
@@ -1676,17 +1699,36 @@ void ED_gpencil_vgroup_remove(bContext *C, Object *ob)
continue;
}
- for (int i = 0; i < gps->totpoints; i++) {
- bGPDspoint *pt = &gps->points[i];
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps) && (gps->editcurve->flag & GP_CURVE_SELECT)) {
+ bGPDcurve *gpc = gps->editcurve;
+ if (gpc->dvert == NULL) {
+ continue;
+ }
+
+ for (int i = 0; i < gpc->tot_curve_points; i++) {
+ bGPDcurve_point *cpt = &gpc->curve_points[i];
+ MDeformVert *dvert = &gpc->dvert[i];
+ if ((cpt->flag & GP_CURVE_POINT_SELECT) && (dvert->totweight > 0)) {
+ MDeformWeight *dw = BKE_defvert_find_index(dvert, def_nr);
+ if (dw != NULL) {
+ BKE_defvert_remove_group(dvert, dw);
+ }
+ }
+ }
+ }
+ else if ((gps->flag & GP_STROKE_SELECT)) {
if (gps->dvert == NULL) {
continue;
}
- MDeformVert *dvert = &gps->dvert[i];
- if ((pt->flag & GP_SPOINT_SELECT) && (dvert->totweight > 0)) {
- MDeformWeight *dw = BKE_defvert_find_index(dvert, def_nr);
- if (dw != NULL) {
- BKE_defvert_remove_group(dvert, dw);
+ for (int i = 0; i < gps->totpoints; i++) {
+ bGPDspoint *pt = &gps->points[i];
+ MDeformVert *dvert = &gps->dvert[i];
+ if ((pt->flag & GP_SPOINT_SELECT) && (dvert->totweight > 0)) {
+ MDeformWeight *dw = BKE_defvert_find_index(dvert, def_nr);
+ if (dw != NULL) {
+ BKE_defvert_remove_group(dvert, dw);
+ }
}
}
}
@@ -1729,20 +1771,39 @@ void ED_gpencil_vgroup_select(bContext *C, Object *ob)
continue;
}
- for (int i = 0; i < gps->totpoints; i++) {
- bGPDspoint *pt = &gps->points[i];
- if (gps->dvert == NULL) {
+ bool selected = false;
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps)) {
+ bGPDcurve *gpc = gps->editcurve;
+ if (gpc->dvert == NULL) {
continue;
}
- MDeformVert *dvert = &gps->dvert[i];
- if (BKE_defvert_find_index(dvert, def_nr) != NULL) {
- pt->flag |= GP_SPOINT_SELECT;
- gps->flag |= GP_STROKE_SELECT;
+ for (int i = 0; i < gpc->tot_curve_points; i++) {
+ bGPDcurve_point *cpt = &gpc->curve_points[i];
+ MDeformVert *dvert = &gpc->dvert[i];
+ if (BKE_defvert_find_index(dvert, def_nr) != NULL) {
+ cpt->flag |= GP_CURVE_POINT_SELECT;
+ BEZT_SEL_ALL(&cpt->bezt);
+ gpc->flag |= GP_CURVE_SELECT;
+ selected = true;
+ }
+ }
+ }
+ else if (gps->dvert != NULL) {
+
+ for (int i = 0; i < gps->totpoints; i++) {
+ bGPDspoint *pt = &gps->points[i];
+ MDeformVert *dvert = &gps->dvert[i];
+
+ if (BKE_defvert_find_index(dvert, def_nr) != NULL) {
+ pt->flag |= GP_SPOINT_SELECT;
+ gps->flag |= GP_STROKE_SELECT;
+ selected = true;
+ }
}
}
- if (gps->flag & GP_STROKE_SELECT) {
+ if (selected) {
BKE_gpencil_stroke_select_index_set(gpd, gps);
}
}
@@ -1783,17 +1844,57 @@ void ED_gpencil_vgroup_deselect(bContext *C, Object *ob)
if (ED_gpencil_stroke_can_use(C, gps) == false) {
continue;
}
+ bool deselected = false;
- for (int i = 0; i < gps->totpoints; i++) {
- bGPDspoint *pt = &gps->points[i];
- if (gps->dvert == NULL) {
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps)) {
+ bGPDcurve *gpc = gps->editcurve;
+ if (gpc->dvert == NULL) {
continue;
}
- MDeformVert *dvert = &gps->dvert[i];
+ int desel_count = 0;
+ for (int i = 0; i < gpc->tot_curve_points; i++) {
+ bGPDcurve_point *cpt = &gpc->curve_points[i];
+ MDeformVert *dvert = &gpc->dvert[i];
+ if (BKE_defvert_find_index(dvert, def_nr) != NULL) {
+ cpt->flag &= ~GP_CURVE_POINT_SELECT;
+ BEZT_DESEL_ALL(&cpt->bezt);
+ desel_count++;
+ }
- if (BKE_defvert_find_index(dvert, def_nr) != NULL) {
- pt->flag &= ~GP_SPOINT_SELECT;
+ if ((cpt->flag & GP_CURVE_POINT_SELECT) == 0) {
+ desel_count++;
+ }
+ }
+
+ if (desel_count == gpc->tot_curve_points) {
+ deselected = true;
+ gpc->flag &= ~GP_CURVE_SELECT;
+ }
+ }
+ else if (gps->dvert != NULL) {
+ int desel_count = 0;
+ for (int i = 0; i < gps->totpoints; i++) {
+ bGPDspoint *pt = &gps->points[i];
+ MDeformVert *dvert = &gps->dvert[i];
+
+ if (BKE_defvert_find_index(dvert, def_nr) != NULL) {
+ pt->flag &= ~GP_SPOINT_SELECT;
+ desel_count++;
+ }
+
+ if ((pt->flag & GP_SPOINT_SELECT) == 0) {
+ desel_count++;
+ }
}
+
+ if (desel_count == gps->totpoints) {
+ deselected = true;
+ gps->flag &= ~GP_STROKE_SELECT;
+ }
+ }
+
+ if (deselected) {
+ BKE_gpencil_stroke_select_index_reset(gps);
}
}
}
@@ -2396,7 +2497,7 @@ static void gpencil_insert_point(bGPdata *gpd,
i2++;
}
/* Calc geometry data. */
- BKE_gpencil_stroke_geometry_update(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
MEM_SAFE_FREE(temp_points);
}
@@ -2628,9 +2729,18 @@ void ED_gpencil_select_toggle_all(bContext *C, int action)
action = SEL_SELECT;
CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) {
- if (gps->flag & GP_STROKE_SELECT) {
- action = SEL_DESELECT;
- break; /* XXX: this only gets out of the inner loop. */
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps)) {
+ bGPDcurve *gpc = gps->editcurve;
+ if (gpc->flag & GP_CURVE_SELECT) {
+ action = SEL_DESELECT;
+ break;
+ }
+ }
+ else {
+ if (gps->flag & GP_STROKE_SELECT) {
+ action = SEL_DESELECT;
+ break; /* XXX: this only gets out of the inner loop. */
+ }
}
}
CTX_DATA_END;
@@ -2654,18 +2764,25 @@ void ED_gpencil_select_toggle_all(bContext *C, int action)
/* deselect all strokes on all frames */
LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
- bGPDstroke *gps;
-
- for (gps = gpf->strokes.first; gps; gps = gps->next) {
- bGPDspoint *pt;
- int i;
-
+ for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) {
/* only edit strokes that are valid in this view... */
- if (ED_gpencil_stroke_can_use(C, gps)) {
- for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
+ if (!ED_gpencil_stroke_can_use(C, gps))
+ continue;
+
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps)) {
+ bGPDcurve *gpc = gps->editcurve;
+ for (uint32_t i = 0; i < gpc->tot_curve_points; i++) {
+ bGPDcurve_point *pt = &gpc->curve_points[i];
+ pt->flag &= ~GP_CURVE_POINT_SELECT;
+ BEZT_DESEL_ALL(&pt->bezt);
+ }
+ gpc->flag &= ~GP_CURVE_SELECT;
+ }
+ else {
+ for (uint32_t i = 0; i < gps->totpoints; i++) {
+ bGPDspoint *pt = &gps->points[i];
pt->flag &= ~GP_SPOINT_SELECT;
}
-
gps->flag &= ~GP_STROKE_SELECT;
BKE_gpencil_stroke_select_index_reset(gps);
}
@@ -2677,39 +2794,68 @@ void ED_gpencil_select_toggle_all(bContext *C, int action)
else {
/* select or deselect all strokes */
CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) {
- bGPDspoint *pt;
- int i;
bool selected = false;
- /* Change selection status of all points, then make the stroke match */
- for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
- switch (action) {
- case SEL_SELECT:
- pt->flag |= GP_SPOINT_SELECT;
- break;
-#if 0
- case SEL_DESELECT:
- pt->flag &= ~GP_SPOINT_SELECT;
- break;
-#endif
- case SEL_INVERT:
- pt->flag ^= GP_SPOINT_SELECT;
- break;
- }
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps)) {
+ bGPDcurve *gpc = gps->editcurve;
+
+ for (uint32_t i = 0; i < gpc->tot_curve_points; i++) {
+ bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
+ BezTriple *bezt = &gpc_pt->bezt;
+ switch (action) {
+ case SEL_SELECT:
+ gpc_pt->flag |= GP_CURVE_POINT_SELECT;
+ BEZT_SEL_ALL(bezt);
+ break;
+ case SEL_INVERT:
+ gpc_pt->flag ^= GP_CURVE_POINT_SELECT;
+ BEZT_SEL_INVERT(bezt);
+ break;
+ default:
+ break;
+ }
- if (pt->flag & GP_SPOINT_SELECT) {
- selected = true;
+ if (gpc_pt->flag & GP_CURVE_POINT_SELECT) {
+ selected = true;
+ }
}
- }
- /* Change status of stroke */
- if (selected) {
- gps->flag |= GP_STROKE_SELECT;
- BKE_gpencil_stroke_select_index_set(gpd, gps);
+ if (selected) {
+ gpc->flag |= GP_CURVE_SELECT;
+ }
+ else {
+ gpc->flag &= ~GP_CURVE_SELECT;
+ }
}
else {
- gps->flag &= ~GP_STROKE_SELECT;
- BKE_gpencil_stroke_select_index_reset(gps);
+ bGPDspoint *pt;
+ int i;
+
+ /* Change selection status of all points, then make the stroke match */
+ for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
+ switch (action) {
+ case SEL_SELECT:
+ pt->flag |= GP_SPOINT_SELECT;
+ break;
+ case SEL_INVERT:
+ pt->flag ^= GP_SPOINT_SELECT;
+ break;
+ }
+
+ if (pt->flag & GP_SPOINT_SELECT) {
+ selected = true;
+ }
+ }
+
+ /* Change status of stroke */
+ if (selected) {
+ gps->flag |= GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_set(gpd, gps);
+ }
+ else {
+ gps->flag &= ~GP_STROKE_SELECT;
+ BKE_gpencil_stroke_select_index_reset(gps);
+ }
}
}
CTX_DATA_END;
@@ -2758,9 +2904,7 @@ void ED_gpencil_select_curve_toggle_all(bContext *C, int action)
/* Make sure stroke has an editcurve */
if (gps->editcurve == NULL) {
- BKE_gpencil_stroke_editcurve_update(gpd, gpl, gps);
- gps->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
- BKE_gpencil_stroke_geometry_update(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
}
bGPDcurve *gpc = gps->editcurve;
@@ -3388,3 +3532,34 @@ void ED_gpencil_stroke_close_by_distance(bGPDstroke *gps, const float threshold)
BKE_gpencil_stroke_close(gps);
}
}
+
+/* Convert 3D point to 2D point in screen space. */
+bool ED_gpencil_3d_point_to_screen_space(struct ARegion *region,
+ const struct rcti *rect,
+ const float diff_mat[4][4],
+ const float co[3],
+ int r_co[2])
+{
+ float parent_co[3];
+ mul_v3_m4v3(parent_co, diff_mat, co);
+ int screen_co[2];
+ if (ED_view3d_project_int_global(
+ region, parent_co, screen_co, V3D_PROJ_RET_CLIP_BB | V3D_PROJ_RET_CLIP_WIN) ==
+ V3D_PROJ_RET_OK) {
+ if (!ELEM(V2D_IS_CLIPPED, screen_co[0], screen_co[1])) {
+ if (rect == NULL) {
+ copy_v2_v2_int(r_co, screen_co);
+ return true;
+ }
+ else {
+ if (BLI_rcti_isect_pt(rect, screen_co[0], screen_co[1])) {
+ copy_v2_v2_int(r_co, screen_co);
+ return true;
+ }
+ }
+ }
+ }
+ r_co[0] = V2D_IS_CLIPPED;
+ r_co[1] = V2D_IS_CLIPPED;
+ return false;
+}
diff --git a/source/blender/editors/gpencil/gpencil_uv.c b/source/blender/editors/gpencil/gpencil_uv.c
index 6bd0540a9d4..64182115b20 100644
--- a/source/blender/editors/gpencil/gpencil_uv.c
+++ b/source/blender/editors/gpencil/gpencil_uv.c
@@ -273,7 +273,7 @@ static bool gpencil_uv_transform_calc(bContext *C, wmOperator *op)
changed = true;
/* Calc geometry data. */
- BKE_gpencil_stroke_geometry_update(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
i++;
}
}
@@ -291,7 +291,7 @@ static bool gpencil_uv_transform_calc(bContext *C, wmOperator *op)
gps->uv_rotation = opdata->array_rot[i] - uv_rotation;
/* Calc geometry data. */
- BKE_gpencil_stroke_geometry_update(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
i++;
}
}
@@ -316,7 +316,7 @@ static bool gpencil_uv_transform_calc(bContext *C, wmOperator *op)
if (gps->flag & GP_STROKE_SELECT) {
gps->uv_scale = opdata->array_scale[i] + scale;
/* Calc geometry data. */
- BKE_gpencil_stroke_geometry_update(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
i++;
}
}
@@ -512,7 +512,7 @@ static int gpencil_reset_transform_fill_exec(bContext *C, wmOperator *op)
gps->uv_scale = 1.0f;
}
/* Calc geometry data. */
- BKE_gpencil_stroke_geometry_update(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
changed = true;
}
}
diff --git a/source/blender/editors/gpencil/gpencil_vertex_ops.c b/source/blender/editors/gpencil/gpencil_vertex_ops.c
index 402bccce2f7..c68d81f8a6b 100644
--- a/source/blender/editors/gpencil/gpencil_vertex_ops.c
+++ b/source/blender/editors/gpencil/gpencil_vertex_ops.c
@@ -59,7 +59,7 @@ static const EnumPropertyItem gpencil_modesEnumPropertyItem_mode[] = {
};
/* Helper: Check if any stroke is selected. */
-static bool is_any_stroke_selected(bContext *C, const bool is_multiedit, const bool is_curve_edit)
+static bool is_any_stroke_selected(bContext *C, const bool is_multiedit)
{
bool is_selected = false;
@@ -82,10 +82,7 @@ static bool is_any_stroke_selected(bContext *C, const bool is_multiedit, const b
continue;
}
- if (is_curve_edit) {
- if (gps->editcurve == NULL) {
- continue;
- }
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps)) {
bGPDcurve *gpc = gps->editcurve;
if (gpc->flag & GP_CURVE_SELECT) {
is_selected = true;
@@ -136,7 +133,7 @@ static int gpencil_vertexpaint_brightness_contrast_exec(bContext *C, wmOperator
bGPdata *gpd = (bGPdata *)ob->data;
const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
const eGp_Vertex_Mode mode = RNA_enum_get(op->ptr, "mode");
- const bool any_selected = is_any_stroke_selected(C, is_multiedit, false);
+ const bool any_selected = is_any_stroke_selected(C, is_multiedit);
float gain, offset;
{
@@ -254,7 +251,7 @@ static int gpencil_vertexpaint_hsv_exec(bContext *C, wmOperator *op)
const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
const eGp_Vertex_Mode mode = RNA_enum_get(op->ptr, "mode");
- const bool any_selected = is_any_stroke_selected(C, is_multiedit, false);
+ const bool any_selected = is_any_stroke_selected(C, is_multiedit);
float hue = RNA_float_get(op->ptr, "h");
float sat = RNA_float_get(op->ptr, "s");
float val = RNA_float_get(op->ptr, "v");
@@ -373,7 +370,7 @@ static int gpencil_vertexpaint_invert_exec(bContext *C, wmOperator *op)
const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
const eGp_Vertex_Mode mode = RNA_enum_get(op->ptr, "mode");
- const bool any_selected = is_any_stroke_selected(C, is_multiedit, false);
+ const bool any_selected = is_any_stroke_selected(C, is_multiedit);
bool changed = false;
CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
@@ -463,7 +460,7 @@ static int gpencil_vertexpaint_levels_exec(bContext *C, wmOperator *op)
const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
const eGp_Vertex_Mode mode = RNA_enum_get(op->ptr, "mode");
- const bool any_selected = is_any_stroke_selected(C, is_multiedit, false);
+ const bool any_selected = is_any_stroke_selected(C, is_multiedit);
float gain = RNA_float_get(op->ptr, "gain");
float offset = RNA_float_get(op->ptr, "offset");
@@ -563,7 +560,7 @@ static int gpencil_vertexpaint_set_exec(bContext *C, wmOperator *op)
const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
const eGp_Vertex_Mode mode = RNA_enum_get(op->ptr, "mode");
- const bool any_selected = is_any_stroke_selected(C, is_multiedit, false);
+ const bool any_selected = is_any_stroke_selected(C, is_multiedit);
float factor = RNA_float_get(op->ptr, "factor");
bool changed = false;
@@ -1060,6 +1057,14 @@ static void gpencil_reset_vertex(bGPDstroke *gps, eGp_Vertex_Mode mode)
}
if (mode != GPPAINT_MODE_FILL) {
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps)) {
+ bGPDcurve *gpc = gps->editcurve;
+ for (int i = 0; i < gpc->tot_curve_points; i++) {
+ bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
+ zero_v4(gpc_pt->vert_color);
+ }
+ }
+
bGPDspoint *pt;
for (int i = 0; i < gps->totpoints; i++) {
pt = &gps->points[i];
@@ -1072,12 +1077,11 @@ static int gpencil_stroke_reset_vertex_color_exec(bContext *C, wmOperator *op)
{
Object *obact = CTX_data_active_object(C);
bGPdata *gpd = (bGPdata *)obact->data;
- const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
const eGp_Vertex_Mode mode = RNA_enum_get(op->ptr, "mode");
/* First need to check if there are something selected. If not, apply to all strokes. */
- const bool any_selected = is_any_stroke_selected(C, is_multiedit, is_curve_edit);
+ const bool any_selected = is_any_stroke_selected(C, is_multiedit);
/* Reset Vertex colors. */
bool changed = false;
@@ -1096,10 +1100,7 @@ static int gpencil_stroke_reset_vertex_color_exec(bContext *C, wmOperator *op)
continue;
}
- if (is_curve_edit) {
- if (gps->editcurve == NULL) {
- continue;
- }
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps)) {
bGPDcurve *gpc = gps->editcurve;
if ((!any_selected) || (gpc->flag & GP_CURVE_SELECT)) {
gpencil_reset_vertex(gps, mode);
diff --git a/source/blender/editors/gpencil/gpencil_vertex_paint.c b/source/blender/editors/gpencil/gpencil_vertex_paint.c
index 16605b6c634..7694dc1ce91 100644
--- a/source/blender/editors/gpencil/gpencil_vertex_paint.c
+++ b/source/blender/editors/gpencil/gpencil_vertex_paint.c
@@ -37,6 +37,7 @@
#include "BKE_colortools.h"
#include "BKE_context.h"
#include "BKE_gpencil.h"
+#include "BKE_gpencil_geom.h"
#include "BKE_material.h"
#include "BKE_report.h"
@@ -435,20 +436,40 @@ static bool brush_tint_apply(tGP_BrushVertexpaintData *gso,
/* Apply color to Stroke point. */
if (GPENCIL_TINT_VERTEX_COLOR_STROKE(brush) && (pt_index > -1)) {
- bGPDspoint *pt = &gps->points[pt_index];
- if (brush_invert_check(gso)) {
- pt->vert_color[3] -= inf;
- CLAMP_MIN(pt->vert_color[3], 0.0f);
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps)) {
+ bGPDcurve_point *pt = &gps->editcurve->curve_points[pt_index];
+ if (brush_invert_check(gso)) {
+ pt->vert_color[3] -= inf;
+ CLAMP_MIN(pt->vert_color[3], 0.0f);
+ }
+ else {
+ /* Premult. */
+ mul_v3_fl(pt->vert_color, pt->vert_color[3]);
+ /* "Alpha over" blending. */
+ interp_v3_v3v3(pt->vert_color, pt->vert_color, gso->linear_color, inf);
+ pt->vert_color[3] = pt->vert_color[3] * (1.0 - inf) + inf;
+ /* Un-premult. */
+ if (pt->vert_color[3] > 0.0f) {
+ mul_v3_fl(pt->vert_color, 1.0f / pt->vert_color[3]);
+ }
+ }
}
else {
- /* Premult. */
- mul_v3_fl(pt->vert_color, pt->vert_color[3]);
- /* "Alpha over" blending. */
- interp_v3_v3v3(pt->vert_color, pt->vert_color, gso->linear_color, inf);
- pt->vert_color[3] = pt->vert_color[3] * (1.0 - inf) + inf;
- /* Un-premult. */
- if (pt->vert_color[3] > 0.0f) {
- mul_v3_fl(pt->vert_color, 1.0f / pt->vert_color[3]);
+ bGPDspoint *pt = &gps->points[pt_index];
+ if (brush_invert_check(gso)) {
+ pt->vert_color[3] -= inf;
+ CLAMP_MIN(pt->vert_color[3], 0.0f);
+ }
+ else {
+ /* Premult. */
+ mul_v3_fl(pt->vert_color, pt->vert_color[3]);
+ /* "Alpha over" blending. */
+ interp_v3_v3v3(pt->vert_color, pt->vert_color, gso->linear_color, inf);
+ pt->vert_color[3] = pt->vert_color[3] * (1.0 - inf) + inf;
+ /* Un-premult. */
+ if (pt->vert_color[3] > 0.0f) {
+ mul_v3_fl(pt->vert_color, 1.0f / pt->vert_color[3]);
+ }
}
}
}
@@ -820,9 +841,71 @@ static void gpencil_save_selected_point(tGP_BrushVertexpaintData *gso,
gso->pbuffer_used++;
}
+static void gpencil_vertexpaint_select_curve(tGP_BrushVertexpaintData *gso,
+ bGPDstroke *gps,
+ const char tool,
+ const float diff_mat[4][4],
+ const float bound_mat[4][4])
+{
+ ARegion *region = gso->region;
+ GP_SpaceConversion *gsc = &gso->gsc;
+ rcti *rect = &gso->brush_rect;
+ Brush *brush = gso->brush;
+ const int radius = (brush->flag & GP_BRUSH_USE_PRESSURE) ? gso->brush->size * gso->pressure :
+ gso->brush->size;
+ bGPDstroke *gps_active = (gps->runtime.gps_orig) ? gps->runtime.gps_orig : gps;
+ bGPDcurve *gpc = gps->editcurve;
+
+ /* Check stroke masking. */
+ if (GPENCIL_ANY_VERTEX_MASK(gso->mask)) {
+ if ((gpc->flag & GP_CURVE_SELECT) == 0) {
+ return;
+ }
+ }
+
+ /* Check if the stroke collide with brush. */
+ if (!ED_gpencil_stroke_check_collision(gsc, gps, gso->mval, radius, bound_mat)) {
+ return;
+ }
+
+ if (gpc->tot_curve_points == 1) {
+ return;
+ }
+
+ /* If the curve has more than one control point... */
+ for (int i = 0; i < gpc->tot_curve_points; i++) {
+ bGPDcurve_point *cpt = &gpc->curve_points[i];
+ BezTriple *bezt = &cpt->bezt;
+
+ int screen_co[2];
+ /* Test if points can be projected. */
+ if (!ED_gpencil_3d_point_to_screen_space(region, rect, diff_mat, bezt->vec[1], screen_co)) {
+ continue;
+ }
+
+ float co[2] = {(float)screen_co[0], (float)screen_co[1]};
+ /* Test if the point is in the circle. */
+ if (len_v2v2(gso->mval, co) > radius) {
+ continue;
+ }
+
+ bGPDcurve_point *cpt_active = NULL;
+ int index = -1;
+ if (cpt->runtime.gpc_pt_orig) {
+ cpt_active = cpt->runtime.gpc_pt_orig;
+ index = cpt->runtime.idx_orig;
+ }
+ else {
+ cpt_active = cpt;
+ index = i;
+ }
+ gpencil_save_selected_point(gso, gps_active, index, screen_co);
+ }
+}
+
/* Select points in this stroke and add to an array to be used later.
* Returns true if any point was hit and got saved */
-static bool gpencil_vertexpaint_select_stroke(tGP_BrushVertexpaintData *gso,
+static void gpencil_vertexpaint_select_stroke(tGP_BrushVertexpaintData *gso,
bGPDstroke *gps,
const char tool,
const float diff_mat[4][4],
@@ -849,13 +932,13 @@ static bool gpencil_vertexpaint_select_stroke(tGP_BrushVertexpaintData *gso,
/* Check stroke masking. */
if (GPENCIL_ANY_VERTEX_MASK(gso->mask)) {
if ((gps->flag & GP_STROKE_SELECT) == 0) {
- return false;
+ return;
}
}
/* Check if the stroke collide with brush. */
if (!ED_gpencil_stroke_check_collision(gsc, gps, gso->mval, radius, bound_mat)) {
- return false;
+ return;
}
if (gps->totpoints == 1) {
@@ -990,8 +1073,6 @@ static bool gpencil_vertexpaint_select_stroke(tGP_BrushVertexpaintData *gso,
}
}
}
-
- return saved;
}
/* Apply vertex paint brushes to strokes in the given frame. */
@@ -1027,12 +1108,11 @@ static bool gpencil_vertexpaint_brush_do_frame(bContext *C,
}
/* Check points below the brush. */
- bool hit = gpencil_vertexpaint_select_stroke(gso, gps, tool, diff_mat, bound_mat);
-
- /* If stroke was hit and has an editcurve the curve needs an update. */
- bGPDstroke *gps_active = (gps->runtime.gps_orig) ? gps->runtime.gps_orig : gps;
- if (gps_active->editcurve != NULL && hit) {
- gps_active->editcurve->flag |= GP_CURVE_NEEDS_STROKE_UPDATE;
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps)) {
+ gpencil_vertexpaint_select_curve(gso, gps, tool, diff_mat, bound_mat);
+ }
+ else {
+ gpencil_vertexpaint_select_stroke(gso, gps, tool, diff_mat, bound_mat);
}
}
@@ -1106,6 +1186,10 @@ static bool gpencil_vertexpaint_brush_do_frame(bContext *C,
printf("ERROR: Unknown type of GPencil Vertex Paint brush\n");
break;
}
+
+ if (changed && GPENCIL_STROKE_TYPE_BEZIER(selected->gps)) {
+ BKE_gpencil_stroke_geometry_update(gso->gpd, selected->gps, GP_GEO_UPDATE_POLYLINE_COLOR);
+ }
}
/* Clear the selected array, but keep the memory allocation. */
gso->pbuffer = gpencil_select_buffer_ensure(
diff --git a/source/blender/editors/gpencil/gpencil_weight_paint.c b/source/blender/editors/gpencil/gpencil_weight_paint.c
index 6d953a4d8cf..8ba23f7678f 100644
--- a/source/blender/editors/gpencil/gpencil_weight_paint.c
+++ b/source/blender/editors/gpencil/gpencil_weight_paint.c
@@ -37,6 +37,7 @@
#include "BKE_context.h"
#include "BKE_deform.h"
#include "BKE_gpencil.h"
+#include "BKE_gpencil_geom.h"
#include "BKE_main.h"
#include "BKE_object_deform.h"
#include "BKE_report.h"
@@ -237,8 +238,15 @@ static bool brush_draw_apply(tGP_BrushWeightpaintData *gso,
/* create dvert */
BKE_gpencil_dvert_ensure(gps);
- MDeformVert *dvert = gps->dvert + pt_index;
+ MDeformVert *dvert;
float inf;
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps)) {
+ bGPDcurve *gpc = gps->editcurve;
+ dvert = gpc->dvert + pt_index;
+ }
+ else {
+ dvert = gps->dvert + pt_index;
+ }
/* Compute strength of effect */
inf = brush_influence_calc(gso, radius, co);
@@ -365,7 +373,14 @@ static void gpencil_save_selected_point(tGP_BrushWeightpaintData *gso,
int pc[2])
{
tGP_Selected *selected;
- bGPDspoint *pt = &gps->points[index];
+ bGPDspoint *pt = NULL;
+ bGPDcurve_point *cpt = NULL;
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps)) {
+ cpt = &gps->editcurve->curve_points[index];
+ }
+ else {
+ pt = &gps->points[index];
+ }
/* Ensure the array to save the list of selected points is big enough. */
gso->pbuffer = gpencil_select_buffer_ensure(
@@ -375,11 +390,65 @@ static void gpencil_save_selected_point(tGP_BrushWeightpaintData *gso,
selected->gps = gps;
selected->pt_index = index;
copy_v2_v2_int(selected->pc, pc);
- copy_v4_v4(selected->color, pt->vert_color);
+ copy_v4_v4(selected->color, (cpt != NULL) ? cpt->vert_color : pt->vert_color);
gso->pbuffer_used++;
}
+static void gpencil_weightpaint_select_curve(tGP_BrushWeightpaintData *gso,
+ bGPDstroke *gps,
+ const float diff_mat[4][4],
+ const float bound_mat[4][4])
+{
+ ARegion *region = gso->region;
+ GP_SpaceConversion *gsc = &gso->gsc;
+ rcti *rect = &gso->brush_rect;
+ Brush *brush = gso->brush;
+ const int radius = (brush->flag & GP_BRUSH_USE_PRESSURE) ? gso->brush->size * gso->pressure :
+ gso->brush->size;
+ bGPDstroke *gps_active = (gps->runtime.gps_orig) ? gps->runtime.gps_orig : gps;
+ bGPDcurve *gpc = gps->editcurve;
+
+ /* Check if the stroke collide with brush. */
+ if (!ED_gpencil_stroke_check_collision(gsc, gps, gso->mval, radius, bound_mat)) {
+ return;
+ }
+
+ if (gpc->tot_curve_points == 1) {
+ return;
+ }
+
+ /* If the curve has more than one control point... */
+ for (int i = 0; i < gpc->tot_curve_points; i++) {
+ bGPDcurve_point *cpt = &gpc->curve_points[i];
+ BezTriple *bezt = &cpt->bezt;
+
+ int screen_co[2];
+ /* Test if points can be projected. */
+ if (!ED_gpencil_3d_point_to_screen_space(region, rect, diff_mat, bezt->vec[1], screen_co)) {
+ continue;
+ }
+
+ float co[2] = {(float)screen_co[0], (float)screen_co[1]};
+ /* Test if the point is in the circle. */
+ if (len_v2v2(gso->mval, co) > radius) {
+ continue;
+ }
+
+ bGPDcurve_point *cpt_active = NULL;
+ int index = -1;
+ if (cpt->runtime.gpc_pt_orig) {
+ cpt_active = cpt->runtime.gpc_pt_orig;
+ index = cpt->runtime.idx_orig;
+ }
+ else {
+ cpt_active = cpt;
+ index = i;
+ }
+ gpencil_save_selected_point(gso, gps_active, index, screen_co);
+ }
+}
+
/* Select points in this stroke and add to an array to be used later. */
static void gpencil_weightpaint_select_stroke(tGP_BrushWeightpaintData *gso,
bGPDstroke *gps,
@@ -531,9 +600,13 @@ static bool gpencil_weightpaint_brush_do_frame(bContext *C,
if (ED_gpencil_stroke_material_editable(ob, gpl, gps) == false) {
continue;
}
-
- /* Check points below the brush. */
- gpencil_weightpaint_select_stroke(gso, gps, diff_mat, bound_mat);
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps)) {
+ gpencil_weightpaint_select_curve(gso, gps, diff_mat, bound_mat);
+ }
+ else {
+ /* Check points below the brush. */
+ gpencil_weightpaint_select_stroke(gso, gps, diff_mat, bound_mat);
+ }
}
/*---------------------------------------------------------------------
@@ -541,7 +614,6 @@ static bool gpencil_weightpaint_brush_do_frame(bContext *C,
*--------------------------------------------------------------------- */
bool changed = false;
for (i = 0; i < gso->pbuffer_used; i++) {
- changed = true;
selected = &gso->pbuffer[i];
switch (tool) {
@@ -554,6 +626,10 @@ static bool gpencil_weightpaint_brush_do_frame(bContext *C,
printf("ERROR: Unknown type of GPencil Weight Paint brush\n");
break;
}
+
+ if (changed && GPENCIL_STROKE_TYPE_BEZIER(selected->gps)) {
+ BKE_gpencil_stroke_geometry_update(gso->gpd, selected->gps, GP_GEO_UPDATE_POLYLINE_WEIGHT);
+ }
}
/* Clear the selected array, but keep the memory allocation. */
gso->pbuffer = gpencil_select_buffer_ensure(
diff --git a/source/blender/editors/include/ED_gpencil.h b/source/blender/editors/include/ED_gpencil.h
index 59b5a1abaa6..ca10f240ffa 100644
--- a/source/blender/editors/include/ED_gpencil.h
+++ b/source/blender/editors/include/ED_gpencil.h
@@ -52,6 +52,7 @@ struct SnapObjectContext;
struct ToolSettings;
struct View3D;
struct bContext;
+struct rcti;
struct Material;
struct Object;
@@ -295,6 +296,11 @@ void ED_gpencil_stroke_reproject(struct Depsgraph *depsgraph,
struct bGPDstroke *gps,
const eGP_ReprojectModes mode,
const bool keep_original);
+bool ED_gpencil_3d_point_to_screen_space(struct ARegion *region,
+ const struct rcti *rect,
+ const float diff_mat[4][4],
+ const float co[3],
+ int r_co[2]);
/* set sculpt cursor */
void ED_gpencil_toggle_brush_cursor(struct bContext *C, bool enable, void *customdata);
diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c
index 39de63c22a9..8825be6d60e 100644
--- a/source/blender/editors/screen/area.c
+++ b/source/blender/editors/screen/area.c
@@ -1761,10 +1761,6 @@ static void ed_default_handlers(
wmKeyMap *keymap_general = WM_keymap_ensure(wm->defaultconf, "Grease Pencil", 0, 0);
WM_event_add_keymap_handler(handlers, keymap_general);
- wmKeyMap *keymap_curve_edit = WM_keymap_ensure(
- wm->defaultconf, "Grease Pencil Stroke Curve Edit Mode", 0, 0);
- WM_event_add_keymap_handler(handlers, keymap_curve_edit);
-
wmKeyMap *keymap_edit = WM_keymap_ensure(
wm->defaultconf, "Grease Pencil Stroke Edit Mode", 0, 0);
WM_event_add_keymap_handler(handlers, keymap_edit);
diff --git a/source/blender/editors/transform/transform_convert_gpencil.c b/source/blender/editors/transform/transform_convert_gpencil.c
index f7b78b10868..7e64d9dea3d 100644
--- a/source/blender/editors/transform/transform_convert_gpencil.c
+++ b/source/blender/editors/transform/transform_convert_gpencil.c
@@ -95,54 +95,77 @@ static short get_bezt_sel_triple_flag(BezTriple *bezt, const bool handles_visibl
return flag;
}
-static void createTransGPencil_curves(bContext *C,
- TransInfo *t,
- Depsgraph *depsgraph,
- ToolSettings *ts,
- Object *obact,
- bGPdata *gpd,
- const int cfra_scene,
- const bool is_multiedit,
- const bool use_multiframe_falloff,
- const bool is_prop_edit,
- const bool is_prop_edit_connected,
- const bool is_scale_thickness)
+static void createTransGPencil_strokes(TransInfo *t,
+ Depsgraph *depsgraph,
+ ToolSettings *ts,
+ Object *obact,
+ bGPdata *gpd,
+ const int cfra_scene,
+ const bool is_multiedit,
+ const bool use_multiframe_falloff,
+ const bool is_prop_edit,
+ const bool is_prop_edit_connected,
+ const bool is_scale_thickness)
{
#define SEL_F1 (1 << 0)
#define SEL_F2 (1 << 1)
#define SEL_F3 (1 << 2)
-
View3D *v3d = t->view;
- Scene *scene = CTX_data_scene(C);
+ Scene *scene = t->scene;
+ TransData *td = NULL;
+ float mtx[3][3], smtx[3][3];
+
const bool handle_only_selected_visible = (v3d->overlay.handle_display == CURVE_HANDLE_SELECTED);
const bool handle_all_visible = (v3d->overlay.handle_display == CURVE_HANDLE_ALL);
TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t);
+ /* == Grease Pencil Strokes to Transform Data ==
+ * Grease Pencil stroke points can be a mixture of 2D (screen-space),
+ * or 3D coordinates. However, they're always saved as 3D points.
+ * For now, we just do these without creating TransData2D for the 2D
+ * strokes. This may cause issues in future though.
+ */
tc->data_len = 0;
- /* Number of selected curve points */
+ /* First Pass: Count the number of data-points required for the strokes,
+ * (and additional info about the configuration - e.g. 2D/3D?).
+ */
uint32_t tot_curve_points = 0, tot_sel_curve_points = 0, tot_points = 0, tot_sel_points = 0;
LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
/* Only editable and visible layers are considered. */
- if (BKE_gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) {
- bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe;
- for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
- if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) {
- LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
- /* skip strokes that are invalid for current view */
- if (ED_gpencil_stroke_can_use(C, gps) == false) {
- continue;
- }
- /* Check if the color is editable. */
- if (ED_gpencil_stroke_material_editable(obact, gpl, gps) == false) {
- continue;
+ if (!BKE_gpencil_layer_is_editable(gpl) || (gpl->actframe == NULL)) {
+ continue;
+ }
+
+ bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe;
+ for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
+ if ((gpf != gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) == 0 && (is_multiedit))) {
+ continue;
+ }
+ LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
+ /* skip strokes that are invalid for current view */
+ if (ED_gpencil_stroke_can_use(t->context, gps) == false) {
+ continue;
+ }
+ /* Check if the color is editable. */
+ if (ED_gpencil_stroke_material_editable(obact, gpl, gps) == false) {
+ continue;
+ }
+
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps)) {
+ bGPDcurve *gpc = gps->editcurve;
+ if (is_prop_edit) {
+ /* Add all points if prop edit or prop edit connected + stroke is selected. */
+ if (is_prop_edit_connected && gpc->flag & GP_CURVE_SELECT) {
+ tot_curve_points += gpc->tot_curve_points;
+ tot_points += gpc->tot_curve_points * 3;
}
- /* Check if stroke has an editcurve */
- if (gps->editcurve == NULL) {
- continue;
+ else {
+ tot_curve_points += gpc->tot_curve_points;
+ tot_points += gpc->tot_curve_points * 3;
}
-
- bGPDcurve *gpc = gps->editcurve;
+ }
+ else if (gpc->flag & GP_CURVE_SELECT) {
for (int i = 0; i < gpc->tot_curve_points; i++) {
bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
BezTriple *bezt = &gpc_pt->bezt;
@@ -167,60 +190,66 @@ static void createTransGPencil_curves(bContext *C,
}
tot_sel_curve_points++;
}
-
- if (is_prop_edit) {
- tot_points += 3;
- tot_curve_points++;
- }
}
}
}
-
- /* If not multi-edit out of loop. */
- if (!is_multiedit) {
- break;
+ else {
+ if (is_prop_edit) {
+ /* Add all points if prop edit or prop edit connected + stroke is selected. */
+ if (is_prop_edit_connected && gps->flag & GP_STROKE_SELECT) {
+ tot_points += gps->totpoints;
+ }
+ else {
+ tot_points += gps->totpoints;
+ }
+ }
+ else if (gps->flag & GP_STROKE_SELECT) {
+ /* Only selected stroke points are considered. */
+ for (int i = 0; i < gps->totpoints; i++) {
+ bGPDspoint *pt = &gps->points[i];
+ if (pt->flag & GP_SPOINT_SELECT) {
+ tot_sel_points++;
+ }
+ }
+ }
}
}
+ /* If not multiedit out of loop. */
+ if (!is_multiedit) {
+ break;
+ }
}
}
- if (((is_prop_edit && !is_prop_edit_connected) ? tot_curve_points : tot_sel_points) == 0) {
- tc->data_len = 0;
- return;
- }
-
- int data_len_pt = 0;
-
- if (is_prop_edit) {
- tc->data_len = tot_points;
- data_len_pt = tot_curve_points;
- }
- else {
- tc->data_len = tot_sel_points;
- data_len_pt = tot_sel_curve_points;
- }
+ tc->data_len = (is_prop_edit || is_prop_edit_connected) ? tot_points : tot_sel_points;
+ /* Stop trying if nothing selected. */
if (tc->data_len == 0) {
return;
}
- transform_around_single_fallback_ex(t, data_len_pt);
+ transform_around_single_fallback_ex(t, tc->data_len);
- tc->data = MEM_callocN(tc->data_len * sizeof(TransData), __func__);
- TransData *td = tc->data;
+ /* Allocate memory for data */
+ tc->data = MEM_callocN(tc->data_len * sizeof(TransData), "TransData(GPencil)");
+ td = tc->data;
+
+ unit_m3(smtx);
+ unit_m3(mtx);
const bool use_around_origins_for_handles_test = ((t->around == V3D_AROUND_LOCAL_ORIGINS) &&
transform_mode_use_local_origins(t));
+ /* Second Pass: Build transdata array. */
LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
- /* Only editable and visible layers are considered. */
+ /* only editable and visible layers are considered */
if (BKE_gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) {
const int cfra = (gpl->flag & GP_LAYER_FRAMELOCK) ? gpl->actframe->framenum : cfra_scene;
bGPDframe *gpf = gpl->actframe;
- bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe;
- float diff_mat[4][4], mtx[3][3];
- float smtx[3][3];
+ float diff_mat[4][4];
+ // float inverse_diff_mat[4][4];
+ bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe;
/* Init multiframe falloff options. */
int f_init = 0;
int f_end = 0;
@@ -229,6 +258,12 @@ static void createTransGPencil_curves(bContext *C,
BKE_gpencil_frame_range_selected(gpl, &f_init, &f_end);
}
+ /* Make a new frame to work on if the layer's frame
+ * and the current scene frame don't match up.
+ *
+ * - This is useful when animating as it saves that "uh-oh" moment when you realize you've
+ * spent too much time editing the wrong frame...
+ */
if ((gpf->framenum != cfra) && (!is_multiedit)) {
if (IS_AUTOKEY_ON(scene)) {
gpf = BKE_gpencil_frame_addcopy(gpl, cfra);
@@ -244,41 +279,75 @@ static void createTransGPencil_curves(bContext *C,
/* Calculate difference matrix. */
BKE_gpencil_layer_transform_matrix_get(depsgraph, obact, gpl, diff_mat);
+ // /* Undo matrix. */
+ // invert_m4_m4(inverse_diff_mat, diff_mat);
+
copy_m3_m4(mtx, diff_mat);
pseudoinverse_m3_m3(smtx, mtx, PSEUDOINVERSE_EPSILON);
+ /* Loop over strokes, adding TransData for points as needed... */
for (gpf = init_gpf; gpf; gpf = gpf->next) {
- if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) {
- /* If multi-frame and falloff, recalculate and save value. */
- float falloff = 1.0f; /* by default no falloff */
- if ((is_multiedit) && (use_multiframe_falloff)) {
- /* Falloff depends on distance to active frame
- * (relative to the overall frame range). */
- falloff = BKE_gpencil_multiframe_falloff_calc(
- gpf, gpl->actframe->framenum, f_init, f_end, ts->gp_sculpt.cur_falloff);
+ if ((gpf != gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) == 0 && (is_multiedit))) {
+ continue;
+ }
+
+ /* If multi-frame and falloff, recalculate and save value. */
+ float falloff = 1.0f; /* by default no falloff */
+ if ((is_multiedit) && (use_multiframe_falloff)) {
+ /* Falloff depends on distance to active frame
+ * (relative to the overall frame range). */
+ falloff = BKE_gpencil_multiframe_falloff_calc(
+ gpf, gpl->actframe->framenum, f_init, f_end, ts->gp_sculpt.cur_falloff);
+ }
+
+ LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
+ TransData *head = td;
+ TransData *tail = td;
+ bool stroke_ok;
+
+ /* skip strokes that are invalid for current view */
+ if (ED_gpencil_stroke_can_use(t->context, gps) == false) {
+ continue;
+ }
+ /* check if the color is editable */
+ if (ED_gpencil_stroke_material_editable(obact, gpl, gps) == false) {
+ continue;
}
- LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
- /* skip strokes that are invalid for current view */
- if (ED_gpencil_stroke_can_use(C, gps) == false) {
- continue;
- }
- /* Check if the color is editable. */
- if (ED_gpencil_stroke_material_editable(obact, gpl, gps) == false) {
- continue;
+ bool stroke_select = GPENCIL_STROKE_TYPE_BEZIER(gps) ?
+ (gps->editcurve->flag & GP_CURVE_SELECT) :
+ (gps->flag & GP_STROKE_SELECT);
+ if (is_prop_edit) {
+ if (is_prop_edit_connected) {
+ /* A) "Connected" - Only those in selected strokes */
+ stroke_ok = stroke_select;
}
- /* Check if stroke has an editcurve */
- if (gps->editcurve == NULL) {
- continue;
+ else {
+ /* B) Proportinal All points, always */
+ stroke_ok = true;
}
- TransData *head, *tail;
- head = tail = td;
+ }
+ else {
+ /* C) Only selected points in selected strokes */
+ stroke_ok = stroke_select;
+ }
+
+ if (!stroke_ok || gps->totpoints == 0) {
+ continue;
+ }
- gps->runtime.multi_frame_falloff = falloff;
- bool need_handle_recalc = false;
+ /* save falloff factor */
+ gps->runtime.multi_frame_falloff = falloff;
+ /* calculate stroke center */
+ float center[3];
+ createTransGPencil_center_get(gps, center);
+
+ const bool is_cyclic = gps->flag & GP_STROKE_CYCLIC;
+ bool need_handle_recalc = false;
+
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps)) {
bGPDcurve *gpc = gps->editcurve;
- const bool is_cyclic = gps->flag & GP_STROKE_CYCLIC;
for (int i = 0; i < gpc->tot_curve_points; i++) {
bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
BezTriple *bezt = &gpc_pt->bezt;
@@ -297,13 +366,16 @@ static void createTransGPencil_curves(bContext *C,
bool is_ctrl_point = (j == 1);
bool sel = sel_flag & (1 << j);
- if (is_prop_edit || sel) {
- copy_v3_v3(td->iloc, bezt->vec[j]);
- td->loc = bezt->vec[j];
- bool rotate_around_ctrl = !handles_visible ||
- (t->around == V3D_AROUND_LOCAL_ORIGINS) ||
- (bezt->f2 & SELECT);
- copy_v3_v3(td->center, bezt->vec[rotate_around_ctrl ? 1 : j]);
+ if (!is_prop_edit && !sel) {
+ continue;
+ }
+
+ copy_v3_v3(td->iloc, bezt->vec[j]);
+ td->loc = bezt->vec[j];
+ bool rotate_around_ctrl = !handles_visible ||
+ (t->around == V3D_AROUND_LOCAL_ORIGINS) ||
+ (bezt->f2 & SELECT);
+ copy_v3_v3(td->center, bezt->vec[rotate_around_ctrl ? 1 : j]);
if (!handles_visible || is_ctrl_point) {
if (bezt->f2 & SELECT) {
@@ -322,44 +394,43 @@ static void createTransGPencil_curves(bContext *C,
}
}
- td->ext = NULL;
- if (is_ctrl_point) {
- if (t->mode != TFM_MIRROR) {
- if (t->mode != TFM_GPENCIL_OPACITY) {
- if (is_scale_thickness) {
- td->val = &(gpc_pt->pressure);
- td->ival = gpc_pt->pressure;
- }
- }
- else {
- td->val = &(gpc_pt->strength);
- td->ival = gpc_pt->strength;
+ td->ext = NULL;
+ if (is_ctrl_point) {
+ if (t->mode != TFM_MIRROR) {
+ if (t->mode != TFM_GPENCIL_OPACITY) {
+ if (is_scale_thickness) {
+ td->val = &(gpc_pt->pressure);
+ td->ival = gpc_pt->pressure;
}
}
+ else {
+ td->val = &(gpc_pt->strength);
+ td->ival = gpc_pt->strength;
+ }
}
- else {
- td->val = NULL;
- }
+ }
+ else {
+ td->val = NULL;
+ }
- if (hdata == NULL) {
- if (is_ctrl_point && ((sel_flag & SEL_F1 & SEL_F3) == 0)) {
- hdata = initTransDataCurveHandles(td, bezt);
- }
- else if (!is_ctrl_point) {
- hdata = initTransDataCurveHandles(td, bezt);
- }
+ if (hdata == NULL) {
+ if (is_ctrl_point && ((sel_flag & SEL_F1 & SEL_F3) == 0)) {
+ hdata = initTransDataCurveHandles(td, bezt);
}
+ else if (!is_ctrl_point) {
+ hdata = initTransDataCurveHandles(td, bezt);
+ }
+ }
- td->extra = gps;
- td->ob = obact;
+ td->extra = gps;
+ td->ob = obact;
- copy_m3_m3(td->smtx, smtx);
- copy_m3_m3(td->mtx, mtx);
- copy_m3_m3(td->axismtx, mtx);
+ copy_m3_m3(td->smtx, smtx);
+ copy_m3_m3(td->mtx, mtx);
+ copy_m3_m3(td->axismtx, mtx);
- td++;
- tail++;
- }
+ td++;
+ tail++;
bezt_use |= sel;
}
@@ -371,307 +442,97 @@ static void createTransGPencil_curves(bContext *C,
need_handle_recalc = true;
}
}
-
- if (is_prop_edit && (head != tail)) {
- calc_distanceCurveVerts(head, tail - 1, is_cyclic);
- }
-
- if (need_handle_recalc) {
- BKE_gpencil_editcurve_recalculate_handles(gps);
- }
}
- }
+ else {
+ /* add all necessary points... */
+ for (int i = 0; i < gps->totpoints; i++) {
+ bGPDspoint *pt = &gps->points[i];
+ bool point_ok;
- /* If not multi-edit out of loop. */
- if (!is_multiedit) {
- break;
- }
- }
- }
- }
-#undef SEL_F1
-#undef SEL_F2
-#undef SEL_F3
-}
-
-static void createTransGPencil_strokes(bContext *C,
- TransInfo *t,
- Depsgraph *depsgraph,
- ToolSettings *ts,
- Object *obact,
- bGPdata *gpd,
- const int cfra_scene,
- const bool is_multiedit,
- const bool use_multiframe_falloff,
- const bool is_prop_edit,
- const bool is_prop_edit_connected,
- const bool is_scale_thickness)
-{
- Scene *scene = CTX_data_scene(C);
- TransData *td = NULL;
- float mtx[3][3], smtx[3][3];
-
- TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t);
- /* == Grease Pencil Strokes to Transform Data ==
- * Grease Pencil stroke points can be a mixture of 2D (screen-space),
- * or 3D coordinates. However, they're always saved as 3D points.
- * For now, we just do these without creating TransData2D for the 2D
- * strokes. This may cause issues in future though.
- */
- tc->data_len = 0;
-
- /* First Pass: Count the number of data-points required for the strokes,
- * (and additional info about the configuration - e.g. 2D/3D?).
- */
- LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
- /* Only editable and visible layers are considered. */
- if (BKE_gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) {
- bGPDframe *gpf;
- bGPDstroke *gps;
- bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe;
-
- for (gpf = init_gpf; gpf; gpf = gpf->next) {
- if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) {
- for (gps = gpf->strokes.first; gps; gps = gps->next) {
- /* skip strokes that are invalid for current view */
- if (ED_gpencil_stroke_can_use(C, gps) == false) {
- continue;
- }
- /* Check if the color is editable. */
- if (ED_gpencil_stroke_material_editable(obact, gpl, gps) == false) {
- continue;
- }
-
- if (is_prop_edit) {
- /* Proportional Editing... */
- if (is_prop_edit_connected) {
- /* Connected only - so only if selected. */
- if (gps->flag & GP_STROKE_SELECT) {
- tc->data_len += gps->totpoints;
- }
+ /* include point? */
+ if (is_prop_edit) {
+ /* Always all points in strokes that get included. */
+ point_ok = true;
}
else {
- /* Everything goes - connection status doesn't matter. */
- tc->data_len += gps->totpoints;
- }
- }
- else {
- /* Only selected stroke points are considered. */
- if (gps->flag & GP_STROKE_SELECT) {
- bGPDspoint *pt;
- int i;
-
- for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
- if (pt->flag & GP_SPOINT_SELECT) {
- tc->data_len++;
- }
- }
+ /* Only selected points in selected strokes. */
+ point_ok = (pt->flag & GP_SPOINT_SELECT) != 0;
}
- }
- }
- }
- /* If not multi-edit out of loop. */
- if (!is_multiedit) {
- break;
- }
- }
- }
- }
-
- /* Stop trying if nothing selected. */
- if (tc->data_len == 0) {
- return;
- }
- /* Allocate memory for data */
- tc->data = MEM_callocN(tc->data_len * sizeof(TransData), "TransData(GPencil)");
- td = tc->data;
-
- unit_m3(smtx);
- unit_m3(mtx);
-
- /* Second Pass: Build transdata array. */
- LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
- /* only editable and visible layers are considered */
- if (BKE_gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) {
- const int cfra = (gpl->flag & GP_LAYER_FRAMELOCK) ? gpl->actframe->framenum : cfra_scene;
- bGPDframe *gpf = gpl->actframe;
- float diff_mat[4][4];
- float inverse_diff_mat[4][4];
-
- bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe;
- /* Init multiframe falloff options. */
- int f_init = 0;
- int f_end = 0;
-
- if (use_multiframe_falloff) {
- BKE_gpencil_frame_range_selected(gpl, &f_init, &f_end);
- }
-
- /* Calculate difference matrix. */
- BKE_gpencil_layer_transform_matrix_get(depsgraph, obact, gpl, diff_mat);
- /* Undo matrix. */
- invert_m4_m4(inverse_diff_mat, diff_mat);
-
- /* Make a new frame to work on if the layer's frame
- * and the current scene frame don't match up.
- *
- * - This is useful when animating as it saves that "uh-oh" moment when you realize you've
- * spent too much time editing the wrong frame...
- */
- if ((gpf->framenum != cfra) && (!is_multiedit)) {
- if (IS_AUTOKEY_ON(scene)) {
- gpf = BKE_gpencil_frame_addcopy(gpl, cfra);
- }
- /* in some weird situations (framelock enabled) return NULL */
- if (gpf == NULL) {
- continue;
- }
- if (!is_multiedit) {
- init_gpf = gpf;
- }
- }
-
- /* Loop over strokes, adding TransData for points as needed... */
- for (gpf = init_gpf; gpf; gpf = gpf->next) {
- if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) {
-
- /* If multi-frame and falloff, recalculate and save value. */
- float falloff = 1.0f; /* by default no falloff */
- if ((is_multiedit) && (use_multiframe_falloff)) {
- /* Falloff depends on distance to active frame
- * (relative to the overall frame range). */
- falloff = BKE_gpencil_multiframe_falloff_calc(
- gpf, gpl->actframe->framenum, f_init, f_end, ts->gp_sculpt.cur_falloff);
- }
-
- LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
- TransData *head = td;
- TransData *tail = td;
- bool stroke_ok;
-
- /* skip strokes that are invalid for current view */
- if (ED_gpencil_stroke_can_use(C, gps) == false) {
- continue;
- }
- /* check if the color is editable */
- if (ED_gpencil_stroke_material_editable(obact, gpl, gps) == false) {
- continue;
- }
- /* What we need to include depends on proportional editing settings... */
- if (is_prop_edit) {
- if (is_prop_edit_connected) {
- /* A) "Connected" - Only those in selected strokes */
- stroke_ok = (gps->flag & GP_STROKE_SELECT) != 0;
+ if (!point_ok) {
+ continue;
+ }
+ /* do point... */
+
+ copy_v3_v3(td->iloc, &pt->x);
+ /* Only copy center in local origins.
+ * This allows get interesting effects also when move
+ * using proportional editing. */
+ if ((gps->flag & GP_STROKE_SELECT) &&
+ (ts->transform_pivot_point == V3D_AROUND_LOCAL_ORIGINS)) {
+ copy_v3_v3(td->center, center);
}
else {
- /* B) All points, always */
- stroke_ok = true;
+ copy_v3_v3(td->center, &pt->x);
}
- }
- else {
- /* C) Only selected points in selected strokes */
- stroke_ok = (gps->flag & GP_STROKE_SELECT) != 0;
- }
-
- /* Do stroke... */
- if (stroke_ok && gps->totpoints) {
- bGPDspoint *pt;
- int i;
- /* save falloff factor */
- gps->runtime.multi_frame_falloff = falloff;
+ td->loc = &pt->x;
- /* calculate stroke center */
- float center[3];
- createTransGPencil_center_get(gps, center);
+ td->flag = 0;
- /* add all necessary points... */
- for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
- bool point_ok;
+ if (pt->flag & GP_SPOINT_SELECT) {
+ td->flag |= TD_SELECTED;
+ }
- /* include point? */
- if (is_prop_edit) {
- /* Always all points in strokes that get included. */
- point_ok = true;
+ /* For other transform modes (e.g. shrink-fatten), need to additional data
+ * but never for mirror.
+ */
+ if (t->mode != TFM_MIRROR) {
+ if (t->mode != TFM_GPENCIL_OPACITY) {
+ if (is_scale_thickness) {
+ td->val = &pt->pressure;
+ td->ival = pt->pressure;
+ }
}
else {
- /* Only selected points in selected strokes. */
- point_ok = (pt->flag & GP_SPOINT_SELECT) != 0;
+ td->val = &pt->strength;
+ td->ival = pt->strength;
}
+ }
- /* do point... */
- if (point_ok) {
- copy_v3_v3(td->iloc, &pt->x);
- /* Only copy center in local origins.
- * This allows get interesting effects also when move
- * using proportional editing. */
- if ((gps->flag & GP_STROKE_SELECT) &&
- (ts->transform_pivot_point == V3D_AROUND_LOCAL_ORIGINS)) {
- copy_v3_v3(td->center, center);
- }
- else {
- copy_v3_v3(td->center, &pt->x);
- }
-
- td->loc = &pt->x;
-
- td->flag = 0;
+ /* screenspace needs special matrices... */
+ if ((gps->flag & (GP_STROKE_3DSPACE | GP_STROKE_2DSPACE | GP_STROKE_2DIMAGE)) == 0) {
+ /* screenspace */
+ td->protectflag = OB_LOCK_LOCZ | OB_LOCK_ROTZ | OB_LOCK_SCALEZ;
+ }
+ else if (gps->flag & (GP_STROKE_2DSPACE | GP_STROKE_2DIMAGE)) {
+ /* configure 2D dataspace points so that they don't play up... */
+ td->protectflag = OB_LOCK_LOCZ | OB_LOCK_ROTZ | OB_LOCK_SCALEZ;
+ }
+ /* apply parent transformations */
+ copy_m3_m3(td->smtx, smtx); /* final position */
+ copy_m3_m3(td->mtx, mtx); /* display position */
+ copy_m3_m3(td->axismtx, mtx); /* axis orientation */
- if (pt->flag & GP_SPOINT_SELECT) {
- td->flag |= TD_SELECTED;
- }
+ /* Triangulation must be calculated again,
+ * so save the stroke for recalc function */
+ td->extra = gps;
- /* For other transform modes (e.g. shrink-fatten), need to additional data
- * but never for mirror.
- */
- if (t->mode != TFM_MIRROR) {
- if (t->mode != TFM_GPENCIL_OPACITY) {
- if (is_scale_thickness) {
- td->val = &pt->pressure;
- td->ival = pt->pressure;
- }
- }
- else {
- td->val = &pt->strength;
- td->ival = pt->strength;
- }
- }
+ /* save pointer to object */
+ td->ob = obact;
- /* screenspace needs special matrices... */
- if ((gps->flag & (GP_STROKE_3DSPACE | GP_STROKE_2DSPACE | GP_STROKE_2DIMAGE)) ==
- 0) {
- /* screenspace */
- td->protectflag = OB_LOCK_LOCZ | OB_LOCK_ROTZ | OB_LOCK_SCALEZ;
- }
- else {
- /* configure 2D dataspace points so that they don't play up... */
- if (gps->flag & (GP_STROKE_2DSPACE | GP_STROKE_2DIMAGE)) {
- td->protectflag = OB_LOCK_LOCZ | OB_LOCK_ROTZ | OB_LOCK_SCALEZ;
- }
- }
- /* apply parent transformations */
- copy_m3_m4(td->smtx, inverse_diff_mat); /* final position */
- copy_m3_m4(td->mtx, diff_mat); /* display position */
- copy_m3_m4(td->axismtx, diff_mat); /* axis orientation */
-
- /* Triangulation must be calculated again,
- * so save the stroke for recalc function */
- td->extra = gps;
-
- /* save pointer to object */
- td->ob = obact;
+ td++;
+ tail++;
+ }
+ }
- td++;
- tail++;
- }
- }
+ /* March over these points, and calculate the proportional editing distances */
+ if (is_prop_edit && (head != tail)) {
+ calc_distanceCurveVerts(head, tail - 1, is_cyclic);
+ }
- /* March over these points, and calculate the proportional editing distances */
- if (is_prop_edit && (head != tail)) {
- calc_distanceCurveVerts(head, tail - 1, false);
- }
- }
+ if (need_handle_recalc) {
+ BKE_gpencil_editcurve_recalculate_handles(gps);
}
}
/* If not multi-edit out of loop. */
@@ -681,6 +542,10 @@ static void createTransGPencil_strokes(bContext *C,
}
}
}
+
+#undef SEL_F1
+#undef SEL_F2
+#undef SEL_F3
}
void createTransGPencil(bContext *C, TransInfo *t)
@@ -707,8 +572,6 @@ void createTransGPencil(bContext *C, TransInfo *t)
const bool is_scale_thickness = ((t->mode == TFM_GPENCIL_SHRINKFATTEN) ||
(ts->gp_sculpt.flag & GP_SCULPT_SETT_FLAG_SCALE_THICKNESS));
- const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
-
/* initialize falloff curve */
if (is_multiedit) {
BKE_curvemapping_init(ts->gp_sculpt.cur_falloff);
@@ -718,34 +581,17 @@ void createTransGPencil(bContext *C, TransInfo *t)
return;
}
- if (is_curve_edit) {
- createTransGPencil_curves(C,
- t,
- depsgraph,
- ts,
- obact,
- gpd,
- cfra_scene,
- is_multiedit,
- use_multiframe_falloff,
- is_prop_edit,
- is_prop_edit_connected,
- is_scale_thickness);
- }
- else {
- createTransGPencil_strokes(C,
- t,
- depsgraph,
- ts,
- obact,
- gpd,
- cfra_scene,
- is_multiedit,
- use_multiframe_falloff,
- is_prop_edit,
- is_prop_edit_connected,
- is_scale_thickness);
- }
+ createTransGPencil_strokes(t,
+ depsgraph,
+ ts,
+ obact,
+ gpd,
+ cfra_scene,
+ is_multiedit,
+ use_multiframe_falloff,
+ is_prop_edit,
+ is_prop_edit_connected,
+ is_scale_thickness);
}
/* force recalculation of triangles during transformation */
@@ -756,18 +602,16 @@ void recalcData_gpencil_strokes(TransInfo *t)
TransData *td = tc->data;
bGPdata *gpd = td->ob->data;
- const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
for (int i = 0; i < tc->data_len; i++, td++) {
bGPDstroke *gps = td->extra;
if ((gps != NULL) && (!BLI_ghash_haskey(strokes, gps))) {
BLI_ghash_insert(strokes, gps, gps);
- if (is_curve_edit && gps->editcurve != NULL) {
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps)) {
BKE_gpencil_editcurve_recalculate_handles(gps);
- gps->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
}
/* Calc geometry data. */
- BKE_gpencil_stroke_geometry_update(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
}
}
BLI_ghash_free(strokes, NULL, NULL);
diff --git a/source/blender/editors/transform/transform_gizmo_3d.c b/source/blender/editors/transform/transform_gizmo_3d.c
index 080a19cce1f..fc35cf6eb15 100644
--- a/source/blender/editors/transform/transform_gizmo_3d.c
+++ b/source/blender/editors/transform/transform_gizmo_3d.c
@@ -653,7 +653,6 @@ int ED_transform_calc_gizmo_stats(const bContext *C,
Object *ob = OBACT(view_layer);
bGPdata *gpd = CTX_data_gpencil_data(C);
const bool is_gp_edit = GPENCIL_ANY_MODE(gpd);
- const bool is_curve_edit = GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
int a, totsel = 0;
const int pivot_point = scene->toolsettings->transform_pivot_point;
@@ -709,11 +708,7 @@ int ED_transform_calc_gizmo_stats(const bContext *C,
continue;
}
- if (is_curve_edit) {
- if (gps->editcurve == NULL) {
- continue;
- }
-
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps)) {
bGPDcurve *gpc = gps->editcurve;
if (gpc->flag & GP_CURVE_SELECT) {
for (uint32_t i = 0; i < gpc->tot_curve_points; i++) {
diff --git a/source/blender/editors/transform/transform_mode_gpopacity.c b/source/blender/editors/transform/transform_mode_gpopacity.c
index 7c496d271ef..0dcd4e4fd17 100644
--- a/source/blender/editors/transform/transform_mode_gpopacity.c
+++ b/source/blender/editors/transform/transform_mode_gpopacity.c
@@ -73,18 +73,18 @@ static void applyGPOpacity(TransInfo *t, const int UNUSED(mval[2]))
bool recalc = false;
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
TransData *td = tc->data;
- bGPdata *gpd = td->ob->data;
- const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
- /* Only recalculate data when in curve edit mode. */
- if (is_curve_edit) {
- recalc = true;
- }
for (i = 0; i < tc->data_len; i++, td++) {
if (td->flag & TD_SKIP) {
continue;
}
+ /* Only recalculate data for bezier strokes. */
+ bGPDstroke *gps = td->extra;
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps)) {
+ recalc = true;
+ }
+
if (td->val) {
*td->val = td->ival * ratio;
/* apply PET */
diff --git a/source/blender/editors/transform/transform_mode_gpshrinkfatten.c b/source/blender/editors/transform/transform_mode_gpshrinkfatten.c
index 608a49f38b1..897655ee674 100644
--- a/source/blender/editors/transform/transform_mode_gpshrinkfatten.c
+++ b/source/blender/editors/transform/transform_mode_gpshrinkfatten.c
@@ -73,18 +73,18 @@ static void applyGPShrinkFatten(TransInfo *t, const int UNUSED(mval[2]))
bool recalc = false;
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
TransData *td = tc->data;
- bGPdata *gpd = td->ob->data;
- const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
- /* Only recalculate data when in curve edit mode. */
- if (is_curve_edit) {
- recalc = true;
- }
for (i = 0; i < tc->data_len; i++, td++) {
if (td->flag & TD_SKIP) {
continue;
}
+ /* Only recalculate data for bezier strokes. */
+ bGPDstroke *gps = td->extra;
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps)) {
+ recalc = true;
+ }
+
if (td->val) {
*td->val = td->ival * ratio;
/* apply PET */
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c b/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c
index 6409c86b6e3..d588c87576d 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c
@@ -34,6 +34,7 @@
#include "DNA_object_types.h"
#include "BKE_deform.h"
+#include "BKE_gpencil_curve.h"
#include "BKE_gpencil_modifier.h"
#include "BKE_material.h"
@@ -138,8 +139,15 @@ bool is_stroke_affected_by_modifier(Object *ob,
}
}
/* need to have a minimum number of points */
- if ((minpoints > 0) && (gps->totpoints < minpoints)) {
- return false;
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps)) {
+ if ((minpoints > 0) && (gps->editcurve->tot_curve_points < minpoints)) {
+ return false;
+ }
+ }
+ else {
+ if ((minpoints > 0) && (gps->totpoints < minpoints)) {
+ return false;
+ }
}
return true;
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilarmature.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilarmature.c
index 08200b03466..4f068bef59e 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencilarmature.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilarmature.c
@@ -41,6 +41,7 @@
#include "BKE_armature.h"
#include "BKE_context.h"
#include "BKE_gpencil.h"
+#include "BKE_gpencil_curve.h"
#include "BKE_gpencil_geom.h"
#include "BKE_gpencil_modifier.h"
#include "BKE_lib_query.h"
@@ -77,7 +78,9 @@ static void copyData(const GpencilModifierData *md, GpencilModifierData *target)
BKE_gpencil_modifier_copydata_generic(md, target);
}
-static void gpencil_deform_verts(ArmatureGpencilModifierData *mmd, Object *target, bGPDstroke *gps)
+static void gpencil_deform_polyline_verts(ArmatureGpencilModifierData *mmd,
+ Object *target,
+ bGPDstroke *gps)
{
bGPDspoint *pt = gps->points;
float(*vert_coords)[3] = MEM_mallocN(sizeof(float[3]) * gps->totpoints, __func__);
@@ -110,8 +113,73 @@ static void gpencil_deform_verts(ArmatureGpencilModifierData *mmd, Object *targe
MEM_freeN(vert_coords);
}
+static void gpencil_deform_bezier_verts(ArmatureGpencilModifierData *mmd,
+ Object *target,
+ bGPDstroke *gps)
+{
+ bGPDcurve *gpc = gps->editcurve;
+ const int totpoints = gpc->tot_curve_points * 3;
+ float(*vert_coords)[3] = MEM_mallocN(sizeof(float[3]) * totpoints, __func__);
+
+ BKE_gpencil_dvert_ensure(gps);
+
+ /* prepare array of points */
+ for (int i = 0; i < gpc->tot_curve_points; i++) {
+ bGPDcurve_point *pt = &gpc->curve_points[i];
+ BezTriple *bezt = &pt->bezt;
+ int idx = i * 3;
+ copy_v3_v3(vert_coords[idx], bezt->vec[0]);
+ copy_v3_v3(vert_coords[idx + 1], bezt->vec[1]);
+ copy_v3_v3(vert_coords[idx + 2], bezt->vec[2]);
+ }
+
+ /* deform verts */
+ BKE_armature_deform_coords_with_gpencil_stroke(mmd->object,
+ target,
+ vert_coords,
+ NULL,
+ totpoints,
+ mmd->deformflag,
+ mmd->vert_coords_prev,
+ mmd->vgname,
+ gps);
+
+ /* Apply deformed coordinates */
+ for (int i = 0; i < gpc->tot_curve_points; i++) {
+ bGPDcurve_point *pt = &gpc->curve_points[i];
+ BezTriple *bezt = &pt->bezt;
+ int idx = i * 3;
+ copy_v3_v3(bezt->vec[0], vert_coords[idx]);
+ copy_v3_v3(bezt->vec[1], vert_coords[idx + 1]);
+ copy_v3_v3(bezt->vec[2], vert_coords[idx + 2]);
+ }
+
+ /* Recalculate the handles. */
+ BKE_gpencil_editcurve_recalculate_handles(gps);
+
+ MEM_freeN(vert_coords);
+}
+
/* deform stroke */
-static void deformStroke(GpencilModifierData *md,
+static void deformPolyline(GpencilModifierData *md,
+ Depsgraph *UNUSED(depsgraph),
+ Object *ob,
+ bGPDlayer *UNUSED(gpl),
+ bGPDframe *UNUSED(gpf),
+ bGPDstroke *gps)
+{
+ ArmatureGpencilModifierData *mmd = (ArmatureGpencilModifierData *)md;
+ if (!mmd->object) {
+ return;
+ }
+ bGPdata *gpd = ob->data;
+
+ gpencil_deform_polyline_verts(mmd, ob, gps);
+ /* Calc geometry data. */
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
+}
+
+static void deformBezier(GpencilModifierData *md,
Depsgraph *UNUSED(depsgraph),
Object *ob,
bGPDlayer *UNUSED(gpl),
@@ -124,9 +192,9 @@ static void deformStroke(GpencilModifierData *md,
}
bGPdata *gpd = ob->data;
- gpencil_deform_verts(mmd, ob, gps);
+ gpencil_deform_bezier_verts(mmd, ob, gps);
/* Calc geometry data. */
- BKE_gpencil_stroke_geometry_update(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
}
static void bakeModifier(Main *UNUSED(bmain),
@@ -155,7 +223,12 @@ static void bakeModifier(Main *UNUSED(bmain),
/* compute armature effects on this frame */
LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
- deformStroke(md_eval, depsgraph, object_eval, gpl, gpf, gps);
+ if (!GPENCIL_STROKE_TYPE_BEZIER(gps)) {
+ deformPolyline(md_eval, depsgraph, object_eval, gpl, gpf, gps);
+ }
+ else {
+ deformBezier(md_eval, depsgraph, object_eval, gpl, gpf, gps);
+ }
}
}
}
@@ -236,7 +309,8 @@ GpencilModifierTypeInfo modifierType_Gpencil_Armature = {
/* copyData */ copyData,
- /* deformStroke */ deformStroke,
+ /* deformPolyline */ deformPolyline,
+ /* deformBezier */ deformBezier,
/* generateStrokes */ NULL,
/* bakeModifier */ bakeModifier,
/* remapTime */ NULL,
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilarray.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilarray.c
index 79d4f4dffec..1cca72c3156 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencilarray.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilarray.c
@@ -260,20 +260,46 @@ static void generate_geometry(GpencilModifierData *md,
/* Duplicate stroke */
bGPDstroke *gps_dst = BKE_gpencil_stroke_duplicate(iter->gps, true, true);
- /* Move points */
- for (int i = 0; i < iter->gps->totpoints; i++) {
- bGPDspoint *pt = &gps_dst->points[i];
- /* Apply randomness matrix. */
- mul_m4_v3(mat_rnd, &pt->x);
-
- /* Apply object local transform (Rot/Scale). */
- if ((mmd->flag & GP_ARRAY_USE_OB_OFFSET) && (mmd->object)) {
- mul_m4_v3(mat, &pt->x);
+ /* Bezier type. */
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps_dst)) {
+ bGPDcurve *gpc = gps_dst->editcurve;
+ for (int i = 0; i < gpc->tot_curve_points; i++) {
+ bGPDcurve_point *pt = &gpc->curve_points[i];
+ BezTriple *bezt = &pt->bezt;
+
+ for (int j = 0; j < 3; j++) {
+ /* Apply randomness matrix. */
+ mul_m4_v3(mat_rnd, bezt->vec[j]);
+ /* Apply object local transform (Rot/Scale). */
+ if ((mmd->flag & GP_ARRAY_USE_OB_OFFSET) && (mmd->object)) {
+ mul_m4_v3(mat, bezt->vec[j]);
+ }
+ /* Global Rotate and scale. */
+ mul_mat3_m4_v3(current_offset, bezt->vec[j]);
+ /* Global translate. */
+ add_v3_v3(bezt->vec[j], current_offset[3]);
+ }
}
- /* Global Rotate and scale. */
- mul_mat3_m4_v3(current_offset, &pt->x);
- /* Global translate. */
- add_v3_v3(&pt->x, current_offset[3]);
+ BKE_gpencil_stroke_geometry_update(gpd, gps_dst, GP_GEO_UPDATE_DEFAULT);
+ }
+ else {
+ /* Polygon type. */
+ for (int i = 0; i < iter->gps->totpoints; i++) {
+ bGPDspoint *pt = &gps_dst->points[i];
+ /* Apply randomness matrix. */
+ mul_m4_v3(mat_rnd, &pt->x);
+
+ /* Apply object local transform (Rot/Scale). */
+ if ((mmd->flag & GP_ARRAY_USE_OB_OFFSET) && (mmd->object)) {
+ mul_m4_v3(mat, &pt->x);
+ }
+ /* Global Rotate and scale. */
+ mul_mat3_m4_v3(current_offset, &pt->x);
+ /* Global translate. */
+ add_v3_v3(&pt->x, current_offset[3]);
+ }
+ /* Calc bounding box. */
+ BKE_gpencil_stroke_boundingbox_calc(gps_dst);
}
/* If replace material, use new one. */
@@ -283,8 +309,6 @@ static void generate_geometry(GpencilModifierData *md,
/* Add new stroke. */
BLI_addhead(&iter->gpf->strokes, gps_dst);
- /* Calc bounding box. */
- BKE_gpencil_stroke_boundingbox_calc(gps_dst);
}
}
@@ -472,7 +496,8 @@ GpencilModifierTypeInfo modifierType_Gpencil_Array = {
/* copyData */ copyData,
- /* deformStroke */ NULL,
+ /* deformPolyline */ NULL,
+ /* deformBezier */ NULL,
/* generateStrokes */ generateStrokes,
/* bakeModifier */ bakeModifier,
/* remapTime */ NULL,
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilbuild.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilbuild.c
index 4e07827c940..c5c079b89d8 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencilbuild.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilbuild.c
@@ -180,7 +180,7 @@ static void reduce_stroke_points(bGPdata *gpd,
gps->totpoints = num_points;
/* Calc geometry data. */
- BKE_gpencil_stroke_geometry_update(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
}
/* --------------------------------------------- */
@@ -637,7 +637,8 @@ GpencilModifierTypeInfo modifierType_Gpencil_Build = {
/* copyData */ copyData,
- /* deformStroke */ NULL,
+ /* deformPolyline */ NULL,
+ /* deformBezier */ NULL,
/* generateStrokes */ generateStrokes,
/* bakeModifier */ NULL,
/* remapTime */ NULL,
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilcolor.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilcolor.c
index 0cb32b693bb..3d0f5f8bb8e 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencilcolor.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilcolor.c
@@ -83,12 +83,12 @@ static void copyData(const GpencilModifierData *md, GpencilModifierData *target)
}
/* color correction strokes */
-static void deformStroke(GpencilModifierData *md,
- Depsgraph *UNUSED(depsgraph),
- Object *ob,
- bGPDlayer *gpl,
- bGPDframe *UNUSED(gpf),
- bGPDstroke *gps)
+static void deformPolyline(GpencilModifierData *md,
+ Depsgraph *UNUSED(depsgraph),
+ Object *ob,
+ bGPDlayer *gpl,
+ bGPDframe *UNUSED(gpf),
+ bGPDstroke *gps)
{
ColorGpencilModifierData *mmd = (ColorGpencilModifierData *)md;
@@ -159,6 +159,18 @@ static void deformStroke(GpencilModifierData *md,
}
}
+/* Deform Bezier. */
+static void deformBezier(GpencilModifierData *md,
+ Depsgraph *depsgraph,
+ Object *ob,
+ bGPDlayer *gpl,
+ bGPDframe *gpf,
+ bGPDstroke *gps)
+{
+ /* Reuse deformPolyline because the changes are not affecting the geometry. */
+ deformPolyline(md, depsgraph, ob, gpl, gpf, gps);
+}
+
static void bakeModifier(Main *UNUSED(bmain),
Depsgraph *depsgraph,
GpencilModifierData *md,
@@ -169,7 +181,7 @@ static void bakeModifier(Main *UNUSED(bmain),
LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
- deformStroke(md, depsgraph, ob, gpl, gpf, gps);
+ deformPolyline(md, depsgraph, ob, gpl, gpf, gps);
}
}
}
@@ -235,7 +247,8 @@ GpencilModifierTypeInfo modifierType_Gpencil_Color = {
/* copyData */ copyData,
- /* deformStroke */ deformStroke,
+ /* deformPolyline */ deformPolyline,
+ /* deformBezier */ deformBezier,
/* generateStrokes */ NULL,
/* bakeModifier */ bakeModifier,
/* remapTime */ NULL,
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilhook.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilhook.c
index bad324ea1ca..cafa76c347c 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencilhook.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilhook.c
@@ -163,7 +163,7 @@ static float gpencil_hook_falloff(const struct GPHookData_cb *tData, const float
}
/* apply point deformation */
-static void gpencil_hook_co_apply(struct GPHookData_cb *tData, float weight, bGPDspoint *pt)
+static void gpencil_hook_co_apply(struct GPHookData_cb *tData, float weight, float co[3])
{
float fac;
@@ -172,11 +172,11 @@ static void gpencil_hook_co_apply(struct GPHookData_cb *tData, float weight, bGP
if (tData->use_uniform) {
float co_uniform[3];
- mul_v3_m3v3(co_uniform, tData->mat_uniform, &pt->x);
+ mul_v3_m3v3(co_uniform, tData->mat_uniform, co);
len_sq = len_squared_v3v3(tData->cent, co_uniform);
}
else {
- len_sq = len_squared_v3v3(tData->cent, &pt->x);
+ len_sq = len_squared_v3v3(tData->cent, co);
}
fac = gpencil_hook_falloff(tData, len_sq);
@@ -187,62 +187,48 @@ static void gpencil_hook_co_apply(struct GPHookData_cb *tData, float weight, bGP
if (fac) {
float co_tmp[3];
- mul_v3_m4v3(co_tmp, tData->mat, &pt->x);
- interp_v3_v3v3(&pt->x, &pt->x, co_tmp, fac * weight);
+ mul_v3_m4v3(co_tmp, tData->mat, co);
+ interp_v3_v3v3(co, co, co_tmp, fac * weight);
}
}
-/* deform stroke */
-static void deformStroke(GpencilModifierData *md,
- Depsgraph *UNUSED(depsgraph),
- Object *ob,
- bGPDlayer *gpl,
- bGPDframe *UNUSED(gpf),
- bGPDstroke *gps)
+static bool do_modifier(Object *ob, HookGpencilModifierData *mmd, bGPDlayer *gpl, bGPDstroke *gps)
{
- HookGpencilModifierData *mmd = (HookGpencilModifierData *)md;
- if (!mmd->object) {
- return;
- }
-
- const int def_nr = BKE_object_defgroup_name_index(ob, mmd->vgname);
+ return is_stroke_affected_by_modifier(ob,
+ mmd->layername,
+ mmd->material,
+ mmd->pass_index,
+ mmd->layer_pass,
+ 1,
+ gpl,
+ gps,
+ mmd->flag & GP_HOOK_INVERT_LAYER,
+ mmd->flag & GP_HOOK_INVERT_PASS,
+ mmd->flag & GP_HOOK_INVERT_LAYERPASS,
+ mmd->flag & GP_HOOK_INVERT_MATERIAL);
+}
+static void calc_hook_data(HookGpencilModifierData *mmd, Object *ob, struct GPHookData_cb *tData)
+{
bPoseChannel *pchan = BKE_pose_channel_find_name(mmd->object->pose, mmd->subtarget);
float dmat[4][4];
- struct GPHookData_cb tData;
-
- if (!is_stroke_affected_by_modifier(ob,
- mmd->layername,
- mmd->material,
- mmd->pass_index,
- mmd->layer_pass,
- 1,
- gpl,
- gps,
- mmd->flag & GP_HOOK_INVERT_LAYER,
- mmd->flag & GP_HOOK_INVERT_PASS,
- mmd->flag & GP_HOOK_INVERT_LAYERPASS,
- mmd->flag & GP_HOOK_INVERT_MATERIAL)) {
- return;
- }
- bGPdata *gpd = ob->data;
/* init struct */
- tData.curfalloff = mmd->curfalloff;
- tData.falloff_type = mmd->falloff_type;
- tData.falloff = (mmd->falloff_type == eHook_Falloff_None) ? 0.0f : mmd->falloff;
- tData.falloff_sq = square_f(tData.falloff);
- tData.fac_orig = mmd->force;
- tData.use_falloff = (tData.falloff_sq != 0.0f);
- tData.use_uniform = (mmd->flag & GP_HOOK_UNIFORM_SPACE) != 0;
-
- if (tData.use_uniform) {
- copy_m3_m4(tData.mat_uniform, mmd->parentinv);
- mul_v3_m3v3(tData.cent, tData.mat_uniform, mmd->cent);
+ tData->curfalloff = mmd->curfalloff;
+ tData->falloff_type = mmd->falloff_type;
+ tData->falloff = (mmd->falloff_type == eHook_Falloff_None) ? 0.0f : mmd->falloff;
+ tData->falloff_sq = square_f(tData->falloff);
+ tData->fac_orig = mmd->force;
+ tData->use_falloff = (tData->falloff_sq != 0.0f);
+ tData->use_uniform = (mmd->flag & GP_HOOK_UNIFORM_SPACE) != 0;
+
+ if (tData->use_uniform) {
+ copy_m3_m4(tData->mat_uniform, mmd->parentinv);
+ mul_v3_m3v3(tData->cent, tData->mat_uniform, mmd->cent);
}
else {
- unit_m3(tData.mat_uniform);
- copy_v3_v3(tData.cent, mmd->cent);
+ unit_m3(tData->mat_uniform);
+ copy_v3_v3(tData->cent, mmd->cent);
}
/* get world-space matrix of target, corrected for the space the verts are in */
@@ -255,7 +241,31 @@ static void deformStroke(GpencilModifierData *md,
copy_m4_m4(dmat, mmd->object->obmat);
}
invert_m4_m4(ob->imat, ob->obmat);
- mul_m4_series(tData.mat, ob->imat, dmat, mmd->parentinv);
+ mul_m4_series(tData->mat, ob->imat, dmat, mmd->parentinv);
+}
+
+/* deform stroke */
+static void deformPolyline(GpencilModifierData *md,
+ Depsgraph *UNUSED(depsgraph),
+ Object *ob,
+ bGPDlayer *gpl,
+ bGPDframe *UNUSED(gpf),
+ bGPDstroke *gps)
+{
+ HookGpencilModifierData *mmd = (HookGpencilModifierData *)md;
+ if (!mmd->object) {
+ return;
+ }
+
+ const int def_nr = BKE_object_defgroup_name_index(ob, mmd->vgname);
+
+ if (!do_modifier(ob, mmd, gpl, gps)) {
+ return;
+ }
+ bGPdata *gpd = ob->data;
+
+ struct GPHookData_cb tData;
+ calc_hook_data(mmd, ob, &tData);
/* loop points and apply deform */
for (int i = 0; i < gps->totpoints; i++) {
@@ -268,10 +278,53 @@ static void deformStroke(GpencilModifierData *md,
if (weight < 0.0f) {
continue;
}
- gpencil_hook_co_apply(&tData, weight, pt);
+ gpencil_hook_co_apply(&tData, weight, &pt->x);
+ }
+ /* Calc geometry data. */
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
+}
+
+static void deformBezier(GpencilModifierData *md,
+ Depsgraph *UNUSED(depsgraph),
+ Object *ob,
+ bGPDlayer *gpl,
+ bGPDframe *UNUSED(gpf),
+ bGPDstroke *gps)
+{
+ HookGpencilModifierData *mmd = (HookGpencilModifierData *)md;
+ if (!mmd->object) {
+ return;
+ }
+
+ const int def_nr = BKE_object_defgroup_name_index(ob, mmd->vgname);
+
+ if (!do_modifier(ob, mmd, gpl, gps)) {
+ return;
+ }
+ bGPdata *gpd = ob->data;
+
+ struct GPHookData_cb tData;
+ calc_hook_data(mmd, ob, &tData);
+
+ /* Loop points and apply deform. */
+ bGPDcurve *gpc = gps->editcurve;
+ for (int i = 0; i < gpc->tot_curve_points; i++) {
+ bGPDcurve_point *pt = &gpc->curve_points[i];
+ BezTriple *bezt = &pt->bezt;
+ MDeformVert *dvert = gpc->dvert != NULL ? &gpc->dvert[i] : NULL;
+
+ /* verify vertex group */
+ const float weight = get_modifier_point_weight(
+ dvert, (mmd->flag & GP_HOOK_INVERT_VGROUP) != 0, def_nr);
+ if (weight < 0.0f) {
+ continue;
+ }
+ for (int j = 0; j < 3; j++) {
+ gpencil_hook_co_apply(&tData, weight, bezt->vec[j]);
+ }
}
/* Calc geometry data. */
- BKE_gpencil_stroke_geometry_update(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
}
/* FIXME: Ideally we be doing this on a copy of the main depsgraph
@@ -301,7 +354,12 @@ static void bakeModifier(Main *UNUSED(bmain),
/* compute hook effects on this frame */
LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
- deformStroke(md, depsgraph, ob, gpl, gpf, gps);
+ if (!GPENCIL_STROKE_TYPE_BEZIER(gps)) {
+ deformPolyline(md, depsgraph, ob, gpl, gpf, gps);
+ }
+ else {
+ deformBezier(md, depsgraph, ob, gpl, gpf, gps);
+ }
}
}
}
@@ -429,7 +487,8 @@ GpencilModifierTypeInfo modifierType_Gpencil_Hook = {
/* copyData */ copyData,
- /* deformStroke */ deformStroke,
+ /* deformPolyline */ deformPolyline,
+ /* deformBezier */ deformBezier,
/* generateStrokes */ NULL,
/* bakeModifier */ bakeModifier,
/* remapTime */ NULL,
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencillattice.c b/source/blender/gpencil_modifiers/intern/MOD_gpencillattice.c
index 2934b89c747..40f9e5f9151 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencillattice.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencillattice.c
@@ -75,29 +75,37 @@ static void copyData(const GpencilModifierData *md, GpencilModifierData *target)
BKE_gpencil_modifier_copydata_generic(md, target);
}
-static void deformStroke(GpencilModifierData *md,
- Depsgraph *UNUSED(depsgraph),
- Object *ob,
- bGPDlayer *gpl,
- bGPDframe *UNUSED(gpf),
- bGPDstroke *gps)
+static bool do_modifier(Object *ob,
+ LatticeGpencilModifierData *mmd,
+ bGPDlayer *gpl,
+ bGPDstroke *gps)
+{
+ return is_stroke_affected_by_modifier(ob,
+ mmd->layername,
+ mmd->material,
+ mmd->pass_index,
+ mmd->layer_pass,
+ 1,
+ gpl,
+ gps,
+ mmd->flag & GP_LATTICE_INVERT_LAYER,
+ mmd->flag & GP_LATTICE_INVERT_PASS,
+ mmd->flag & GP_LATTICE_INVERT_LAYERPASS,
+ mmd->flag & GP_LATTICE_INVERT_MATERIAL);
+}
+
+static void deformPolyline(GpencilModifierData *md,
+ Depsgraph *UNUSED(depsgraph),
+ Object *ob,
+ bGPDlayer *gpl,
+ bGPDframe *UNUSED(gpf),
+ bGPDstroke *gps)
{
bGPdata *gpd = ob->data;
LatticeGpencilModifierData *mmd = (LatticeGpencilModifierData *)md;
const int def_nr = BKE_object_defgroup_name_index(ob, mmd->vgname);
- if (!is_stroke_affected_by_modifier(ob,
- mmd->layername,
- mmd->material,
- mmd->pass_index,
- mmd->layer_pass,
- 1,
- gpl,
- gps,
- mmd->flag & GP_LATTICE_INVERT_LAYER,
- mmd->flag & GP_LATTICE_INVERT_PASS,
- mmd->flag & GP_LATTICE_INVERT_LAYERPASS,
- mmd->flag & GP_LATTICE_INVERT_MATERIAL)) {
+ if (!do_modifier(ob, mmd, gpl, gps)) {
return;
}
@@ -119,7 +127,47 @@ static void deformStroke(GpencilModifierData *md,
(struct LatticeDeformData *)mmd->cache_data, &pt->x, mmd->strength * weight);
}
/* Calc geometry data. */
- BKE_gpencil_stroke_geometry_update(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
+}
+
+static void deformBezier(GpencilModifierData *md,
+ Depsgraph *UNUSED(depsgraph),
+ Object *ob,
+ bGPDlayer *gpl,
+ bGPDframe *UNUSED(gpf),
+ bGPDstroke *gps)
+{
+ bGPdata *gpd = ob->data;
+ LatticeGpencilModifierData *mmd = (LatticeGpencilModifierData *)md;
+ const int def_nr = BKE_object_defgroup_name_index(ob, mmd->vgname);
+
+ if (!do_modifier(ob, mmd, gpl, gps)) {
+ return;
+ }
+
+ if (mmd->cache_data == NULL) {
+ return;
+ }
+
+ bGPDcurve *gpc = gps->editcurve;
+ for (int i = 0; i < gpc->tot_curve_points; i++) {
+ bGPDcurve_point *pt = &gpc->curve_points[i];
+ BezTriple *bezt = &pt->bezt;
+ MDeformVert *dvert = gpc->dvert != NULL ? &gpc->dvert[i] : NULL;
+
+ /* verify vertex group */
+ const float weight = get_modifier_point_weight(
+ dvert, (mmd->flag & GP_LATTICE_INVERT_VGROUP) != 0, def_nr);
+ if (weight < 0.0f) {
+ continue;
+ }
+ for (int j = 0; j < 3; j++) {
+ BKE_lattice_deform_data_eval_co(
+ (struct LatticeDeformData *)mmd->cache_data, bezt->vec[j], mmd->strength * weight);
+ }
+ }
+ /* Calc geometry data. */
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
}
/* FIXME: Ideally we be doing this on a copy of the main depsgraph
@@ -156,7 +204,12 @@ static void bakeModifier(Main *UNUSED(bmain),
/* Compute lattice effects on this frame. */
LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
- deformStroke(md, depsgraph, ob, gpl, gpf, gps);
+ if (!GPENCIL_STROKE_TYPE_BEZIER(gps)) {
+ deformPolyline(md, depsgraph, ob, gpl, gpf, gps);
+ }
+ else {
+ deformBezier(md, depsgraph, ob, gpl, gpf, gps);
+ }
}
}
}
@@ -271,7 +324,8 @@ GpencilModifierTypeInfo modifierType_Gpencil_Lattice = {
/* copyData */ copyData,
- /* deformStroke */ deformStroke,
+ /* deformPolyline */ deformPolyline,
+ /* deformBezier */ deformBezier,
/* generateStrokes */ NULL,
/* bakeModifier */ bakeModifier,
/* remapTime */ NULL,
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencillength.c b/source/blender/gpencil_modifiers/intern/MOD_gpencillength.c
index fd94ac92bc3..20209cdad81 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencillength.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencillength.c
@@ -109,7 +109,7 @@ static void applyLength(LengthGpencilModifierData *lmd, bGPdata *gpd, bGPDstroke
changed |= gpencil_modify_stroke(gps, len * lmd->end_fac, lmd->overshoot_fac, 2);
if (changed) {
- BKE_gpencil_stroke_geometry_update(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
}
}
@@ -134,12 +134,12 @@ static void bakeModifier(Main *UNUSED(bmain),
/* -------------------------------- */
/* Generic "generateStrokes" callback */
-static void deformStroke(GpencilModifierData *md,
- Depsgraph *UNUSED(depsgraph),
- Object *ob,
- bGPDlayer *gpl,
- bGPDframe *UNUSED(gpf),
- bGPDstroke *gps)
+static void deformPolyline(GpencilModifierData *md,
+ Depsgraph *UNUSED(depsgraph),
+ Object *ob,
+ bGPDlayer *gpl,
+ bGPDframe *UNUSED(gpf),
+ bGPDstroke *gps)
{
bGPdata *gpd = ob->data;
LengthGpencilModifierData *lmd = (LengthGpencilModifierData *)md;
@@ -207,7 +207,8 @@ GpencilModifierTypeInfo modifierType_Gpencil_Length = {
/* copyData */ copyData,
- /* deformStroke */ deformStroke,
+ /* deformPolyline */ deformPolyline,
+ /* deformBezier */ NULL,
/* generateStrokes */ NULL,
/* bakeModifier */ bakeModifier,
/* remapTime */ NULL,
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c b/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c
index fcc44aab583..a9f50f08cc7 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c
@@ -681,7 +681,8 @@ GpencilModifierTypeInfo modifierType_Gpencil_Lineart = {
/* copyData. */ copyData,
- /* deformStroke. */ NULL,
+ /* deformPolyline. */ NULL,
+ /* deformBezier. */ NULL,
/* generateStrokes. */ generateStrokes,
/* bakeModifier. */ bakeModifier,
/* remapTime. */ NULL,
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilmirror.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilmirror.c
index 1dc09cf1f74..d7e5458ef98 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencilmirror.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilmirror.c
@@ -39,6 +39,7 @@
#include "BKE_context.h"
#include "BKE_gpencil.h"
+#include "BKE_gpencil_geom.h"
#include "BKE_gpencil_modifier.h"
#include "BKE_lib_query.h"
#include "BKE_main.h"
@@ -74,13 +75,25 @@ static void copyData(const GpencilModifierData *md, GpencilModifierData *target)
/* Mirror is using current object as origin. */
static void update_mirror_local(bGPDstroke *gps, int axis)
{
- int i;
- bGPDspoint *pt;
float factor[3] = {1.0f, 1.0f, 1.0f};
factor[axis] = -1.0f;
- for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
- mul_v3_v3(&pt->x, factor);
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps)) {
+ bGPDcurve *gpc = gps->editcurve;
+ for (int i = 0; i < gpc->tot_curve_points; i++) {
+ bGPDcurve_point *pt = &gpc->curve_points[i];
+ BezTriple *bezt = &pt->bezt;
+ for (int j = 0; j < 3; j++) {
+ mul_v3_v3(bezt->vec[j], factor);
+ }
+ }
+ }
+ else {
+ int i;
+ bGPDspoint *pt;
+ for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
+ mul_v3_v3(&pt->x, factor);
+ }
}
}
@@ -101,8 +114,20 @@ static void update_mirror_object(Object *ob,
invert_m4_m4(itmp, tmp);
mul_m4_series(mtx, itmp, mtx, tmp);
- for (int i = 0; i < gps->totpoints; i++) {
- mul_m4_v3(mtx, &gps->points[i].x);
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps)) {
+ bGPDcurve *gpc = gps->editcurve;
+ for (int i = 0; i < gpc->tot_curve_points; i++) {
+ bGPDcurve_point *pt = &gpc->curve_points[i];
+ BezTriple *bezt = &pt->bezt;
+ for (int j = 0; j < 3; j++) {
+ mul_m4_v3(mtx, bezt->vec[j]);
+ }
+ }
+ }
+ else {
+ for (int i = 0; i < gps->totpoints; i++) {
+ mul_m4_v3(mtx, &gps->points[i].x);
+ }
}
}
@@ -120,6 +145,7 @@ static void generate_geometry(GpencilModifierData *md, Object *ob, bGPDlayer *gp
{
MirrorGpencilModifierData *mmd = (MirrorGpencilModifierData *)md;
bGPDstroke *gps, *gps_new = NULL;
+ bGPdata *gpd = ob->data;
int tot_strokes;
int i;
@@ -145,6 +171,7 @@ static void generate_geometry(GpencilModifierData *md, Object *ob, bGPDlayer *gp
mmd->flag & GP_MIRROR_INVERT_MATERIAL)) {
gps_new = BKE_gpencil_stroke_duplicate(gps, true, true);
update_position(ob, mmd, gps_new, xi);
+ BKE_gpencil_stroke_geometry_update(gpd, gps_new, GP_GEO_UPDATE_DEFAULT);
BLI_addtail(&gpf->strokes, gps_new);
}
}
@@ -261,7 +288,8 @@ GpencilModifierTypeInfo modifierType_Gpencil_Mirror = {
/* copyData */ copyData,
- /* deformStroke */ NULL,
+ /* deformPolyline */ NULL,
+ /* deformBezier */ NULL,
/* generateStrokes */ generateStrokes,
/* bakeModifier */ bakeModifier,
/* remapTime */ NULL,
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilmultiply.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilmultiply.c
index ac60e694618..d4fd73142c1 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencilmultiply.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilmultiply.c
@@ -185,7 +185,7 @@ static void duplicateStroke(Object *ob,
}
/* Calc geometry data. */
if (new_gps != NULL) {
- BKE_gpencil_stroke_geometry_update(gpd, new_gps);
+ BKE_gpencil_stroke_geometry_update(gpd, new_gps, GP_GEO_UPDATE_DEFAULT);
}
MEM_freeN(t1_array);
MEM_freeN(t2_array);
@@ -368,7 +368,8 @@ GpencilModifierTypeInfo modifierType_Gpencil_Multiply = {
/* copyData */ copyData,
- /* deformStroke */ NULL,
+ /* deformPolyline */ NULL,
+ /* deformBezier */ NULL,
/* generateStrokes */ generateStrokes,
/* bakeModifier */ bakeModifier,
/* remapTime */ NULL,
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilnoise.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilnoise.c
index 9e9eba3d61e..c76ff79a62e 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencilnoise.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilnoise.c
@@ -123,12 +123,12 @@ BLI_INLINE float table_sample(float *table, float x)
/**
* Apply noise effect based on stroke direction.
*/
-static void deformStroke(GpencilModifierData *md,
- Depsgraph *depsgraph,
- Object *ob,
- bGPDlayer *gpl,
- bGPDframe *gpf,
- bGPDstroke *gps)
+static void deformPolyline(GpencilModifierData *md,
+ Depsgraph *depsgraph,
+ Object *ob,
+ bGPDlayer *gpl,
+ bGPDframe *gpf,
+ bGPDstroke *gps)
{
NoiseGpencilModifierData *mmd = (NoiseGpencilModifierData *)md;
MDeformVert *dvert = NULL;
@@ -273,7 +273,7 @@ static void bakeModifier(struct Main *UNUSED(bmain),
LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
- deformStroke(md, depsgraph, ob, gpl, gpf, gps);
+ deformPolyline(md, depsgraph, ob, gpl, gpf, gps);
}
}
}
@@ -359,7 +359,8 @@ GpencilModifierTypeInfo modifierType_Gpencil_Noise = {
/* copyData */ copyData,
- /* deformStroke */ deformStroke,
+ /* deformPolyline */ deformPolyline,
+ /* deformBezier */ NULL,
/* generateStrokes */ NULL,
/* bakeModifier */ bakeModifier,
/* remapTime */ NULL,
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpenciloffset.c b/source/blender/gpencil_modifiers/intern/MOD_gpenciloffset.c
index 1a38b91a18b..b45653a9361 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpenciloffset.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpenciloffset.c
@@ -70,41 +70,50 @@ static void copyData(const GpencilModifierData *md, GpencilModifierData *target)
BKE_gpencil_modifier_copydata_generic(md, target);
}
-/* change stroke offsetness */
-static void deformStroke(GpencilModifierData *md,
- Depsgraph *UNUSED(depsgraph),
- Object *ob,
- bGPDlayer *gpl,
- bGPDframe *gpf,
- bGPDstroke *gps)
+static bool do_modifier(Object *ob,
+ OffsetGpencilModifierData *mmd,
+ bGPDlayer *gpl,
+ bGPDstroke *gps)
{
- OffsetGpencilModifierData *mmd = (OffsetGpencilModifierData *)md;
- const int def_nr = BKE_object_defgroup_name_index(ob, mmd->vgname);
+ return is_stroke_affected_by_modifier(ob,
+ mmd->layername,
+ mmd->material,
+ mmd->pass_index,
+ mmd->layer_pass,
+ 1,
+ gpl,
+ gps,
+ mmd->flag & GP_OFFSET_INVERT_LAYER,
+ mmd->flag & GP_OFFSET_INVERT_PASS,
+ mmd->flag & GP_OFFSET_INVERT_LAYERPASS,
+ mmd->flag & GP_OFFSET_INVERT_MATERIAL);
+}
- float mat[4][4];
+/* Calculate transform matrix. */
+static float prepare_matrix(OffsetGpencilModifierData *mmd, float weight, float r_mat[4][4])
+{
float loc[3], rot[3], scale[3];
- if (!is_stroke_affected_by_modifier(ob,
- mmd->layername,
- mmd->material,
- mmd->pass_index,
- mmd->layer_pass,
- 1,
- gpl,
- gps,
- mmd->flag & GP_OFFSET_INVERT_LAYER,
- mmd->flag & GP_OFFSET_INVERT_PASS,
- mmd->flag & GP_OFFSET_INVERT_LAYERPASS,
- mmd->flag & GP_OFFSET_INVERT_MATERIAL)) {
- return;
- }
+ mul_v3_v3fl(loc, mmd->loc, weight);
+ mul_v3_v3fl(rot, mmd->rot, weight);
+ mul_v3_v3fl(scale, mmd->scale, weight);
+ add_v3_fl(scale, 1.0);
+ loc_eul_size_to_mat4(r_mat, loc, rot, scale);
+
+ return (scale[0] + scale[1] + scale[2]) / 3.0f;
+}
+
+/* Calculate random seeds. */
+static void prepare_random_seeds(
+ GpencilModifierData *md, Object *ob, bGPDframe *gpf, bGPDstroke *gps, float rand[3][3])
+{
+ OffsetGpencilModifierData *mmd = (OffsetGpencilModifierData *)md;
int seed = mmd->seed;
/* Make sure different modifiers get different seeds. */
seed += BLI_hash_string(ob->id.name + 2);
seed += BLI_hash_string(md->name);
- float rand[3][3];
float rand_offset = BLI_hash_int_01(seed);
/* Get stroke index for random offset. */
@@ -129,9 +138,28 @@ static void deformStroke(GpencilModifierData *md,
}
}
}
+}
- bGPdata *gpd = ob->data;
+/* change stroke offsetness */
+static void deformPolyline(GpencilModifierData *md,
+ Depsgraph *UNUSED(depsgraph),
+ Object *ob,
+ bGPDlayer *gpl,
+ bGPDframe *gpf,
+ bGPDstroke *gps)
+{
+ OffsetGpencilModifierData *mmd = (OffsetGpencilModifierData *)md;
+ const int def_nr = BKE_object_defgroup_name_index(ob, mmd->vgname);
+
+ if (!do_modifier(ob, mmd, gpl, gps)) {
+ return;
+ }
+ /* Calculate Random seeds. */
+ float rand[3][3];
+ prepare_random_seeds(md, ob, gpf, gps, rand);
+
+ bGPdata *gpd = ob->data;
for (int i = 0; i < gps->totpoints; i++) {
bGPDspoint *pt = &gps->points[i];
MDeformVert *dvert = gps->dvert != NULL ? &gps->dvert[i] : NULL;
@@ -161,20 +189,78 @@ static void deformStroke(GpencilModifierData *md,
mul_m4_v3(mat_rnd, &pt->x);
/* Calculate matrix. */
- mul_v3_v3fl(loc, mmd->loc, weight);
- mul_v3_v3fl(rot, mmd->rot, weight);
- mul_v3_v3fl(scale, mmd->scale, weight);
- add_v3_fl(scale, 1.0);
- loc_eul_size_to_mat4(mat, loc, rot, scale);
-
- /* Apply scale to thickness. */
- float unit_scale = (scale[0] + scale[1] + scale[2]) / 3.0f;
+ float mat[4][4];
+ float unit_scale = prepare_matrix(mmd, weight, mat);
pt->pressure *= unit_scale;
mul_m4_v3(mat, &pt->x);
}
/* Calc geometry data. */
- BKE_gpencil_stroke_geometry_update(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
+}
+
+static void deformBezier(GpencilModifierData *md,
+ Depsgraph *UNUSED(depsgraph),
+ Object *ob,
+ bGPDlayer *gpl,
+ bGPDframe *gpf,
+ bGPDstroke *gps)
+{
+ OffsetGpencilModifierData *mmd = (OffsetGpencilModifierData *)md;
+ const int def_nr = BKE_object_defgroup_name_index(ob, mmd->vgname);
+
+ if (!do_modifier(ob, mmd, gpl, gps)) {
+ return;
+ }
+
+ /* Calculate Random seeds. */
+ float rand[3][3];
+ prepare_random_seeds(md, ob, gpf, gps, rand);
+
+ bGPdata *gpd = ob->data;
+ bGPDcurve *gpc = gps->editcurve;
+
+ for (int i = 0; i < gpc->tot_curve_points; i++) {
+ bGPDcurve_point *pt = &gpc->curve_points[i];
+ BezTriple *bezt = &pt->bezt;
+ MDeformVert *dvert = (gpc->dvert != NULL) ? &gpc->dvert[i] : NULL;
+
+ /* Verify vertex group. */
+ const float weight = get_modifier_point_weight(
+ dvert, (mmd->flag & GP_OFFSET_INVERT_VGROUP) != 0, def_nr);
+ if (weight < 0.0f) {
+ continue;
+ }
+
+ /* Calculate Random matrix. */
+ float mat_rnd[4][4];
+ float rnd_loc[3], rnd_rot[3], rnd_scale_weight[3];
+ float rnd_scale[3] = {1.0f, 1.0f, 1.0f};
+
+ mul_v3_v3fl(rnd_loc, rand[0], weight);
+ mul_v3_v3fl(rnd_rot, rand[1], weight);
+ mul_v3_v3fl(rnd_scale_weight, rand[2], weight);
+
+ mul_v3_v3v3(rnd_loc, mmd->rnd_offset, rnd_loc);
+ mul_v3_v3v3(rnd_rot, mmd->rnd_rot, rnd_rot);
+ madd_v3_v3v3(rnd_scale, mmd->rnd_scale, rnd_scale_weight);
+
+ loc_eul_size_to_mat4(mat_rnd, rnd_loc, rnd_rot, rnd_scale);
+ /* Apply randomness matrix. */
+ for (int j = 0; j < 3; j++) {
+ mul_m4_v3(mat_rnd, bezt->vec[j]);
+ }
+
+ float mat[4][4];
+ float unit_scale = prepare_matrix(mmd, weight, mat);
+ pt->pressure *= unit_scale;
+
+ for (int j = 0; j < 3; j++) {
+ mul_m4_v3(mat, bezt->vec[j]);
+ }
+ }
+
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
}
static void bakeModifier(struct Main *UNUSED(bmain),
@@ -187,7 +273,12 @@ static void bakeModifier(struct Main *UNUSED(bmain),
LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
- deformStroke(md, depsgraph, ob, gpl, gpf, gps);
+ if (!GPENCIL_STROKE_TYPE_BEZIER(gps)) {
+ deformPolyline(md, depsgraph, ob, gpl, gpf, gps);
+ }
+ else {
+ deformBezier(md, depsgraph, ob, gpl, gpf, gps);
+ }
}
}
}
@@ -254,7 +345,8 @@ GpencilModifierTypeInfo modifierType_Gpencil_Offset = {
/* copyData */ copyData,
- /* deformStroke */ deformStroke,
+ /* deformPolyline */ deformPolyline,
+ /* deformBezier */ deformBezier,
/* generateStrokes */ NULL,
/* bakeModifier */ bakeModifier,
/* remapTime */ NULL,
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilopacity.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilopacity.c
index fb75b1e99ac..7ba6443b2b2 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencilopacity.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilopacity.c
@@ -85,12 +85,12 @@ static void copyData(const GpencilModifierData *md, GpencilModifierData *target)
}
/* opacity strokes */
-static void deformStroke(GpencilModifierData *md,
- Depsgraph *UNUSED(depsgraph),
- Object *ob,
- bGPDlayer *gpl,
- bGPDframe *UNUSED(gpf),
- bGPDstroke *gps)
+static void deformPolyline(GpencilModifierData *md,
+ Depsgraph *UNUSED(depsgraph),
+ Object *ob,
+ bGPDlayer *gpl,
+ bGPDframe *UNUSED(gpf),
+ bGPDstroke *gps)
{
OpacityGpencilModifierData *mmd = (OpacityGpencilModifierData *)md;
const int def_nr = BKE_object_defgroup_name_index(ob, mmd->vgname);
@@ -192,6 +192,18 @@ static void deformStroke(GpencilModifierData *md,
}
}
+/* Deform Bezier. */
+static void deformBezier(GpencilModifierData *md,
+ Depsgraph *depsgraph,
+ Object *ob,
+ bGPDlayer *gpl,
+ bGPDframe *gpf,
+ bGPDstroke *gps)
+{
+ /* Reuse deformPolyline because the changes are not affecting the geometry. */
+ deformPolyline(md, depsgraph, ob, gpl, gpf, gps);
+}
+
static void bakeModifier(Main *UNUSED(bmain),
Depsgraph *depsgraph,
GpencilModifierData *md,
@@ -202,7 +214,7 @@ static void bakeModifier(Main *UNUSED(bmain),
LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
- deformStroke(md, depsgraph, ob, gpl, gpf, gps);
+ deformPolyline(md, depsgraph, ob, gpl, gpf, gps);
}
}
}
@@ -313,7 +325,8 @@ GpencilModifierTypeInfo modifierType_Gpencil_Opacity = {
/* copyData */ copyData,
- /* deformStroke */ deformStroke,
+ /* deformPolyline */ deformPolyline,
+ /* deformBezier */ deformBezier,
/* generateStrokes */ NULL,
/* bakeModifier */ bakeModifier,
/* remapTime */ NULL,
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilsimplify.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilsimplify.c
index 18785705e50..fe32f5baadc 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencilsimplify.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilsimplify.c
@@ -66,12 +66,12 @@ static void copyData(const GpencilModifierData *md, GpencilModifierData *target)
BKE_gpencil_modifier_copydata_generic(md, target);
}
-static void deformStroke(GpencilModifierData *md,
- Depsgraph *UNUSED(depsgraph),
- Object *ob,
- bGPDlayer *gpl,
- bGPDframe *gpf,
- bGPDstroke *gps)
+static void deformPolyline(GpencilModifierData *md,
+ Depsgraph *UNUSED(depsgraph),
+ Object *ob,
+ bGPDlayer *gpl,
+ bGPDframe *gpf,
+ bGPDstroke *gps)
{
SimplifyGpencilModifierData *mmd = (SimplifyGpencilModifierData *)md;
@@ -126,7 +126,7 @@ static void bakeModifier(struct Main *UNUSED(bmain),
LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
- deformStroke(md, depsgraph, ob, gpl, gpf, gps);
+ deformPolyline(md, depsgraph, ob, gpl, gpf, gps);
}
}
}
@@ -189,7 +189,8 @@ GpencilModifierTypeInfo modifierType_Gpencil_Simplify = {
/* copyData */ copyData,
- /* deformStroke */ deformStroke,
+ /* deformPolyline */ deformPolyline,
+ /* deformBezier */ NULL,
/* generateStrokes */ NULL,
/* bakeModifier */ bakeModifier,
/* remapTime */ NULL,
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilsmooth.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilsmooth.c
index 4142a1c02dc..f8d230f1401 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencilsmooth.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilsmooth.c
@@ -39,6 +39,7 @@
#include "BKE_colortools.h"
#include "BKE_context.h"
#include "BKE_deform.h"
+#include "BKE_gpencil_curve.h"
#include "BKE_gpencil_geom.h"
#include "BKE_gpencil_modifier.h"
#include "BKE_lib_query.h"
@@ -84,12 +85,12 @@ static void copyData(const GpencilModifierData *md, GpencilModifierData *target)
/**
* Apply smooth effect based on stroke direction.
*/
-static void deformStroke(GpencilModifierData *md,
- Depsgraph *UNUSED(depsgraph),
- Object *ob,
- bGPDlayer *gpl,
- bGPDframe *UNUSED(gpf),
- bGPDstroke *gps)
+static void deformPolyline(GpencilModifierData *md,
+ Depsgraph *UNUSED(depsgraph),
+ Object *ob,
+ bGPDlayer *gpl,
+ bGPDframe *UNUSED(gpf),
+ bGPDstroke *gps)
{
SmoothGpencilModifierData *mmd = (SmoothGpencilModifierData *)md;
const int def_nr = BKE_object_defgroup_name_index(ob, mmd->vgname);
@@ -151,6 +152,55 @@ static void deformStroke(GpencilModifierData *md,
}
}
+static void deformBezier(GpencilModifierData *md,
+ Depsgraph *UNUSED(depsgraph),
+ Object *ob,
+ bGPDlayer *gpl,
+ bGPDframe *UNUSED(gpf),
+ bGPDstroke *gps)
+{
+ SmoothGpencilModifierData *mmd = (SmoothGpencilModifierData *)md;
+
+ if (!is_stroke_affected_by_modifier(ob,
+ mmd->layername,
+ mmd->material,
+ mmd->pass_index,
+ mmd->layer_pass,
+ 3,
+ gpl,
+ gps,
+ mmd->flag & GP_SMOOTH_INVERT_LAYER,
+ mmd->flag & GP_SMOOTH_INVERT_PASS,
+ mmd->flag & GP_SMOOTH_INVERT_LAYERPASS,
+ mmd->flag & GP_SMOOTH_INVERT_MATERIAL)) {
+ return;
+ }
+
+ if (mmd->factor <= 0.0f) {
+ return;
+ }
+ bGPdata *gpd = ob->data;
+ const int def_nr = BKE_object_defgroup_name_index(ob, mmd->vgname);
+ const bool use_curve = (mmd->flag & GP_SMOOTH_CUSTOM_CURVE) != 0 && mmd->curve_intensity;
+
+ BKE_gpencil_editcurve_smooth_ex(gps,
+ mmd->factor,
+ 2,
+ mmd->step,
+ false,
+ false,
+ true,
+ (mmd->flag & GP_SMOOTH_INVERT_VGROUP) != 0,
+ def_nr,
+ use_curve ? mmd->curve_intensity : NULL,
+ mmd->flag & GP_SMOOTH_MOD_LOCATION,
+ mmd->flag & GP_SMOOTH_MOD_THICKNESS,
+ mmd->flag & GP_SMOOTH_MOD_STRENGTH);
+
+ BKE_gpencil_editcurve_recalculate_handles(gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_POLYLINE_REGENERATE_ALL);
+}
+
static void bakeModifier(struct Main *UNUSED(bmain),
Depsgraph *depsgraph,
GpencilModifierData *md,
@@ -161,7 +211,12 @@ static void bakeModifier(struct Main *UNUSED(bmain),
LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
- deformStroke(md, depsgraph, ob, gpl, gpf, gps);
+ if (GPENCIL_STROKE_TYPE_BEZIER(gps)) {
+ deformBezier(md, depsgraph, ob, gpl, gpf, gps);
+ }
+ else {
+ deformPolyline(md, depsgraph, ob, gpl, gpf, gps);
+ }
}
}
}
@@ -232,7 +287,8 @@ GpencilModifierTypeInfo modifierType_Gpencil_Smooth = {
/* copyData */ copyData,
- /* deformStroke */ deformStroke,
+ /* deformPolyline */ deformPolyline,
+ /* deformBezier */ deformBezier,
/* generateStrokes */ NULL,
/* bakeModifier */ bakeModifier,
/* remapTime */ NULL,
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilsubdiv.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilsubdiv.c
index f444103ae3f..dab909ecd12 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencilsubdiv.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilsubdiv.c
@@ -66,12 +66,12 @@ static void copyData(const GpencilModifierData *md, GpencilModifierData *target)
}
/* subdivide stroke to get more control points */
-static void deformStroke(GpencilModifierData *md,
- Depsgraph *UNUSED(depsgraph),
- Object *ob,
- bGPDlayer *gpl,
- bGPDframe *UNUSED(gpf),
- bGPDstroke *gps)
+static void deformPolyline(GpencilModifierData *md,
+ Depsgraph *UNUSED(depsgraph),
+ Object *ob,
+ bGPDlayer *gpl,
+ bGPDframe *UNUSED(gpf),
+ bGPDstroke *gps)
{
SubdivGpencilModifierData *mmd = (SubdivGpencilModifierData *)md;
bGPdata *gpd = ob->data;
@@ -113,7 +113,7 @@ static void bakeModifier(struct Main *UNUSED(bmain),
LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
- deformStroke(md, depsgraph, ob, gpl, gpf, gps);
+ deformPolyline(md, depsgraph, ob, gpl, gpf, gps);
}
}
}
@@ -162,7 +162,8 @@ GpencilModifierTypeInfo modifierType_Gpencil_Subdiv = {
/* copyData */ copyData,
- /* deformStroke */ deformStroke,
+ /* deformPolyline */ deformPolyline,
+ /* deformBezier */ NULL,
/* generateStrokes */ NULL,
/* bakeModifier */ bakeModifier,
/* remapTime */ NULL,
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpenciltexture.c b/source/blender/gpencil_modifiers/intern/MOD_gpenciltexture.c
index 74297908808..5a424be5e54 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpenciltexture.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpenciltexture.c
@@ -70,12 +70,12 @@ static void copyData(const GpencilModifierData *md, GpencilModifierData *target)
}
/* change stroke uv texture values */
-static void deformStroke(GpencilModifierData *md,
- Depsgraph *UNUSED(depsgraph),
- Object *ob,
- bGPDlayer *gpl,
- bGPDframe *UNUSED(gpf),
- bGPDstroke *gps)
+static void deformPolyline(GpencilModifierData *md,
+ Depsgraph *UNUSED(depsgraph),
+ Object *ob,
+ bGPDlayer *gpl,
+ bGPDframe *UNUSED(gpf),
+ bGPDstroke *gps)
{
TextureGpencilModifierData *mmd = (TextureGpencilModifierData *)md;
const int def_nr = BKE_object_defgroup_name_index(ob, mmd->vgname);
@@ -100,7 +100,7 @@ static void deformStroke(GpencilModifierData *md,
gps->uv_translation[0] += mmd->fill_offset[0];
gps->uv_translation[1] += mmd->fill_offset[1];
gps->uv_scale *= mmd->fill_scale;
- BKE_gpencil_stroke_geometry_update(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
}
if (ELEM(mmd->mode, STROKE, STROKE_AND_FILL)) {
@@ -130,6 +130,18 @@ static void deformStroke(GpencilModifierData *md,
}
}
+/* Deform Bezier. */
+static void deformBezier(GpencilModifierData *md,
+ Depsgraph *depsgraph,
+ Object *ob,
+ bGPDlayer *gpl,
+ bGPDframe *gpf,
+ bGPDstroke *gps)
+{
+ /* Reuse deformPolyline because the UV values of curve are not used yet. */
+ deformPolyline(md, depsgraph, ob, gpl, gpf, gps);
+}
+
static void bakeModifier(struct Main *UNUSED(bmain),
Depsgraph *depsgraph,
GpencilModifierData *md,
@@ -140,7 +152,7 @@ static void bakeModifier(struct Main *UNUSED(bmain),
LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
- deformStroke(md, depsgraph, ob, gpl, gpf, gps);
+ deformPolyline(md, depsgraph, ob, gpl, gpf, gps);
}
}
}
@@ -210,7 +222,8 @@ GpencilModifierTypeInfo modifierType_Gpencil_Texture = {
/* copyData */ copyData,
- /* deformStroke */ deformStroke,
+ /* deformPolyline */ deformPolyline,
+ /* deformBezier */ deformBezier,
/* generateStrokes */ NULL,
/* bakeModifier */ bakeModifier,
/* remapTime */ NULL,
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilthick.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilthick.c
index cac700e15f4..f7fe39caad5 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencilthick.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilthick.c
@@ -37,6 +37,7 @@
#include "BKE_colortools.h"
#include "BKE_context.h"
#include "BKE_deform.h"
+#include "BKE_gpencil_geom.h"
#include "BKE_gpencil_modifier.h"
#include "BKE_lib_query.h"
#include "BKE_modifier.h"
@@ -89,29 +90,34 @@ static void copyData(const GpencilModifierData *md, GpencilModifierData *target)
tgmd->curve_thickness = BKE_curvemapping_copy(gmd->curve_thickness);
}
+static bool do_modifier(Object *ob, ThickGpencilModifierData *mmd, bGPDlayer *gpl, bGPDstroke *gps)
+{
+ return is_stroke_affected_by_modifier(ob,
+ mmd->layername,
+ mmd->material,
+ mmd->pass_index,
+ mmd->layer_pass,
+ 1,
+ gpl,
+ gps,
+ mmd->flag & GP_THICK_INVERT_LAYER,
+ mmd->flag & GP_THICK_INVERT_PASS,
+ mmd->flag & GP_THICK_INVERT_LAYERPASS,
+ mmd->flag & GP_THICK_INVERT_MATERIAL);
+}
+
/* change stroke thickness */
-static void deformStroke(GpencilModifierData *md,
- Depsgraph *UNUSED(depsgraph),
- Object *ob,
- bGPDlayer *gpl,
- bGPDframe *UNUSED(gpf),
- bGPDstroke *gps)
+static void deformPolyline(GpencilModifierData *md,
+ Depsgraph *UNUSED(depsgraph),
+ Object *ob,
+ bGPDlayer *gpl,
+ bGPDframe *UNUSED(gpf),
+ bGPDstroke *gps)
{
ThickGpencilModifierData *mmd = (ThickGpencilModifierData *)md;
const int def_nr = BKE_object_defgroup_name_index(ob, mmd->vgname);
- if (!is_stroke_affected_by_modifier(ob,
- mmd->layername,
- mmd->material,
- mmd->pass_index,
- mmd->layer_pass,
- 1,
- gpl,
- gps,
- mmd->flag & GP_THICK_INVERT_LAYER,
- mmd->flag & GP_THICK_INVERT_PASS,
- mmd->flag & GP_THICK_INVERT_LAYERPASS,
- mmd->flag & GP_THICK_INVERT_MATERIAL)) {
+ if (!do_modifier(ob, mmd, gpl, gps)) {
return;
}
@@ -160,6 +166,59 @@ static void deformStroke(GpencilModifierData *md,
}
}
+static void deformBezier(GpencilModifierData *md,
+ Depsgraph *UNUSED(depsgraph),
+ Object *ob,
+ bGPDlayer *gpl,
+ bGPDframe *UNUSED(gpf),
+ bGPDstroke *gps)
+{
+ ThickGpencilModifierData *mmd = (ThickGpencilModifierData *)md;
+ const int def_nr = BKE_object_defgroup_name_index(ob, mmd->vgname);
+
+ if (!do_modifier(ob, mmd, gpl, gps)) {
+ return;
+ }
+ float stroke_thickness_inv = 1.0f / max_ii(gps->thickness, 1);
+
+ bGPDcurve *gpc = gps->editcurve;
+ for (int i = 0; i < gpc->tot_curve_points; i++) {
+ bGPDcurve_point *pt = &gpc->curve_points[i];
+ MDeformVert *dvert = gpc->dvert != NULL ? &gpc->dvert[i] : NULL;
+
+ /* Verify point is part of vertex group. */
+ float weight = get_modifier_point_weight(
+ dvert, (mmd->flag & GP_THICK_INVERT_VGROUP) != 0, def_nr);
+ if (weight < 0.0f) {
+ continue;
+ }
+
+ float curvef = 1.0f;
+ if ((mmd->flag & GP_THICK_CUSTOM_CURVE) && (mmd->curve_thickness)) {
+ /* Normalize value to evaluate curve. */
+ float value = (float)i / (gpc->tot_curve_points - 1);
+ curvef = BKE_curvemapping_evaluateF(mmd->curve_thickness, 0, value);
+ }
+
+ float target;
+ if (mmd->flag & GP_THICK_NORMALIZE) {
+ target = mmd->thickness * stroke_thickness_inv;
+ target *= curvef;
+ }
+ else {
+ target = pt->pressure * mmd->thickness_fac;
+ weight *= curvef;
+ }
+
+ pt->pressure = interpf(target, pt->pressure, weight);
+
+ CLAMP_MIN(pt->pressure, 0.0f);
+ /* Calc geometry data. */
+ bGPdata *gpd = ob->data;
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
+ }
+}
+
static void bakeModifier(struct Main *UNUSED(bmain),
Depsgraph *depsgraph,
GpencilModifierData *md,
@@ -170,7 +229,12 @@ static void bakeModifier(struct Main *UNUSED(bmain),
LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
- deformStroke(md, depsgraph, ob, gpl, gpf, gps);
+ if (!GPENCIL_STROKE_TYPE_BEZIER(gps)) {
+ deformPolyline(md, depsgraph, ob, gpl, gpf, gps);
+ }
+ else {
+ deformBezier(md, depsgraph, ob, gpl, gpf, gps);
+ }
}
}
}
@@ -236,7 +300,8 @@ GpencilModifierTypeInfo modifierType_Gpencil_Thick = {
/* copyData */ copyData,
- /* deformStroke */ deformStroke,
+ /* deformPolyline */ deformPolyline,
+ /* deformBezier */ deformBezier,
/* generateStrokes */ NULL,
/* bakeModifier */ bakeModifier,
/* remapTime */ NULL,
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpenciltime.c b/source/blender/gpencil_modifiers/intern/MOD_gpenciltime.c
index e743a724425..ba1074f4a6b 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpenciltime.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpenciltime.c
@@ -258,7 +258,8 @@ GpencilModifierTypeInfo modifierType_Gpencil_Time = {
/* copyData */ copyData,
- /* deformStroke */ NULL,
+ /* deformPolyline */ NULL,
+ /* deformBezier */ NULL,
/* generateStrokes */ NULL,
/* bakeModifier */ NULL,
/* remapTime */ remapTime,
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpenciltint.c b/source/blender/gpencil_modifiers/intern/MOD_gpenciltint.c
index 680f5ab05ec..26637e53168 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpenciltint.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpenciltint.c
@@ -113,12 +113,12 @@ static void copyData(const GpencilModifierData *md, GpencilModifierData *target)
}
/* deform stroke */
-static void deformStroke(GpencilModifierData *md,
- Depsgraph *UNUSED(depsgraph),
- Object *ob,
- bGPDlayer *gpl,
- bGPDframe *UNUSED(gpf),
- bGPDstroke *gps)
+static void deformPolyline(GpencilModifierData *md,
+ Depsgraph *UNUSED(depsgraph),
+ Object *ob,
+ bGPDlayer *gpl,
+ bGPDframe *UNUSED(gpf),
+ bGPDstroke *gps)
{
TintGpencilModifierData *mmd = (TintGpencilModifierData *)md;
if ((mmd->type == GP_TINT_GRADIENT) && (!mmd->object)) {
@@ -268,6 +268,18 @@ static void deformStroke(GpencilModifierData *md,
}
}
+/* Deform Bezier. */
+static void deformBezier(GpencilModifierData *md,
+ Depsgraph *depsgraph,
+ Object *ob,
+ bGPDlayer *gpl,
+ bGPDframe *gpf,
+ bGPDstroke *gps)
+{
+ /* Reuse deformPolyline because the changes are not affecting the geometry. */
+ deformPolyline(md, depsgraph, ob, gpl, gpf, gps);
+}
+
/* FIXME: Ideally we be doing this on a copy of the main depsgraph
* (i.e. one where we don't have to worry about restoring state)
*/
@@ -295,7 +307,7 @@ static void bakeModifier(Main *UNUSED(bmain),
/* compute effects on this frame */
LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
- deformStroke(md, depsgraph, ob, gpl, gpf, gps);
+ deformPolyline(md, depsgraph, ob, gpl, gpf, gps);
}
}
}
@@ -413,7 +425,8 @@ GpencilModifierTypeInfo modifierType_Gpencil_Tint = {
/* copyData */ copyData,
- /* deformStroke */ deformStroke,
+ /* deformPolyline */ deformPolyline,
+ /* deformBezier */ deformBezier,
/* generateStrokes */ NULL,
/* bakeModifier */ bakeModifier,
/* remapTime */ NULL,
diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilweight.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilweight.c
index c7fe20edef7..bf9269582ae 100644
--- a/source/blender/gpencil_modifiers/intern/MOD_gpencilweight.c
+++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilweight.c
@@ -96,12 +96,12 @@ static float calc_point_weight_by_distance(Object *ob,
}
/* change stroke thickness */
-static void deformStroke(GpencilModifierData *md,
- Depsgraph *UNUSED(depsgraph),
- Object *ob,
- bGPDlayer *gpl,
- bGPDframe *UNUSED(gpf),
- bGPDstroke *gps)
+static void deformPolyline(GpencilModifierData *md,
+ Depsgraph *UNUSED(depsgraph),
+ Object *ob,
+ bGPDlayer *gpl,
+ bGPDframe *UNUSED(gpf),
+ bGPDstroke *gps)
{
WeightGpencilModifierData *mmd = (WeightGpencilModifierData *)md;
const int def_nr = BKE_object_defgroup_name_index(ob, mmd->vgname);
@@ -205,6 +205,18 @@ static void deformStroke(GpencilModifierData *md,
}
}
+/* Deform Bezier. */
+static void deformBezier(GpencilModifierData *md,
+ Depsgraph *depsgraph,
+ Object *ob,
+ bGPDlayer *gpl,
+ bGPDframe *gpf,
+ bGPDstroke *gps)
+{
+ /* Reuse deformPolyline. */
+ deformPolyline(md, depsgraph, ob, gpl, gpf, gps);
+}
+
static void bakeModifier(struct Main *UNUSED(bmain),
Depsgraph *depsgraph,
GpencilModifierData *md,
@@ -215,7 +227,7 @@ static void bakeModifier(struct Main *UNUSED(bmain),
LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
- deformStroke(md, depsgraph, ob, gpl, gpf, gps);
+ deformPolyline(md, depsgraph, ob, gpl, gpf, gps);
}
}
}
@@ -319,7 +331,8 @@ GpencilModifierTypeInfo modifierType_Gpencil_Weight = {
/* copyData */ copyData,
- /* deformStroke */ deformStroke,
+ /* deformPolyline */ deformPolyline,
+ /* deformBezier */ deformBezier,
/* generateStrokes */ NULL,
/* bakeModifier */ bakeModifier,
/* remapTime */ NULL,
diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c
index 3f722f33946..1eb717040af 100644
--- a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c
+++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c
@@ -4338,7 +4338,7 @@ static void lineart_gpencil_generate(LineartCache *cache,
if (G.debug_value == 4000) {
BKE_gpencil_stroke_set_random_color(gps);
}
- BKE_gpencil_stroke_geometry_update(gpencil_object->data, gps);
+ BKE_gpencil_stroke_geometry_update(gpencil_object->data, gps, GP_GEO_UPDATE_DEFAULT);
stroke_count++;
}
diff --git a/source/blender/io/gpencil/intern/gpencil_io_import_svg.cc b/source/blender/io/gpencil/intern/gpencil_io_import_svg.cc
index 52fcc017ffb..39bfb2e5ba6 100644
--- a/source/blender/io/gpencil/intern/gpencil_io_import_svg.cc
+++ b/source/blender/io/gpencil/intern/gpencil_io_import_svg.cc
@@ -222,7 +222,7 @@ void GpencilImporterSVG::create_stroke(bGPdata *gpd,
/* Cleanup and recalculate geometry. */
BKE_gpencil_stroke_merge_distance(gpd, gpf, gps, 0.001f, true);
- BKE_gpencil_stroke_geometry_update(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
}
/* Unpack internal NanoSVG color. */
diff --git a/source/blender/makesdna/DNA_brush_enums.h b/source/blender/makesdna/DNA_brush_enums.h
index 6563afd2b4a..cd1f5aeb68d 100644
--- a/source/blender/makesdna/DNA_brush_enums.h
+++ b/source/blender/makesdna/DNA_brush_enums.h
@@ -44,6 +44,7 @@ typedef enum eGPBrush_Presets {
GP_BRUSH_PRESET_ERASER_POINT = 12,
GP_BRUSH_PRESET_ERASER_STROKE = 13,
GP_BRUSH_PRESET_TINT = 14,
+ GP_BRUSH_PRESET_INK_CURVE = 15,
/* Vertex Paint 100-199. */
GP_BRUSH_PRESET_VERTEX_DRAW = 100,
@@ -101,6 +102,8 @@ typedef enum eGPDbrush_Flag {
GP_BRUSH_OCCLUDE_ERASER = (1 << 15),
/* Post process trim stroke */
GP_BRUSH_TRIM_STROKE = (1 << 16),
+ /* Indicates that the stroke will be converted to a curve automatically. */
+ GP_BRUSH_CURVE_DATA = (1 << 17),
} eGPDbrush_Flag;
typedef enum eGPDbrush_Flag2 {
@@ -554,6 +557,7 @@ typedef enum eBrushGPaintTool {
GPAINT_TOOL_FILL = 1,
GPAINT_TOOL_ERASE = 2,
GPAINT_TOOL_TINT = 3,
+ GPAINT_TOOL_CURVE = 4, /* UNUSED */
} eBrushGPaintTool;
/* BrushGpencilSettings->brush type */
diff --git a/source/blender/makesdna/DNA_gpencil_types.h b/source/blender/makesdna/DNA_gpencil_types.h
index c573de6b54e..8874d7e3ca0 100644
--- a/source/blender/makesdna/DNA_gpencil_types.h
+++ b/source/blender/makesdna/DNA_gpencil_types.h
@@ -107,13 +107,14 @@ typedef struct bGPDspoint {
typedef enum eGPDspoint_Flag {
/* stroke point is selected (for editing) */
GP_SPOINT_SELECT = (1 << 0),
-
/* stroke point is tagged (for some editing operation) */
GP_SPOINT_TAG = (1 << 1),
/* stroke point is temp tagged (for some editing operation) */
GP_SPOINT_TEMP_TAG = (1 << 2),
/* stroke point is temp tagged (for some editing operation) */
GP_SPOINT_TEMP_TAG2 = (1 << 3),
+ /* Stroke point is "linked" to control point of bezier handle. */
+ GP_SPOINT_IS_BEZT_CONTROL = (1 << 4),
} eGPSPoint_Flag;
/* ***************************************** */
@@ -183,6 +184,14 @@ typedef enum eGPDpalette_Flag {
/* ***************************************** */
/* GP Curve Point */
+typedef struct bGPDcpoint_Runtime {
+ /* Original curve point */
+ struct bGPDcurve_point *gpc_pt_orig;
+ /* Original index array position */
+ int idx_orig;
+ char _pad0[4];
+} bGPDcpoint_Runtime;
+
typedef struct bGPDcurve_point {
/** Bezier Triple for the handles and control points. */
BezTriple bezt;
@@ -205,12 +214,14 @@ typedef struct bGPDcurve_point {
/** Vertex Color RGBA (A=mix factor). */
float vert_color[4];
- char _pad[4];
+
+ bGPDcpoint_Runtime runtime;
} bGPDcurve_point;
/* bGPDcurve_point->flag */
typedef enum eGPDcurve_point_Flag {
GP_CURVE_POINT_SELECT = (1 << 0),
+ GP_CURVE_POINT_TAG = (1 << 1),
} eGPDcurve_point_Flag;
/* ***************************************** */
@@ -218,19 +229,22 @@ typedef enum eGPDcurve_point_Flag {
/* Curve for Bezier Editing. */
typedef struct bGPDcurve {
- /** Array of BezTriple. */
+ /** Array of curve points. */
bGPDcurve_point *curve_points;
/** Total number of curve points. */
int tot_curve_points;
/** General flag. */
short flag;
char _pad[2];
+ /** Vertex weight data. */
+ struct MDeformVert *dvert;
+ void *_pad2;
+
} bGPDcurve;
/* bGPDcurve_Flag->flag */
typedef enum bGPDcurve_Flag {
- /* Flag to indicated that the stroke data has been changed and the curve needs to be refitted */
- GP_CURVE_NEEDS_STROKE_UPDATE = (1 << 0),
+ /* GP_CURVE_NEEDS_STROKE_UPDATE = (1 << 0), */ /* Deprecated */
/* Curve is selected */
GP_CURVE_SELECT = (1 << 1),
} bGPDcurve_Flag;
@@ -341,9 +355,6 @@ typedef enum eGPDstroke_Flag {
/* Flag used to indicate that stroke is used for fill close and must use
* fill color for stroke and no fill area */
GP_STROKE_NOFILL = (1 << 8),
- /* Flag to indicated that the editcurve has been changed and the stroke needs to be updated with
- * the curve data */
- GP_STROKE_NEEDS_CURVE_UPDATE = (1 << 9),
/* only for use with stroke-buffer (while drawing arrows) */
GP_STROKE_USE_ARROW_START = (1 << 12),
/* only for use with stroke-buffer (while drawing arrows) */
@@ -657,10 +668,6 @@ typedef struct bGPdata {
int flag;
/** Default resolution for generated curves using curve editing method. */
int curve_edit_resolution;
- /** Curve Editing error threshold. */
- float curve_edit_threshold;
- /** Curve Editing corner angle (less or equal is treated as corner). */
- float curve_edit_corner_angle;
/* Palettes */
/** List of bGPDpalette's - Deprecated (2.78 - 2.79 only). */
@@ -871,6 +878,17 @@ typedef enum eGP_DrawMode {
#define GPENCIL_PLAY_ON(gpd) ((gpd) && ((gpd)->runtime.playing == 1))
+/* True if the stroke has curve data (is of type BEZIER). */
+#define GPENCIL_STROKE_TYPE_BEZIER(gps) ((gps) && ((gps)->editcurve != NULL))
+
+/* True if the stroke is of type POLY */
+#define GPENCIL_STROKE_TYPE_POLY(gps) ((gps) && ((gps)->editcurve == NULL))
+
+/* True if the stroke type of gps_a and gps_b is equal. */
+#define GPENCIL_STROKE_TYPE_EQ(gps_a, gps_b) \
+ ((GPENCIL_STROKE_TYPE_BEZIER(gps_a) && GPENCIL_STROKE_TYPE_BEZIER(gps_b)) || \
+ (GPENCIL_STROKE_TYPE_POLY(gps_a) && GPENCIL_STROKE_TYPE_POLY(gps_b)))
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/makesdna/DNA_scene_defaults.h b/source/blender/makesdna/DNA_scene_defaults.h
index 1a2a8892e64..cf09c277af5 100644
--- a/source/blender/makesdna/DNA_scene_defaults.h
+++ b/source/blender/makesdna/DNA_scene_defaults.h
@@ -376,6 +376,9 @@
.gpencil_v2d_align = GP_PROJECT_VIEWSPACE, \
.gpencil_seq_align = GP_PROJECT_VIEWSPACE, \
.gpencil_ima_align = GP_PROJECT_VIEWSPACE, \
+ /* GP Curve Fitting */ \
+ .gpencil_curve_fit_threshold = GP_DEFAULT_CURVE_ERROR, \
+ .gpencil_curve_fit_corner_angle = GP_DEFAULT_CURVE_EDIT_CORNER_ANGLE,\
}
/* clang-format off */
diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h
index cd752b220a3..15cae8c47ce 100644
--- a/source/blender/makesdna/DNA_scene_types.h
+++ b/source/blender/makesdna/DNA_scene_types.h
@@ -483,7 +483,7 @@ typedef struct ImageFormatData {
#define R_IMF_IMTYPE_INVALID 255
/** #ImageFormatData.flag */
-#define R_IMF_FLAG_ZBUF (1 << 0) /* was R_OPENEXR_ZBUF */
+#define R_IMF_FLAG_ZBUF (1 << 0) /* was R_OPENEXR_ZBUF */
#define R_IMF_FLAG_PREVIEW_JPG (1 << 1) /* was R_PREVIEW_JPG */
/* Return values from #BKE_imtype_valid_depths, note this is depths per channel. */
@@ -525,8 +525,8 @@ typedef enum eImageFormatDepth {
/** #ImageFormatData.jp2_flag */
#define R_IMF_JP2_FLAG_YCC (1 << 0) /* when disabled use RGB */ /* was R_JPEG2K_YCC */
-#define R_IMF_JP2_FLAG_CINE_PRESET (1 << 1) /* was R_JPEG2K_CINE_PRESET */
-#define R_IMF_JP2_FLAG_CINE_48 (1 << 2) /* was R_JPEG2K_CINE_48FPS */
+#define R_IMF_JP2_FLAG_CINE_PRESET (1 << 1) /* was R_JPEG2K_CINE_PRESET */
+#define R_IMF_JP2_FLAG_CINE_48 (1 << 2) /* was R_JPEG2K_CINE_48FPS */
/** #ImageFormatData.jp2_codec */
#define R_IMF_JP2_CODEC_JP2 0
@@ -1437,6 +1437,11 @@ typedef struct ToolSettings {
/* Particle Editing */
struct ParticleEditSettings particle;
+ /** Curve Fit error threshold. */
+ float gpencil_curve_fit_threshold;
+ /** Curve Fit corner angle (less or equal is treated as corner). */
+ float gpencil_curve_fit_corner_angle;
+
/* Transform Proportional Area of Effect */
float proportional_size;
@@ -1842,12 +1847,12 @@ typedef struct Scene {
#define R_MODE_UNUSED_20 (1 << 20) /* cleared */
#define R_MODE_UNUSED_21 (1 << 21) /* cleared */
-#define R_NO_OVERWRITE (1 << 22) /* skip existing files */
-#define R_TOUCH (1 << 23) /* touch files before rendering */
+#define R_NO_OVERWRITE (1 << 22) /* skip existing files */
+#define R_TOUCH (1 << 23) /* touch files before rendering */
#define R_SIMPLIFY (1 << 24)
-#define R_EDGE_FRS (1 << 25) /* R_EDGE reserved for Freestyle */
+#define R_EDGE_FRS (1 << 25) /* R_EDGE reserved for Freestyle */
#define R_PERSISTENT_DATA (1 << 26) /* keep data around for re-render */
-#define R_MODE_UNUSED_27 (1 << 27) /* cleared */
+#define R_MODE_UNUSED_27 (1 << 27) /* cleared */
/** #RenderData.seq_flag */
enum {
@@ -2306,6 +2311,8 @@ typedef enum eGPencil_Flags {
GP_TOOL_FLAG_CREATE_WEIGHTS = (1 << 4),
/* Automerge with last stroke */
GP_TOOL_FLAG_AUTOMERGE_STROKE = (1 << 5),
+ /* Convert all strokes to Bezier */
+ GP_TOOL_FLAG_BEZIER_MODE = (1 << 6),
} eGPencil_Flags;
/** #Scene.r.simplify_gpencil */
diff --git a/source/blender/makesdna/intern/dna_defaults.c b/source/blender/makesdna/intern/dna_defaults.c
index 03f7dbf6489..d482dc91d7d 100644
--- a/source/blender/makesdna/intern/dna_defaults.c
+++ b/source/blender/makesdna/intern/dna_defaults.c
@@ -94,6 +94,7 @@
#include "DNA_curve_types.h"
#include "DNA_fluid_types.h"
#include "DNA_gpencil_modifier_types.h"
+#include "DNA_gpencil_types.h"
#include "DNA_hair_types.h"
#include "DNA_image_types.h"
#include "DNA_key_types.h"
diff --git a/source/blender/makesrna/intern/rna_brush.c b/source/blender/makesrna/intern/rna_brush.c
index 2b09ea51a84..7b221dfb32d 100644
--- a/source/blender/makesrna/intern/rna_brush.c
+++ b/source/blender/makesrna/intern/rna_brush.c
@@ -192,6 +192,13 @@ const EnumPropertyItem rna_enum_brush_gpencil_types_items[] = {
ICON_BRUSH_TEXDRAW,
"Tint",
"The brush is of type used for tinting strokes"},
+#if 0
+ {GPAINT_TOOL_CURVE,
+ "CURVE",
+ ICON_STROKE,
+ "Curve",
+ "The brush is of type used for drawing curves"},
+#endif
{0, NULL, 0, NULL, NULL},
};
@@ -1904,6 +1911,11 @@ static void rna_def_gpencil_options(BlenderRNA *brna)
RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_BRUSH_OCCLUDE_ERASER);
RNA_def_property_ui_text(prop, "Occlude Eraser", "Erase only strokes visible and not occluded");
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+
+ prop = RNA_def_property(srna, "use_curve_data", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_BRUSH_CURVE_DATA);
+ RNA_def_property_ui_text(prop, "Generate Curve", "Create Curve data for the new stroke");
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
}
static void rna_def_brush(BlenderRNA *brna)
diff --git a/source/blender/makesrna/intern/rna_gpencil.c b/source/blender/makesrna/intern/rna_gpencil.c
index aad6f1231dd..bf313444de5 100644
--- a/source/blender/makesrna/intern/rna_gpencil.c
+++ b/source/blender/makesrna/intern/rna_gpencil.c
@@ -193,43 +193,16 @@ static void rna_GpencilLayerMatrix_update(Main *bmain, Scene *scene, PointerRNA
rna_GPencil_update(bmain, scene, ptr);
}
-static void rna_GPencil_curve_edit_mode_toggle(Main *bmain, Scene *scene, PointerRNA *ptr)
-{
- ToolSettings *ts = scene->toolsettings;
- bGPdata *gpd = (bGPdata *)ptr->owner_id;
-
- /* Curve edit mode is turned on. */
- if (GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd)) {
- /* If the current select mode is segment and the Bezier mode is on, change
- * to Point because segment is not supported. */
- if (ts->gpencil_selectmode_edit == GP_SELECTMODE_SEGMENT) {
- ts->gpencil_selectmode_edit = GP_SELECTMODE_POINT;
- }
-
- BKE_gpencil_strokes_selected_update_editcurve(gpd);
- }
- /* Curve edit mode is turned off. */
- else {
- BKE_gpencil_strokes_selected_sync_selection_editcurve(gpd);
- }
-
- /* Standard update. */
- rna_GPencil_update(bmain, scene, ptr);
-}
-
static void rna_GPencil_stroke_curve_update(Main *bmain, Scene *scene, PointerRNA *ptr)
{
bGPdata *gpd = (bGPdata *)ptr->owner_id;
- if (GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd)) {
- LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
- if (gpl->actframe != NULL) {
- bGPDframe *gpf = gpl->actframe;
- LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
- if (gps->editcurve != NULL) {
- gps->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
- BKE_gpencil_stroke_geometry_update(gpd, gps);
- }
+ LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
+ if (gpl->actframe != NULL) {
+ bGPDframe *gpf = gpl->actframe;
+ LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
+ if (gps->editcurve != NULL) {
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
}
}
}
@@ -238,26 +211,6 @@ static void rna_GPencil_stroke_curve_update(Main *bmain, Scene *scene, PointerRN
rna_GPencil_update(bmain, scene, ptr);
}
-static void rna_GPencil_stroke_curve_resolution_update(Main *bmain, Scene *scene, PointerRNA *ptr)
-{
- bGPdata *gpd = (bGPdata *)ptr->owner_id;
-
- if (GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd)) {
- LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
- if (gpl->actframe != NULL) {
- bGPDframe *gpf = gpl->actframe;
- LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
- if (gps->editcurve != NULL) {
- gps->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
- BKE_gpencil_stroke_geometry_update(gpd, gps);
- }
- }
- }
- }
- }
- rna_GPencil_update(bmain, scene, ptr);
-}
-
static void rna_GPencil_dependency_update(Main *bmain, Scene *UNUSED(scene), PointerRNA *ptr)
{
DEG_id_tag_update(ptr->owner_id, ID_RECALC_TRANSFORM);
@@ -275,7 +228,7 @@ static void rna_GPencil_uv_update(Main *UNUSED(bmain), Scene *UNUSED(scene), Poi
bGPDstroke *gps = (bGPDstroke *)ptr->data;
/* Calc geometry data. */
- BKE_gpencil_stroke_geometry_update(gpd, gps);
+ BKE_gpencil_stroke_geometry_update(gpd, gps, GP_GEO_UPDATE_DEFAULT);
DEG_id_tag_update(ptr->owner_id, ID_RECALC_GEOMETRY);
WM_main_add_notifier(NC_GPENCIL | NA_EDITED, NULL);
@@ -749,7 +702,7 @@ static void rna_GPencil_stroke_point_add(
stroke->totpoints += count;
/* Calc geometry data. */
- BKE_gpencil_stroke_geometry_update(gpd, stroke);
+ BKE_gpencil_stroke_geometry_update(gpd, stroke, GP_GEO_UPDATE_DEFAULT);
DEG_id_tag_update(&gpd->id,
ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE);
@@ -810,7 +763,7 @@ static void rna_GPencil_stroke_point_pop(ID *id,
}
/* Calc geometry data. */
- BKE_gpencil_stroke_geometry_update(gpd, stroke);
+ BKE_gpencil_stroke_geometry_update(gpd, stroke, GP_GEO_UPDATE_DEFAULT);
DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE);
@@ -823,7 +776,7 @@ static void rna_GPencil_stroke_point_update(ID *id, bGPDstroke *stroke)
/* Calc geometry data. */
if (stroke) {
- BKE_gpencil_stroke_geometry_update(gpd, stroke);
+ BKE_gpencil_stroke_geometry_update(gpd, stroke, GP_GEO_UPDATE_DEFAULT);
DEG_id_tag_update(&gpd->id,
ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE);
@@ -2525,11 +2478,8 @@ static void rna_def_gpencil_data(BlenderRNA *brna)
RNA_def_property_int_default(prop, GP_DEFAULT_CURVE_RESOLUTION);
RNA_def_parameter_clear_flags(prop, PROP_ANIMATABLE, 0);
RNA_def_property_ui_text(
- prop,
- "Curve Resolution",
- "Number of segments generated between control points when editing strokes in curve mode");
- RNA_def_property_update(
- prop, NC_GPENCIL | ND_DATA, "rna_GPencil_stroke_curve_resolution_update");
+ prop, "Bézier Curve Resolution", "Number of segments generated between control points");
+ RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_stroke_curve_update");
prop = RNA_def_property(srna, "use_adaptive_curve_resolution", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_DATA_CURVE_ADAPTIVE_RESOLUTION);
@@ -2539,25 +2489,7 @@ static void rna_def_gpencil_data(BlenderRNA *brna)
"Set the resolution of each editcurve segment dynamically depending on "
"the length of the segment. The resolution is the number of points "
"generated per unit distance");
- RNA_def_property_update(
- prop, NC_GPENCIL | ND_DATA, "rna_GPencil_stroke_curve_resolution_update");
-
- /* Curve editing error threshold. */
- prop = RNA_def_property(srna, "curve_edit_threshold", PROP_FLOAT, PROP_FACTOR);
- RNA_def_property_float_sdna(prop, NULL, "curve_edit_threshold");
- RNA_def_property_range(prop, FLT_MIN, 10.0);
- RNA_def_property_float_default(prop, GP_DEFAULT_CURVE_ERROR);
- RNA_def_property_ui_text(prop, "Threshold", "Curve conversion error threshold");
- RNA_def_property_ui_range(prop, FLT_MIN, 10.0, 2, 5);
- RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
-
- /* Curve editing corner angle. */
- prop = RNA_def_property(srna, "curve_edit_corner_angle", PROP_FLOAT, PROP_ANGLE);
- RNA_def_property_float_sdna(prop, NULL, "curve_edit_corner_angle");
- RNA_def_property_range(prop, 0.0f, DEG2RADF(180.0f));
- RNA_def_property_float_default(prop, DEG2RADF(90.0f));
- RNA_def_property_ui_text(prop, "Corner Angle", "Angle threshold to be treated as corners");
- RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+ RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_stroke_curve_update");
prop = RNA_def_property(srna, "use_multiedit", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_DATA_STROKE_MULTIEDIT);
@@ -2567,11 +2499,6 @@ static void rna_def_gpencil_data(BlenderRNA *brna)
"(keyframes must be selected to be included)");
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_update");
- prop = RNA_def_property(srna, "use_curve_edit", PROP_BOOLEAN, PROP_NONE);
- RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_DATA_CURVE_EDIT_MODE);
- RNA_def_property_ui_text(prop, "Curve Editing", "Edit strokes using curve handles");
- RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, "rna_GPencil_curve_edit_mode_toggle");
-
prop = RNA_def_property(srna, "use_autolock_layers", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_DATA_AUTOLOCK_LAYERS);
RNA_def_property_ui_text(
diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c
index 0a91d5f01bc..40f072bea93 100644
--- a/source/blender/makesrna/intern/rna_scene.c
+++ b/source/blender/makesrna/intern/rna_scene.c
@@ -3035,6 +3035,21 @@ static void rna_def_tool_settings(BlenderRNA *brna)
RNA_def_property_range(prop, 0.0, 1.0);
RNA_def_property_ui_range(prop, 0.0, 0.1, 0.01, 6);
+ /* Grease Pencil Curve fitting error threshold. */
+ prop = RNA_def_property(srna, "gpencil_curve_fit_threshold", PROP_FLOAT, PROP_FACTOR);
+ RNA_def_property_float_sdna(prop, NULL, "gpencil_curve_fit_threshold");
+ RNA_def_property_range(prop, FLT_MIN, 10.0);
+ RNA_def_property_ui_text(prop, "Threshold", "Curve conversion error threshold");
+ RNA_def_property_ui_range(prop, FLT_MIN, 10.0, 2, 5);
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+
+ /* Grease Pencil Curve fitting corner angle. */
+ prop = RNA_def_property(srna, "gpencil_curve_fit_corner_angle", PROP_FLOAT, PROP_ANGLE);
+ RNA_def_property_float_sdna(prop, NULL, "gpencil_curve_fit_corner_angle");
+ RNA_def_property_range(prop, 0.0f, DEG2RADF(180.0f));
+ RNA_def_property_ui_text(prop, "Corner Angle", "Angle threshold to be treated as corners");
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+
/* Pivot Point */
prop = RNA_def_property(srna, "transform_pivot_point", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "transform_pivot_point");
@@ -3232,6 +3247,12 @@ static void rna_def_tool_settings(BlenderRNA *brna)
"Join by distance last drawn stroke with previous strokes in the active layer");
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+ prop = RNA_def_property(srna, "use_gpencil_bezier_mode", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "gpencil_flags", GP_TOOL_FLAG_BEZIER_MODE);
+ RNA_def_property_boolean_default(prop, false);
+ RNA_def_property_ui_text(prop, "Bezier Mode", "Convert all strokes to Bezier curves");
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+
prop = RNA_def_property(srna, "gpencil_sculpt", PROP_POINTER, PROP_NONE);
RNA_def_property_pointer_sdna(prop, NULL, "gp_sculpt");
RNA_def_property_struct_type(prop, "GPencilSculptSettings");