# How to sculpt a continuous brush stroke with python?

I'm trying to deform a mesh using python. My application is to randomly deform thousands of shapes, so I need to use a python script. With the GUI, I really like the ability to hold left-click while moving the mouse across a shape, which results in a smooth and continuous deformation. I want to emulate this with python.

The best code example of how to use the sculpt function with python comes from this code example.

There, the bpy.ops.sculpt.brush_stroke() command is given a list of strokes to apply, with the following form:

strokes = []
for i, coordinate in enumerate(coordinates):
stroke = {
"name": "stroke",
"mouse": (0,0),
"pen_flip" : pen_flip,
"is_start": True if i==0 else False,
"location": coordinate,
"size": 50,
"pressure": 1,
"time": float(i)
}
strokes.append(stroke)


However, despite the 'is_start' key being set to False for all but the first stroke, it appears to me that blender applies these strokes as if they were all independent left-clicks; it doesn't emulate the smoothness of holding down the left-mouse-button.

Here's my full script:

import bpy
import random
import numpy as np
import math

# Clear all objects
try:
bpy.ops.object.mode_set(mode='OBJECT')
except:
pass
for obj in bpy.context.scene.objects:
obj.select_set(True)
bpy.ops.object.delete()

# Create basic ico_sphere with 7 subdivisions
bpy.ops.object.mode_set(mode='SCULPT')
bpy.data.objects[0].select_set(True)
bpy.context.scene.tool_settings.sculpt.use_symmetry_x = False

# Must override context while stroke is applied
def context_override():
for window in bpy.context.window_manager.windows:
screen = window.screen
for area in screen.areas:
if area.type == 'VIEW_3D':
for region in area.regions:
if region.type == 'WINDOW':
return {'window': window, 'screen': screen, 'area': area, 'region': region, 'scene': bpy.context.scene}

# Define brush stroke parameters
num_steps = 5
steps = np.linspace(0, np.pi/2, num_steps, endpoint=False)
points = []
for i, s in enumerate(steps):
px = np.sin(s)
py = np.cos(s)
pz = 0 # np.sin(s)*np.cos(s)
points.append([px, py, pz])

strokes = []
for i, p in enumerate(points):
stroke = {
"name": "stroke",
"mouse": (0, 0),
"pen_flip": False,
"is_start": True, # if i==0 else False,
"location": p,
"size": 1.0,
"pressure": 1.0, #
"time": float(i)}
strokes.append(stroke)

# Set brush settings
bpy.data.scenes["Scene"].tool_settings.unified_paint_settings.use_locked_size = "SCENE"
bpy.ops.paint.brush_select(sculpt_tool='BLOB', toggle=False)
bpy.data.brushes["Blob"].strength = 1
bpy.data.brushes["Blob"].curve_preset = "SMOOTH"
bpy.data.brushes["Blob"].auto_smooth_factor = 1.0

# Apply stroke
bpy.ops.sculpt.brush_stroke(context_override(), stroke=strokes)


Running that script with num_steps=5, I get this result, which is choppy.

If I run the script with num_steps=50, the result appears smoother, but there is some unevenness; if you look closely there is a bump on the right-most portion of the deformation.

Finally, if I run the script with num_steps=500, the result is also smooth, but it's clear that the effect is accumulating (the entire deformation is larger in intensity). This would not happen if I used the GUI and just held down the left-mouse-button. For this reason, just increasing the number of steps between two points in order to achieve smoothness is not ideal because the spacing of the steps causes the deformation to have a varying intensity.

In short, how can I smoothly sculpt along the surface between two points on a mesh using a python script? bpy.ops.sculpt.brush_stroke() appears to apply strokes independently.