# Need help with my modal operator

I’m making a modal that draws & places empties on a surface, but would like to include steps.

I have this hack which uses the mouse path.

``````self.mouse_path.append((event.mouse_region_x, event.mouse_region_y))
play = len(self.mouse_path)

index=  play * 0.1 %  20

step = -index % 2

if self.lmb:
for x in range(0, 10):
if step == x:

``````

Is there a better way of doing this?

I’m not sure I understand your question… what are “steps”? Are you talking about loop steps (in which case, just use the optional third parameter of the range function)? are you talking about interpolating the mouse position?

Hey, so I’m trying to make a modal where you can click and drag on a surface and it adds an empty. How would I use that range function?

range is a built in function: https://docs.python.org/3.5/library/stdtypes.html#range

If by steps you mean a fixed distance between each empty you’ll need:

1. Raycast start location
2. Raycast end location
3. Interpolated vectors between 1. and 2. using a fixed distance.

Example which projects an empty onto a surface based on the surface distance `self.length`.
Run and search for generic operator.

``````import bpy
from mathutils import Vector
from bpy_extras.view3d_utils import (region_2d_to_vector_3d,
region_2d_to_origin_3d)

def raycast(context, event):
mxy = event.mouse_region_x, event.mouse_region_y
region = context.region
rv3d = context.region_data
if rv3d:
vl = context.view_layer
origin = region_2d_to_origin_3d(region, rv3d, mxy)
direction = region_2d_to_vector_3d(region, rv3d, mxy)
hit, loc, *_ = context.scene.ray_cast(vl, origin, direction)
if hit:
return loc

class WM_OT_generic_operator(bpy.types.Operator):
bl_idname = "wm.generic_operator"
bl_label = "Generic Operator"

ob = bpy.data.objects.new("Empty", None)
ob.location = loc
ob.empty_display_size = 0.1
self.objects.append(ob)

def modal(self, context, event):
if event.type in {'MIDDLEMOUSE', 'WHEELUPMOUSE', 'WHEELDOWNMOUSE'}:
return {'PASS_THROUGH'}

if event.type in {'ESC', 'RET'}:
if event.type == 'ESC':
for ob in self.objects:
bpy.data.objects.remove(ob)
return {'FINISHED'}

if self.active and event.type == 'MOUSEMOVE':
end_loc = raycast(context, event)

if self.start_loc and end_loc:
distance = (end_loc - self.start_loc).length

while distance >= self.length:
factor = self.length / distance
location = self.start_loc.lerp(end_loc, factor)
self.start_loc = location
distance -= self.length

if event.type == 'LEFTMOUSE':
if not self.active:
self.start_loc = raycast(context, event)
if self.start_loc:
self.active = event.value == 'PRESS'
return {'RUNNING_MODAL'}

def invoke(self, context, event):
self.objects = []
self.start_loc = None
wm = context.window_manager