def CreatePolygon(self, context): me = bpy.data.meshes.new('C_Poly') ob = bpy.data.objects.new('C_Poly', me) self.CurrentObj = ob scene = context.scene region = context.region rv3d = context.region_data coord = self.mouse_path[0][0], self.mouse_path[0][1] depthLocation = region_2d_to_vector_3d(region, rv3d, coord) self.ViewVector = depthLocation loc = region_2d_to_location_3d(region, rv3d, coord, depthLocation) mat = context.space_data.region_3d.view_matrix.transposed().to_3x3().to_4x4() ob.matrix_world = mat ob.location = loc bpy.context.scene.objects.link(ob) t_bm = bmesh.new() t_bm.from_mesh(me) # Parcours tous les points et les convertit avant de les sauvegarder FacesList = [] NbVertices = 0 for x, y in self.mouse_path: v0 = x + self.xpos, y + self.ypos vec = region_2d_to_vector_3d(region, rv3d, v0) loc0 = region_2d_to_location_3d(region, rv3d, v0, vec) - loc loc0 = loc0 * mat x0 = loc0[0] y0 = loc0[1] z0 = loc0[2] NbVertices += 1 if(NbVertices == 1): t_v0 = t_bm.verts.new((x0, y0, z0)) t_init = t_v0 xInit = x0 yInit = y0 zInit = z0 t_bm.verts.index_update() FacesList.append(t_v0) else: t_v1 = t_bm.verts.new((x0, y0, z0)) t_edges = t_bm.edges.new([t_v0, t_v1]) FacesList.append(t_v1) NbVertices = 1 t_v0 = t_v1 if(self.Closed): t_v1 = t_bm.verts.new((xInit, yInit, zInit)) t_edges = t_bm.edges.new([t_v0, t_v1]) FacesList.append(t_v1) t_face = t_bm.faces.new(FacesList) t_bm.to_mesh(me)
def CreateCylinder(self, context): me = bpy.data.meshes.new('C_Cylinder') ob = bpy.data.objects.new('C_Cylinder', me) self.CurrentObj = ob scene = context.scene region = context.region rv3d = context.region_data coord = self.mouse_path[0][0], self.mouse_path[0][1] depthLocation = region_2d_to_vector_3d(region, rv3d, coord) self.ViewVector = depthLocation loc = region_2d_to_location_3d(region, rv3d, coord, depthLocation) mat = context.space_data.region_3d.view_matrix.transposed().to_3x3().to_4x4() ob.matrix_world = mat ob.location = loc bpy.context.scene.objects.link(ob) t_bm = bmesh.new() t_bm.from_mesh(me) x0 = self.mouse_path[0][0] y0 = self.mouse_path[0][1] x1 = self.mouse_path[1][0] y1 = self.mouse_path[1][1] v0 = mathutils.Vector((self.mouse_path[0][0], self.mouse_path[0][1], 0)) v1 = mathutils.Vector((self.mouse_path[1][0], self.mouse_path[1][1], 0)) v0 -= v1 radius = self.mouse_path[1][0] - self.mouse_path[0][0] DEG2RAD = 3.14159 / (180 / self.stepAngle[self.step]) if(self.ctrl): shift = (3.14159 / (360 / self.stepAngle[self.step])) * self.stepRotation else: shift = (self.mouse_path[1][1] - self.mouse_path[0][1]) / 50 # Passe en revue tous les points du cercle pour les convertir FacesList = [] for i in range(0, int(360 / self.stepAngle[self.step])): degInRad = i * DEG2RAD v0 = x0 + self.xpos + int(math.cos(degInRad + shift) * radius), y0 + self.ypos + int(math.sin(degInRad + shift) * radius) vec = region_2d_to_vector_3d(region, rv3d, v0) loc0 = region_2d_to_location_3d(region, rv3d, v0, vec) - loc loc0 = loc0 * mat t_v0 = t_bm.verts.new(loc0) FacesList.append(t_v0) t_bm.verts.index_update() t_face = t_bm.faces.new(FacesList) t_bm.to_mesh(me)
def invoke(self, context, event): self.target = bpy.context.active_object if context.space_data.type == 'VIEW_3D': self.scene = context.scene self.region = context.region self.rv3d = context.region_data self.mouse_co = event.mouse_region_x, event.mouse_region_y self.depth = view3d_utils.region_2d_to_vector_3d(self.region, self.rv3d, self.mouse_co) self.emp_co = view3d_utils.region_2d_to_location_3d(self.region, self.rv3d, self.mouse_co, self.depth) bpy.ops.object.select_all(action='DESELECT') # add custom objects bpy.ops.object.empty_add() context.object.location = self.emp_co context.window_manager.modal_handler_add(self) # need active to set origin bpy.context.scene.objects.active = self.target return {'RUNNING_MODAL'} else: self.report({'WARNING'}, "Active space must be a View3d") return {'CANCELLED'}
def pick_voxel(self, context, event, voxelarray): """Run this function on left mouse, execute the ray cast TODO: report/go through some problems with selecting in the operator_modal_view3d_raycast.py. Most of the problem is when trying to click close to the edge of the object. The distance values are often mucked up""" # get the context arguments ray_max=10000.0 region = context.region rv3d = context.region_data coord = event.mouse_region_x, event.mouse_region_y # get the ray from the viewport and mouse view_vector = view3d_utils.region_2d_to_vector_3d(region, rv3d, coord) ray_origin = view3d_utils.region_2d_to_origin_3d(region, rv3d, coord) ray_target = ray_origin + (view_vector * ray_max) #TODO: raise some kind of error, or do a check/poll on this operator #to ensure that there has been a voxel array created and selected isects = voxelarray.intersect_ray(ray_origin, ray_target) best_dist_squared = ray_max * ray_max best_isect = None if isects is None: return None for isect in isects: dist_squared = isect.dist_squared if(dist_squared < best_dist_squared): best_dist_squared = dist_squared best_isect = isect return best_isect
def pick_object(region, rv3d, x, y, near, far, objects): ''' Selects an object underneath given screen coordinates Parameters: region (bpy.types.Region): Section of the UI in which to do picking rv3d (bpy.types.RegionView3D): 3D view region data near (float): Near clipping plane far (float): Far clipping plane objects (seq<bpy.types.Object>): Sequence of pickable objects Returns: (obj, location, normal, index): Reference to picked object and raycast hit information; otherwise None obj (bpy.types.Object): Nearest object in path of the ray location (Vector): Object space location of ray-face intersection normal (Vector): Normal vector of intersected face index (int): Index of intersected face ''' blender_version = bpy.app.version # Determine ray extents. coord = Vector((x, y)) ray_dir = region_2d_to_vector_3d(region, rv3d, coord) ray_start = region_2d_to_origin_3d(region, rv3d, coord) + ray_dir * near ray_end = ray_start + ray_dir * (far - near) # Pick a mesh object underneath given screen coordinates. result = None min_dist_squared = sys.float_info.max for obj in objects: # Skip objects that cannot participate in ray casting. if (not obj.type == 'MESH' or obj.mode == 'EDIT' or not obj.data.polygons ): continue # Cast ray in object space. inverse_model_matrix = obj.matrix_world.inverted() hit = obj.ray_cast( inverse_model_matrix * ray_start, inverse_model_matrix * ray_end ) if blender_version < (2, 76, 9): location, normal, index = hit else: location, normal, index = hit[1:] # Compare intersection distances. if index != -1: dist_squared = (obj.matrix_world * location - ray_start).length_squared # Record closer of the two hits. if dist_squared < min_dist_squared: min_dist_squared = dist_squared result = (obj, location, normal, index) return result
def click_add_seed(self,context,x,y): ''' x,y = event.mouse_region_x, event.mouse_region_y this will add a point into the bezier curve or close the curve into a cyclic curve ''' region = context.region rv3d = context.region_data coord = x, y view_vector = view3d_utils.region_2d_to_vector_3d(region, rv3d, coord) ray_origin = view3d_utils.region_2d_to_origin_3d(region, rv3d, coord) ray_target = ray_origin + (view_vector * 1000) mx = self.cut_ob.matrix_world imx = mx.inverted() if bversion() < '002.077.000': loc, no, face_ind = self.cut_ob.ray_cast(imx * ray_origin, imx * ray_target) else: res, loc, no, face_ind = self.cut_ob.ray_cast(imx * ray_origin, imx * ray_target - imx * ray_origin) if face_ind == -1: self.selected = -1 return self.seed = self.bme.faces[face_ind] self.seed_loc = loc self.geo_data = [dict(), set(), set(), set()]
def raycast_screen(self, loc2d, rgn, r3d): o,d = region_2d_to_origin_3d(rgn, r3d, loc2d),region_2d_to_vector_3d(rgn, r3d, loc2d) back = 0 if r3d.is_perspective else 100 p3d,n3d,idx,dist = self.bvh_raycast(self.imx * (o-d*back), self.imx3x3 * d) p3d = self.mx * p3d if p3d else None n3d = self.nmx * n3d if n3d else None return (p3d, n3d)
def click_add_target(self, context, x, y): region = context.region rv3d = context.region_data coord = x, y view_vector = view3d_utils.region_2d_to_vector_3d(region, rv3d, coord) ray_origin = view3d_utils.region_2d_to_origin_3d(region, rv3d, coord) ray_target = ray_origin + (view_vector * 1000) mx = self.cut_ob.matrix_world imx = mx.inverted() if bversion() < '002.077.000': loc, no, face_ind = self.cut_ob.ray_cast(imx * ray_origin, imx * ray_target) else: res, loc, no, face_ind = self.cut_ob.ray_cast(imx * ray_origin, imx * ray_target - imx * ray_origin) if face_ind == -1: return self.target = self.bme.faces[face_ind] self.target_loc = loc vrts, eds, ed_cross, f_cross, error = path_between_2_points(self.bme, self.bvh, mx,mx* self.seed_loc,mx*self.target_loc, max_tests = 10000, debug = True, prev_face = None, use_limit = True) if not error: self.path = vrts else: self.path = [] return
def cursor_2d_to_location_3d(context, event): from bpy_extras.view3d_utils import region_2d_to_vector_3d, region_2d_to_location_3d coords = event.mouse_region_x, event.mouse_region_y region = context.region rv3d = context.space_data.region_3d return region_2d_to_location_3d(region, rv3d, coords, region_2d_to_vector_3d(region, rv3d, coords))
def invoke(self, context, event): if context.mode == 'OBJECT': coord = event.mouse_region_x, event.mouse_region_y region = context.region rv3d = context.space_data.region_3d vec = region_2d_to_vector_3d(region, rv3d, coord) loc = region_2d_to_location_3d(region, rv3d, coord, vec) SelectedObjects.storedSelectedObjects = [ obj for obj in context.visible_objects if obj.select ] for obj in SelectedObjects.storedSelectedObjects: obj.select = False oldPivot = bpy.data.objects.get('PivotPro', None) context.scene.tool_settings.use_snap = True if oldPivot is not None: enablePivot(context) oldPivot.location = loc else: createPivot(context) pivot = bpy.data.objects.get( 'PivotPro', None) # it exist now after CreatePivot pivot.location = loc # so put pivot under cursor return {'FINISHED'}
def set_ray_from_region(self, x, y): context = bpy.context mesh_object = self.mesh_object region = context.region region_co = Vector((x, y)) rv3d = context.region_data sv3d = context.space_data # Determine the view's clipping distances. if rv3d.view_perspective == 'CAMERA': camera_data = sv3d.camera.data clip_start = camera_data.clip_start clip_end = camera_data.clip_end else: clip_start = sv3d.clip_start clip_end = sv3d.clip_end # Determine the ray's direction in world space. ray_direction = view3d_utils.region_2d_to_vector_3d( region, rv3d, region_co ) # For orthographic projections in Blender versions prior to 2.72, the # ray's direction needs to be inverted to point into the scene. if bpy.app.version < (2, 72, 0): if rv3d.view_perspective == 'ORTHO' or ( rv3d.view_perspective == 'CAMERA' and sv3d.camera.data.type == 'ORTHO' ): ray_direction *= -1 # Determine the ray's origin in world space. ray_origin =\ view3d_utils.region_2d_to_origin_3d(region, rv3d, region_co) # Determine the ray's target in world space. ray_target = ray_origin + clip_end * ray_direction # If the view is an orthographic projection, the ray's origin may exist # behind the mesh object. Therefore, it is necessary to move the ray's # origin a sufficient distance antiparallel to the ray's direction to # ensure that the ray's origin is in front of the mesh object. if rv3d.view_perspective == 'ORTHO': ray_origin -= 10000 * ray_direction # Otherwise, if the view is a perspective projection or projected from # a camera then advance the ray's origin to the near clipping plane. else: ray_origin += clip_start * ray_direction # Convert the ray's origin and target from world space to object space, # if necessary. if self.coordinate_system == 'OBJECT': inverse_model_matrix = mesh_object.matrix_world.inverted() for co in ray_origin, ray_target: co.xyz = inverse_model_matrix * co # Set the ray caster object's ray attributes. self.ray_origin = ray_origin self.ray_target = ray_target
def click_add_target(self, context, x, y): region = context.region rv3d = context.region_data coord = x, y view_vector = view3d_utils.region_2d_to_vector_3d(region, rv3d, coord) ray_origin = view3d_utils.region_2d_to_origin_3d(region, rv3d, coord) ray_target = ray_origin + (view_vector * 1000) mx = self.cut_ob.matrix_world imx = mx.inverted() loc, no, face_ind = self.cut_ob.ray_cast(imx * ray_origin, imx * ray_target) if face_ind == -1: return self.target = self.bme.faces[face_ind] self.target_loc = loc geos, fixed, close, far = geodesic_walk(self.bme, self.seed, self.seed_loc, targets = [self.target], subset = None, max_iters = 100000, min_dist = None) path_elements, self.path = gradient_descent(self.bme, geos, self.target, self.target_loc, epsilon = .0000001) self.geo_data = [geos, fixed, close, far] return
def invoke(self, context, event): if context.area.type == 'VIEW_3D': # the arguments we pass the the callback args = (self, context) # Add the region OpenGL drawing callback self._timer = context.window_manager.event_timer_add(.1, context.window) #bpy.types.WindoManager.event_time_add? self._handle = bpy.types.SpaceView3D.draw_handler_add(bgl_utils.draw_callback_crevice_walking, args, 'WINDOW', 'POST_PIXEL') #initialze important values and gather important info region = context.region rv3d = context.space_data.region_3d coord = event.mouse_region_x, event.mouse_region_y self.tracer_name = context.object.name #adding bpy here because getting weird error self.target_name = [ob.name for ob in context.selected_editable_objects if ob.name != self.tracer_name][0] tracer = bpy.data.objects[self.tracer_name] target = bpy.data.objects[self.target_name] #target.select = False tracer.select = True context.scene.objects.active = tracer #mod = tracer.modifers['Shrinkwrap'] #going to need to do some checking for dumb scenarios #self.target = mod.target vec = view3d_utils.region_2d_to_vector_3d(region, rv3d, coord) ray_origin = view3d_utils.region_2d_to_location_3d(region, rv3d, coord, vec) #raycast onto active object ray_target = ray_origin + 10000*vec [hit, normal, face] = odcutils.obj_ray_cast(target, target.matrix_world, ray_origin, ray_target) self.max_iters = 50 self.keep_iterating = False #turn this off/on when we get convergence on the bias point or add a new bias point self.session_iterations = 0 if hit: self.current_bias_point = target.matrix_world * hit else: self.current_bias_point = target.location self.confirmed_path = [] self.pending_path = [tracer.location.copy()] self.bias_points = [] self.bias_normals = [] #gotta keep track of the normals so we can re-orient the tracker pointed the right way self.step_size = .5 self.convergence_limit = 5 context.window_manager.modal_handler_add(self) return {'RUNNING_MODAL'} else: self.report({'WARNING'}, "View3D not found, cannot run operator") return {'CANCELLED'}
def sketch_confirm(self, context, eventd): print('sketch confirmed') if len(self.sketch) < 5 and self.knife.ui_type == 'DENSE_POLY': print('sketch too short, cant confirm') return x,y = eventd['mouse'] region = context.region rv3d = context.region_data view_vector = view3d_utils.region_2d_to_vector_3d(region, rv3d, (x,y)) last_hovered = self.knife.hovered[1] #guaranteed to be a point by criteria to enter sketch mode self.knife.hover(context,x,y) print('last hovered %i' % last_hovered) sketch_3d = common_utilities.ray_cast_path(context, self.knife.cut_ob,self.sketch) if self.knife.hovered[0] == None: #drawing out into space #add the points in if last_hovered == len(self.knife.pts) - 1: if last_hovered == 0: self.knife.cyclic = False self.knife.pts += sketch_3d[0::5] self.knife.normals += [view_vector]*len(sketch_3d[0::5]) #TODO optimize...don't slice twice, you are smart enough to calc this length! print('add on to the tail') else: self.knife.pts = self.knife.pts[:last_hovered] + sketch_3d[0::5] self.knife.normals = self.knife.normals[:last_hovered] + [view_vector]*len(sketch_3d[0::5]) print('snipped off and added on to the tail') else: print('inserted new segment') print('last hovered is %i, now hovered %i' % (last_hovered, self.knife.hovered[1])) new_pts = sketch_3d[0::5] if last_hovered > self.knife.hovered[1]: if self.knife.hovered[1] == 0: self.knife.pts = self.knife.pts[:last_hovered] + new_pts self.knife.normals = self.knife.normals[:last_hovered] + [view_vector]*len(new_pts) else: new_pts.reverse() self.knife.pts = self.knife.pts[:self.knife.hovered[1]] + new_pts + self.knife.pts[last_hovered:] self.knife.normals = self.knife.normals[:self.knife.hovered[1]] + [view_vector]*len(new_pts) + self.knife.normals[last_hovered:] else: if self.knife.hovered[1] == 0: #drew back into tail self.knife.pts += sketch_3d[0::5] self.knife.normals += [view_vector]*len(sketch_3d[0::5]) self.knife.cyclic = True else: self.knife.pts = self.knife.pts[:last_hovered] + new_pts + self.knife.pts[self.knife.hovered[1]:] self.knife.normals = self.knife.normals[:last_hovered] + [view_vector]*len(new_pts) + self.knife.normals[self.knife.hovered[1]:] self.knife.snap_poly_line()
def mouseTo3d(self, context, x, y): '''Convert event.mouse_region to world coordinates''' coords = (x, y) reg = context.region reg3d = context.region_data vec = region_2d_to_vector_3d(reg, reg3d, coords) loc = region_2d_to_location_3d(reg, reg3d, coords, vec) return loc
def modal(self, context, event): region = context.region rv3d = context.region_data co2d = ((event.mouse_region_x, event.mouse_region_y)) view_vector = view3d_utils.region_2d_to_vector_3d(region, rv3d, co2d) enterloc = view3d_utils.region_2d_to_origin_3d(region, rv3d, co2d) + view_vector*100 NP020PL.enterloc = copy.deepcopy(enterloc) print('02_GetMouseloc_FINISHED', ';', 'NP020PL.flag = ', NP020PL.flag) return{'FINISHED'}
def mouseTo3d(context, x, y): '''Convert event.mouse_region to world coordinates''' if context.area.type != 'VIEW_3D': raise Exception('Wrong context') coords = (x, y) reg = context.region reg3d = context.region_data vec = region_2d_to_vector_3d(reg, reg3d, coords) loc = region_2d_to_location_3d(reg, reg3d, coords, vec) #WARNING, this function return indeterminate value when view3d clip distance is too large return loc
def modal(self,context,event): print('05_BglPlane_START',';','NP020RM.flag = ', NP020RM.flag) context.area.tag_redraw() flag = NP020RM.flag mode = NP020RM.mode plane = NP020RM.plane helper = NP020RM.helper centerloc = NP020RM.centerloc if event.type == 'MOUSEMOVE': self.co2d = ((event.mouse_region_x, event.mouse_region_y)) print('05_BglPlane_mousemove',';','NP020RM.flag = ', NP020RM.flag, 'NP020RM.mode = ', NP020RM.mode, 'NP020RM.plane = ', NP020RM.plane) elif event.type in ('LEFTMOUSE', 'RIGHTMOUSE', 'RET', 'NUMPAD_ENTER'): bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW') region = context.region rv3d = context.region_data co2d = self.co2d view_vector = view3d_utils.region_2d_to_vector_3d(region, rv3d, co2d) viewloc = view3d_utils.region_2d_to_origin_3d(region, rv3d, co2d) away = (centerloc - viewloc).length pointloc = viewloc + view_vector * away # to place the helper on centerloc to be in the vicinity of projection plane helper.location = pointloc helper.hide = False NP020RM.qdef = copy.deepcopy(NP020RM.q) NP020RM.ndef = copy.deepcopy(NP020RM.n) NP020RM.plane = 'SET' NP020RM.flag = 'RUNTRANSSTART' print('05_BglPlane_lmb_FINISHED',';','NP020RM.flag = ', NP020RM.flag, 'NP020RM.mode = ', NP020RM.mode, 'NP020RM.plane = ', NP020RM.plane) return{'FINISHED'} elif event.ctrl and event.value == 'PRESS': bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW') if mode == 'FREE': NP020RM.mode = 'X' elif mode == 'X': NP020RM.mode = 'Y' elif mode == 'Y': NP020RM.mode = 'Z' else: NP020RM.mode = 'FREE' print('05_BglPlane_rmb_FINISHED',';','NP020RM.flag = ', NP020RM.flag, 'NP020RM.mode = ', NP020RM.mode, 'NP020RM.plane = ', NP020RM.plane) elif event.type == 'ESC': bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW') NP020RM.flag = 'EXIT' print('05_BglPlane_esc_FINISHED',';','NP020RM.flag = ', NP020RM.flag) return{'FINISHED'} elif event.type in {'MIDDLEMOUSE', 'WHEELUPMOUSE', 'WHEELDOWNMOUSE'}: print('05_BglPlane_middle_wheel_any_PASS_THROUGH') return {'PASS_THROUGH'} print('05_BglPlane_standard_RUNNING_MODAL',';','NP020RM.flag = ', NP020RM.flag) return {'RUNNING_MODAL'}
def modal(self,context, event): region = context.region rv3d = context.region_data co2d = ((event.mouse_region_x, event.mouse_region_y)) view_vector = view3d_utils.region_2d_to_vector_3d(region, rv3d, co2d) pointloc = view3d_utils.region_2d_to_origin_3d(region, rv3d, co2d) + view_vector/5 print(pointloc) Storage.pointloc=pointloc #print('020') return{'FINISHED'}
def unProject(region, rv3d, mcursor): view_vector = view3d_utils.region_2d_to_vector_3d(region, rv3d, mcursor) ray_origin = view3d_utils.region_2d_to_origin_3d(region, rv3d, mcursor) ray_target = ray_origin + (view_vector * 1000) scene = bpy.context.scene # cast the ray result, object, matrix, location, normal = scene.ray_cast(ray_origin, ray_target) if location == None: location = Vector((0,0,0)) return result, object, matrix, location, normal
def hover_non_man(self,context,x,y): region = context.region rv3d = context.region_data coord = x, y self.mouse = Vector((x, y)) loc3d_reg2D = view3d_utils.location_3d_to_region_2d view_vector = view3d_utils.region_2d_to_vector_3d(region, rv3d, coord) ray_origin = view3d_utils.region_2d_to_origin_3d(region, rv3d, coord) ray_target = ray_origin + (view_vector * 1000) mx = self.cut_ob.matrix_world imx = mx.inverted() loc, no, face_ind = self.cut_ob.ray_cast(imx * ray_origin, imx * ray_target) if len(self.non_man_points): co3d, index, dist = self.kd.find(mx * loc) #get the actual non man vert from original list close_bmvert = self.bme.verts[self.non_man_bmverts[index]] #stupid mapping, unreadable, terrible, fix this, because can't keep a list of actual bmverts close_eds = [ed for ed in close_bmvert.link_edges if not ed.is_manifold] if len(close_eds) == 2: bm0 = close_eds[0].other_vert(close_bmvert) bm1 = close_eds[1].other_vert(close_bmvert) a0 = bm0.co b = close_bmvert.co a1 = bm1.co inter_0, d0 = intersect_point_line(loc, a0, b) inter_1, d1 = intersect_point_line(loc, a1, b) screen_0 = loc3d_reg2D(region, rv3d, mx * inter_0) screen_1 = loc3d_reg2D(region, rv3d, mx * inter_1) screen_v = loc3d_reg2D(region, rv3d, mx * b) screen_d0 = (self.mouse - screen_0).length screen_d1 = (self.mouse - screen_1).length screen_dv = (self.mouse - screen_v).length if 0 < d0 <= 1 and screen_d0 < 30: self.hovered = ['NON_MAN_ED', (close_eds[0], mx*inter_0)] return elif 0 < d1 <= 1 and screen_d1 < 30: self.hovered = ['NON_MAN_ED', (close_eds[1], mx*inter_1)] return elif screen_dv < 30: if abs(d0) < abs(d1): self.hovered = ['NON_MAN_VERT', (close_eds[0], mx*b)] return else: self.hovered = ['NON_MAN_VERT', (close_eds[1], mx*b)] return
def grab_mouse_move(self,context,x,y): region = context.region rv3d = context.region_data coord = x, y view_vector = view3d_utils.region_2d_to_vector_3d(region, rv3d, coord) ray_origin = view3d_utils.region_2d_to_origin_3d(region, rv3d, coord) ray_target = ray_origin + (view_vector * 1000) crv_mx = self.crv_obj.matrix_world i_crv_mx = crv_mx.inverted() hit = False if self.snap_type == 'SCENE': mx = Matrix.Identity(4) #scene ray cast returns world coords if bversion() < '002.077.000': res, obj, omx, loc, no = context.scene.ray_cast(ray_origin, ray_target) else: res, loc, no, ind, obj, omx = context.scene.ray_cast(ray_origin, view_vector) if res: hit = True else: #cast the ray into a plane a #perpendicular to the view dir, at the last bez point of the curve hit = True view_direction = rv3d.view_rotation * Vector((0,0,-1)) plane_pt = self.grab_undo_loc loc = intersect_line_plane(ray_origin, ray_target,plane_pt, view_direction) elif self.snap_type == 'OBJECT': mx = self.snap_ob.matrix_world imx = mx.inverted() if bversion() < '002.077.000': loc, no, face_ind = self.snap_ob.ray_cast(imx * ray_origin, imx * ray_target) if face_ind != -1: hit = True else: ok, loc, no, face_ind = self.snap_ob.ray_cast(imx * ray_origin, imx * ray_target - imx*ray_origin) if ok: hit = True if not hit: self.grab_cancel() else: local_loc = i_crv_mx * mx * loc self.crv_data.splines[0].bezier_points[self.selected].co = local_loc self.b_pts[self.selected] = mx * loc
def modal(self, context, event): props = context.scene.son_props if context.mode == 'OBJECT': # マウスカーソルのリージョン座標を取得 mv = Vector((event.mouse_region_x, event.mouse_region_y)) # 3Dビューエリアのウィンドウリージョンと、スペースを取得する region, space = ShowObjectName.__get_region_space( context, 'VIEW_3D', 'WINDOW', 'VIEW_3D' ) # マウスカーソルの位置に向けて発したレイの方向を求める ray_dir = view3d_utils.region_2d_to_vector_3d( region, space.region_3d, mv ) # マウスカーソルの位置に向けて発したレイの発生源を求める ray_orig = view3d_utils.region_2d_to_origin_3d( region, space.region_3d, mv ) # レイの始点 start = ray_orig # レイの終点(線分の長さは2000とした) end = ray_orig + ray_dir * 2000 # カメラやライトなど、メッシュ型ではないオブジェクトは除く objs = [o for o in bpy.data.objects if o.type == 'MESH'] self.__intersected_objs = [] for o in objs: try: # レイとオブジェクトの交差判定 mwi = o.matrix_world.inverted() result = o.ray_cast(mwi * start, mwi * end) # オブジェクトとレイが交差した場合は交差した面のインデックス、交差しない場合は-1が返ってくる if result[2] != -1: self.__intersected_objs.append(o) # メッシュタイプのオブジェクトが作られているが、ray_cast対象の面が存在しない場合 except RuntimeError: print("""サンプル5-4: オブジェクト生成タイミングの問題により、 例外エラー「レイキャスト可能なデータなし」が発生""") # 3Dビューの画面を更新 if context.area: context.area.tag_redraw() # 作業時間計測を停止 if props.running is False: self.__handle_remove(context) return {'FINISHED'} return {'PASS_THROUGH'}
def get_mouse_on_plane(context, plane_pos, mouse_coords): region = context.region rv3d = context.region_data cam_dir = rv3d.view_rotation * Vector((0.0, 0.0, -1.0)) #cam_pos = view3d_utils.region_2d_to_origin_3d(region, rv3d, (region.width/2.0, region.height/2.0)) mouse_pos = view3d_utils.region_2d_to_origin_3d(region, rv3d, mouse_coords) mouse_dir = view3d_utils.region_2d_to_vector_3d(region, rv3d, mouse_coords) new_pos = mathu.geometry.intersect_line_plane(mouse_pos, mouse_pos+(mouse_dir*10000.0), plane_pos, cam_dir, False) if new_pos: return new_pos return None
def mouse_to_scene_raycast(self, context, event): """ convert mouse pos to 3d point over plane defined by origin and normal """ region = context.region rv3d = context.region_data co2d = (event.mouse_region_x, event.mouse_region_y) view_vector_mouse = region_2d_to_vector_3d(region, rv3d, co2d) ray_origin_mouse = region_2d_to_origin_3d(region, rv3d, co2d) res, pos, normal, face_index, object, matrix_world = context.scene.ray_cast( ray_origin_mouse, view_vector_mouse) return res, pos, normal, face_index, object, matrix_world
def fromTopView(cls, context): '''Create a 2D BBOX from Blender 3dview if the view is top left ortho else return None''' scn = context.scene area = context.area if area.type != 'VIEW_3D': return None reg = context.region reg3d = context.region_data if reg3d.view_perspective != 'ORTHO' or tuple(reg3d.view_matrix.to_euler()) != (0,0,0): print("View3d must be in top ortho") return None # w, h = area.width, area.height coords = (w, h) vec = region_2d_to_vector_3d(reg, reg3d, coords) loc_ne = region_2d_to_location_3d(reg, reg3d, coords, vec) xmax, ymax = loc_ne.x, loc_ne.y # coords = (0, 0) vec = region_2d_to_vector_3d(reg, reg3d, coords) loc_sw = region_2d_to_location_3d(reg, reg3d, coords, vec) xmin, ymin = loc_sw.x, loc_sw.y # return cls(xmin=xmin, ymin=ymin, xmax=xmax, ymax=ymax)
def out_Location(rv3d, region, mcursor): view_matrix = rv3d.view_matrix.transposed() orig = view3d_utils.region_2d_to_origin_3d(region, rv3d, mcursor) vector = view3d_utils.region_2d_to_vector_3d(region, rv3d, mcursor) v1 = Vector((int(view_matrix[0][0]*1.5),int(view_matrix[1][0]*1.5),int(view_matrix[2][0]*1.5))) v2 = Vector((int(view_matrix[0][1]*1.5),int(view_matrix[1][1]*1.5),int(view_matrix[2][1]*1.5))) hit = mathutils.geometry.intersect_ray_tri(Vector((1,0,0)), Vector((0,1,0)), Vector((0,0,0)), (vector), (orig), False) if hit == None: hit = mathutils.geometry.intersect_ray_tri(v1, v2, Vector((0,0,0)), (vector), (orig), False) if hit == None: hit = mathutils.geometry.intersect_ray_tri(v1, v2, Vector((0,0,0)), (-vector), (orig), False) if hit == None: hit = Vector((0,0,0)) return hit
def mousemove_drawing(self, context, event): screen_v = Vector((event.mouse_region_x, event.mouse_region_y)) self.mouse_path.append((event.mouse_region_x, event.mouse_region_y)) #this will just add in apoint every 10 recorded mouse positions #later you will want to do something smarter :-) if len(self.mouse_path) > self.draw_points_max or (screen_v - Vector(self.mouse_path[0])).length >= self.extrusion_radius: region = context.region rv3d = context.region_data #this is the view_vector @ the mous coord #which is not the same as the view_direction!!! view_vector = view3d_utils.region_2d_to_vector_3d(region, rv3d, self.mouse_path[-1]) ray_origin = view3d_utils.region_2d_to_origin_3d(region, rv3d, self.mouse_path[-1]) ray_target = ray_origin + (view_vector * 10000) #cast the ray into a plane a #perpendicular to the view dir, at the last bez point of the curve view_direction = rv3d.view_rotation * Vector((0,0,-1)) if self.active_spline.type == 'BEZIER': plane_pt = self.curve_object.matrix_world * self.active_spline.bezier_points[-1].co elif self.active_spline.type == 'NURBS': plane_pt = self.curve_object.matrix_world * self.active_spline.points[-1].co new_coord = intersect_line_plane(ray_origin, ray_target,plane_pt, view_direction) if new_coord: if self.active_spline.type == 'BEZIER': self.active_spline.bezier_points.add(1) self.active_spline.bezier_points[-1].co = self.curve_object.matrix_world.inverted() * new_coord self.active_spline.bezier_points[-1].handle_right.xyz = self.active_spline.bezier_points[-1].co self.active_spline.bezier_points[-1].handle_left.xyz = self.active_spline.bezier_points[-1].co self.active_spline.bezier_points[-1].handle_left_type = 'AUTO' self.active_spline.bezier_points[-1].handle_right_type = 'AUTO' elif self.active_spline.type == 'NURBS': self.active_spline.points.add(1) loc = self.curve_object.matrix_world.inverted() * new_coord #NURBS pahts have 4 dim points self.active_spline.points[-1].co = Vector((loc[0], loc[1], loc[2], 1)) #udpate everything #udpate modifiers and objects etc. self.curve_object.update_tag() context.scene.update() self.mouse_path = []
def get_mouse_on_plane(context, plane_pos, plane_dir, mouse_coords): region = context.region rv3d = context.region_data final_dir = plane_dir if plane_dir is None: final_dir = rv3d.view_rotation * Vector((0.0, 0.0, -1.0)) mouse_pos = view3d_utils.region_2d_to_origin_3d(region, rv3d, mouse_coords) mouse_dir = view3d_utils.region_2d_to_vector_3d(region, rv3d, mouse_coords) new_pos = mathu.geometry.intersect_line_plane( mouse_pos, mouse_pos + (mouse_dir * 10000.0), plane_pos, final_dir, False) if new_pos: return new_pos return None
def mouse_to_plane(self, context, event, origin=Vector((0, 0, 0)), normal=Vector((0, 0, 1))): """ convert mouse pos to 3d point over plane defined by origin and normal """ region = context.region rv3d = context.region_data co2d = (event.mouse_region_x, event.mouse_region_y) view_vector_mouse = region_2d_to_vector_3d(region, rv3d, co2d) ray_origin_mouse = region_2d_to_origin_3d(region, rv3d, co2d) pt = intersect_line_plane(ray_origin_mouse, ray_origin_mouse + view_vector_mouse, origin, normal, False) # fix issue with parallel plane if pt is None: pt = intersect_line_plane(ray_origin_mouse, ray_origin_mouse + view_vector_mouse, origin, view_vector_mouse, False) return pt
def PickObject(context, event, self, ray_max=10000.0): scene = context.scene region = context.region rv3d = context.region_data coord = event.mouse_region_x, event.mouse_region_y view_vector = view3d_utils.region_2d_to_vector_3d(region, rv3d, coord) ray_origin = view3d_utils.region_2d_to_origin_3d(region, rv3d, coord) ray_target = ray_origin + (view_vector * ray_max) def obj_ray_cast(obj, matrix): matrix_inv = matrix.inverted() ray_origin_obj = matrix_inv * ray_origin ray_target_obj = matrix_inv * ray_target hit, normal, face_index = obj.ray_cast(ray_origin_obj, ray_target_obj) if face_index != -1: return hit, normal, face_index else: return None, None, None for obj in bpy.context.visible_objects: if (obj.type == "MESH"): fcanv = False for oc in self.CList: if (oc == obj): fcanv = True for oc in self.OPList: if (oc == obj): fcanv = True if (fcanv == False): matrix = obj.matrix_world hit, normal, face_index = obj_ray_cast(obj, matrix) if hit is not None: hit_world = matrix * hit return obj return None
def grab_mouse_move(self, context, x, y): region = context.region rv3d = context.region_data coord = x, y view_vector = view3d_utils.region_2d_to_vector_3d(region, rv3d, coord) ray_origin = view3d_utils.region_2d_to_origin_3d(region, rv3d, coord) ray_target = ray_origin + (view_vector * 1000) mx = self.cut_ob.matrix_world imx = mx.inverted() if bversion() < '002.077.000': loc, no, face_ind = self.cut_ob.ray_cast(imx * ray_origin, imx * ray_target) else: ok, loc, no, face_ind = self.cut_ob.ray_cast( imx * ray_origin, imx * ray_target - imx * ray_origin) if face_ind == -1: self.grab_cancel() else: self.pts[self.selected] = mx * loc self.cut_pts[self.selected] = loc self.normals[self.selected] = no self.face_map[self.selected] = face_ind
def RayCast(self, context, event, ray_max=1000.0): """Run this function on left mouse, execute the ray cast""" # get the context arguments scene = context.scene region = context.region rv3d = context.region_data coord = event.mouse_region_x, event.mouse_region_y # get the ray from the viewport and mouse view_vector = view3d_utils.region_2d_to_vector_3d(region, rv3d, coord).normalized() ray_origin = view3d_utils.region_2d_to_origin_3d(region, rv3d, coord) ray_target = ray_origin + view_vector def visible_objects_and_duplis(): """Loop over (object, matrix) pairs (mesh only)""" for obj in context.visible_objects: if obj.type == 'MESH': yield (obj, obj.matrix_world.copy()) if obj.dupli_type != 'NONE': obj.dupli_list_create(scene) for dob in obj.dupli_list: obj_dupli = dob.object if obj_dupli.type == 'MESH': yield (obj_dupli, dob.matrix.copy()) obj.dupli_list_clear() def obj_ray_cast(obj, matrix): """Wrapper for ray casting that moves the ray into object space""" # get the ray relative to the object matrix_inv = matrix.inverted() ray_origin_obj = matrix_inv * ray_origin ray_target_obj = matrix_inv * ray_target ray_direction_obj = ray_target_obj - ray_origin_obj d = ray_direction_obj.length ray_direction_obj.normalize() # cast the ray if context.mode == 'OBJECT': #print("object") if obj.modifiers == 0: bvh = BVHTree.FromObject(obj, context.scene) location, normal, face_index, d = bvh.ray_cast( ray_origin_obj, ray_direction_obj) else: scene = bpy.context.scene #obj = obj.to_mesh(scene, apply_modifiers=True, settings='PREVIEW') bvh = BVHTree.FromObject(obj, context.scene) location, normal, face_index, d = bvh.ray_cast( ray_origin_obj, ray_direction_obj) #success, location, normal, face_index = obj.ray_cast(ray_origin_obj, ray_direction_obj) #bpy.data.meshes.remove(obj) elif context.mode == 'EDIT_MESH': obj.update_from_editmode() bvh = BVHTree.FromBMesh(bmesh.from_edit_mesh(obj.data)) location, normal, face_index, d = bvh.ray_cast( ray_origin_obj, ray_direction_obj) if face_index != -1: return location, normal, face_index else: return None, None, None # cast rays and find the closest object best_length_squared = -1.0 best_obj = None best_matrix = None best_face = None best_hit = None for obj, matrix in visible_objects_and_duplis(): if obj.type == 'MESH': hit, normal, face_index = obj_ray_cast(obj, matrix) if hit is not None: hit_world = matrix * hit scene.cursor_location = hit_world length_squared = (hit_world - ray_origin).length_squared if best_obj is None or length_squared < best_length_squared: best_length_squared = length_squared best_obj = obj best_matrix = matrix best_face = face_index best_hit = hit break def run(best_obj, best_matrix, best_face, best_hit): best_distance = float( "inf" ) # use float("inf") (infinity) to have unlimited search range if context.mode == 'EDIT_MESH': mesh = best_obj.data else: mesh = best_obj #best_matrix = best_obj.matrix_world for vert_index in mesh.polygons[best_face].vertices: vert_coord = mesh.vertices[vert_index].co distance = (vert_coord - best_hit).magnitude if distance < best_distance: best_distance = distance CreateOrientation(self, context, event, vert_coord) scene.cursor_location = best_matrix * vert_coord for v0, v1 in mesh.polygons[best_face].edge_keys: p0 = mesh.vertices[v0].co p1 = mesh.vertices[v1].co p = (p0 + p1) / 2 distance = (p - best_hit).magnitude if distance < best_distance: best_distance = distance vec = p0 - p1 CreateOrientation(self, context, event, vec) scene.cursor_location = best_matrix * p face_pos = Vector(mesh.polygons[best_face].center) distance = (face_pos - best_hit).magnitude if distance < best_distance: best_distance = distance vec = mesh.polygons[best_face].normal.copy() CreateOrientation(self, context, event, vec) scene.cursor_location = best_matrix * face_pos if best_obj is not None: if best_obj.modifiers == 0: run(best_obj.data, best_matrix, best_face, best_hit) else: if context.mode == 'OBJECT': scene = bpy.context.scene obj_data = best_obj.to_mesh(scene, apply_modifiers=True, settings='PREVIEW') run(obj_data, best_matrix, best_face, best_hit) bpy.data.meshes.remove(obj_data) elif context.mode == 'EDIT_MESH': run(best_obj, best_matrix, best_face, best_hit)
def main(context, event): """Run this function on left mouse, execute the ray cast""" # get the context arguments scene = context.scene region = context.region rv3d = context.region_data coord = event.mouse_region_x, event.mouse_region_y # get the ray from the viewport and mouse view_vector = view3d_utils.region_2d_to_vector_3d(region, rv3d, coord) ray_origin = view3d_utils.region_2d_to_origin_3d(region, rv3d, coord) ray_target = ray_origin + view_vector def visible_objects_and_duplis(): """Loop over (object, matrix) pairs (mesh only)""" for obj in context.visible_objects: if obj.type == 'MESH': yield (obj, obj.matrix_world.copy()) if obj.dupli_type != 'NONE': obj.dupli_list_create(scene) for dob in obj.dupli_list: obj_dupli = dob.object if obj_dupli.type == 'MESH': yield (obj_dupli, dob.matrix.copy()) obj.dupli_list_clear() def obj_ray_cast(obj, matrix): """Wrapper for ray casting that moves the ray into object space""" # get the ray relative to the object matrix_inv = matrix.inverted() ray_origin_obj = matrix_inv @ ray_origin ray_target_obj = matrix_inv @ ray_target ray_direction_obj = ray_target_obj - ray_origin_obj # cast the ray success, location, normal, face_index = obj.ray_cast( ray_origin_obj, ray_direction_obj) if success: return location, normal, face_index else: return None, None, None # cast rays and find the closest object best_length_squared = -1.0 best_obj = None for obj, matrix in visible_objects_and_duplis(): if obj.type == 'MESH': hit, normal, face_index = obj_ray_cast(obj, matrix) if hit is not None: hit_world = matrix @ hit scene.cursor_location = hit_world length_squared = (hit_world - ray_origin).length_squared if best_obj is None or length_squared < best_length_squared: best_length_squared = length_squared best_obj = obj # now we have the object under the mouse cursor, # we could do lots of stuff but for the example just select. if best_obj is not None: best_obj.select_set(True) context.view_layer.objects.active = best_obj
def pick_and_clone(self, context, event, ray_max=10000.0): """Run this function on left mouse, execute the ray cast""" # get the context arguments scene = context.scene region = context.region rv3d = context.region_data coord = event.mouse_region_x, event.mouse_region_y def obj_ray_cast(obj, matrix, view_vector, ray_origin): """Wrapper for ray casting that moves the ray into object space""" ray_target = ray_origin + (view_vector * ray_max) # get the ray relative to the object matrix_inv = matrix.inverted() ray_origin_obj = matrix_inv * ray_origin ray_target_obj = matrix_inv * ray_target ray_vector_obj = ray_target_obj - ray_origin_obj # cast the ray hit_result, hit, normal, face_index = obj.ray_cast( ray_origin_obj, ray_vector_obj, ray_max) if hit_result: hit_world = matrix * hit length_squared = (hit_world - ray_origin).length_squared if face_index != -1: #normal_world = (matrix.to_quaternion() * normal).normalized() normal_world = (matrix.to_quaternion() * normal).to_4d() normal_world.w = 0 normal_world = ( matrix.to_quaternion() * (matrix_inv * normal_world).to_3d()).normalized() return normal_world, hit_world, length_squared return None, None, None # cast rays and find the closest object best_length_squared = ray_max * ray_max best_obj, best_obj_nor, best_obj_pos = None, None, None best_obj_rand, best_obj_nor_rand, best_obj_pos_rand = None, None, None best_obj_hit = None pct = context.scene.paintClonesTools thePressure = max(1.0 - pct.drawPressure, self.tabletPressure) # The pressure of a pen! for obj, matrix in self.dupliList: # get the ray from the viewport and mouse view_vector_mouse = view3d_utils.region_2d_to_vector_3d( region, rv3d, coord) ray_origin_mouse = view3d_utils.region_2d_to_origin_3d( region, rv3d, coord) # Do RayCast! t1,t2,t3,t4 - temp values t1, t2, t3 = obj_ray_cast(obj, matrix, view_vector_mouse, ray_origin_mouse) if t1 is not None and t3 < best_length_squared: best_obj = obj best_obj_nor, best_obj_pos, best_length_squared = t1, t2, t3 best_obj_hit = t2 # Check for stroke length if best_obj is not None: previousHit = None if len(self.currentStrokeList) > 0: previousHit = self.currentStrokeList[-1] # get Last Element # Check for stroke strokeLength = pct.drawStrokeLength if pct.drawPressureScale is True and pct.drawPressureRelativeStroke is True and self.tabletPressure < 1.0 and pct.drawPressure > 0.0: strokeLength *= thePressure if previousHit is not None and ( best_obj_pos - previousHit.hitPoint).length < strokeLength: best_obj = None # Don't do cloning # random scatter things if pct.drawRandomStrokeScatter > 0.0 and best_obj is not None and t1 is not None: # Random Vec randX = random.uniform( -1.0, 1.0) * pct.drawRandomStrokeScatter # 3.0 is random addition randY = random.uniform( -1.0, 1.0) * pct.drawRandomStrokeScatter # 3.0 is random addition randZ = random.uniform(-1.0, 1.0) * pct.drawRandomStrokeScatter if pct.drawPressureScatter is True and self.tabletPressure < 1.0 and pct.drawPressure > 0.0: randX *= thePressure randY *= thePressure randZ *= thePressure randVect = Vector((randX, randY, randZ)) for obj, matrix in self.dupliList: ray_origin_rand = best_obj_pos + randVect ray_origin_rand_2d = view3d_utils.location_3d_to_region_2d( region, rv3d, ray_origin_rand) view_vector_rand = view3d_utils.region_2d_to_vector_3d( region, rv3d, (ray_origin_rand_2d.x, ray_origin_rand_2d.y)) t1, t2, t3 = obj_ray_cast(obj, matrix, view_vector_rand, ray_origin_rand) # 3.0 is random addition if t1 is not None and ( t2 - best_obj_hit).length <= pct.drawRandomStrokeScatter * 3.0: best_obj_nor, best_obj_pos = t1, t2 # now we have the object under the mouse cursor, if best_obj is not None: objToClone = bpy.data.objects.get(random.choice(paintClonesSourceList)) best_obj_nor = best_obj_nor.normalized() # Rotation To LookAt previousHit = None if len(self.currentStrokeList) > 0: previousHit = self.currentStrokeList[-1] # get Last Element else: if len(self.allStrokesList) > 0: previousHit = self.allStrokesList[-1][ -1] # get Last Element of previous Stroke if previousHit is not None: stroke_axis = (best_obj_hit - previousHit.hitPoint).normalized() else: stroke_axis = xAxis # clone object newDup = bpy.data.objects.new(objToClone.name, objToClone.data) # copy draw type newDup.draw_type = objToClone.draw_type newDup.show_wire = objToClone.show_wire # copy transformation newDup.matrix_world = objToClone.matrix_world newDup.location = best_obj_pos newDup.scale = objToClone.scale newDup.rotation_euler = objToClone.rotation_euler # bpy.ops.object.rotation_clear() context.scene.objects.link(newDup) newDup.select = True context.scene.objects.active = newDup # Random Rotation (PreRotate) randRotX, randRotY, randRotZ = random.uniform( -1.0, 1.0), random.uniform(-1.0, 1.0), random.uniform(-1.0, 1.0) bpy.ops.transform.rotate(value=randRotX * pct.randRotationX, axis=get_obj_axis(newDup, 'X'), proportional='DISABLED') bpy.ops.transform.rotate(value=randRotY * pct.randRotationY, axis=get_obj_axis(newDup, 'Y'), proportional='DISABLED') bpy.ops.transform.rotate(value=randRotZ * pct.randRotationZ, axis=get_obj_axis(newDup, 'Z'), proportional='DISABLED') # Rotation To Stroke direction if pct.align_mode == 'STROKE': track_vec = stroke_axis up_vec = best_obj_nor # Rotation To Normal if pct.align_mode == 'NORMAL': track_vec = best_obj_nor up_vec = stroke_axis # Rotation To X_AXIS if pct.align_mode == 'X_AXIS': track_vec = xAxis up_vec = stroke_axis # Rotation To Y_AXIS if pct.align_mode == 'Y_AXIS': track_vec = yAxis up_vec = stroke_axis # Rotation To Z_AXIS if pct.align_mode == 'Z_AXIS': track_vec = zAxis up_vec = stroke_axis q = get_rot_quat(newDup, track_vec, up_vec, pct.track_axis, pct.up_axis) bpy.ops.transform.rotate(value=q.angle, axis=(q.axis), proportional='DISABLED') # Scale if pct.drawPressure > 0.0 or pct.randScale > 0.0: newSize = newDup.scale if self.tabletPressure < 1.0 and pct.drawPressure > 0.0 and pct.drawPressureScale is True: newSize *= thePressure if pct.randScale > 0.0: randScale = 1.0 - \ (random.uniform(0.0, 1.0) * pct.randScale / 100.0) newSize *= randScale # do optimization for a stroke or not if pct.drawClonesOptimize is False: copy_settings_clones(pct, newDup, objToClone) newDup.select = False # Clear Selection # Add this point with all its stuff self.currentStrokeList.append( StrokePoint(newDup, objToClone, best_obj_hit, best_obj_nor))
def get_selection_point(context, event, ray_max=10000.0,objects=None,floor=None,exclude_objects=[]): """Gets the point to place an object based on selection""" # get the context arguments scene = context.scene region = context.region rv3d = context.region_data coord = event.mouse_region_x, event.mouse_region_y # get the ray from the viewport and mouse view_vector = view3d_utils.region_2d_to_vector_3d(region, rv3d, coord) ray_origin = view3d_utils.region_2d_to_origin_3d(region, rv3d, coord) ray_target = ray_origin + view_vector def visible_objects_and_duplis(): """Loop over (object, matrix) pairs (mesh only)""" for obj in context.visible_objects: if objects: if obj in objects and obj not in exclude_objects: yield (obj, obj.matrix_world.copy()) else: if obj not in exclude_objects: if floor is not None and obj == floor: yield (obj, obj.matrix_world.copy()) # if obj.draw_type != 'WIRE': if obj.type == 'MESH' and obj.hide_select == False: yield (obj, obj.matrix_world.copy()) if obj.instance_type != 'NONE': obj.dupli_list_create(scene) for dob in obj.dupli_list: obj_dupli = dob.object if obj_dupli.type == 'MESH': yield (obj_dupli, dob.matrix.copy()) # obj.dupli_list_clear() def obj_ray_cast(obj, matrix): """Wrapper for ray casting that moves the ray into object space""" try: # get the ray relative to the object matrix_inv = matrix.inverted() ray_origin_obj = matrix_inv @ ray_origin ray_target_obj = matrix_inv @ ray_target ray_direction_obj = ray_target_obj - ray_origin_obj # cast the ray success, location, normal, face_index = obj.ray_cast(ray_origin_obj, ray_direction_obj) if success: return location, normal, face_index else: return None, None, None except: print("ERROR IN obj_ray_cast",obj) return None, None, None best_length_squared = ray_max * ray_max best_obj = None best_hit = (0,0,0) for obj, matrix in visible_objects_and_duplis(): if obj.type == 'MESH': if obj.data: hit, normal, face_index = obj_ray_cast(obj, matrix) if hit is not None: hit_world = matrix @ hit length_squared = (hit_world - ray_origin).length_squared if length_squared < best_length_squared: best_hit = hit_world best_length_squared = length_squared best_obj = obj return best_hit, best_obj
def modal(self, context, event): if not self._handers_installed: self.add_viewport_handlers(context) context.window.cursor_modal_set('CROSSHAIR') context.area.tag_redraw() if context.mode != 'EDIT_MESH' or not self.active: self.cleanup(context, event) if self.touched: return {'FINISHED'} else: return {'CANCELLED'} update_mesh = False if self.obj != context.active_object: self.obj = context.active_object update_mesh = True if self.mesh_changed: update_mesh = True self.select_changed = True self.mesh_changed = False if update_mesh: self.mesh = bmesh.from_edit_mesh(self.obj.data) self.select_changed = True obj = self.obj mesh = self.mesh scene = context.scene vps = obj.data.vertex_paint_settings radius = float(vps.radius) region = context.region region_3d = context.space_data.region_3d mouse_pos = mouse_x, mouse_y = (event.mouse_region_x, event.mouse_region_y) to_obj = obj.matrix_world.inverted() direction = to_obj * view3d_utils.region_2d_to_vector_3d(region, region_3d, mouse_pos).normalized() mouse = mu.Vector(mouse_pos) self.mouse_pos = mouse_pos if event.type in ('RIGHTMOUSE', 'A') and event.value == 'RELEASE': self.mesh_changed = True if not self.painting and ( mouse_x < 0 or mouse_x > region.width or mouse_y < 0 or mouse_y > region.height): return {'PASS_THROUGH'} if event.type == 'LEFTMOUSE': if event.value == 'PRESS': self.last_daub_position = mu.Vector((-1000, -1000)) self.last_daub_time = 0 self.painting = True elif event.value == 'RELEASE': ops.ed.undo_push() bmesh.update_edit_mesh(obj.data) scene.update() self.painting = False if self.painting: self.touched = True update_mask = False apply_daub = False if event.type in ('LEFTMOUSE', 'MOUSEMOVE'): if vps.stroke_method == 'SHIFT': delta = (mouse - self.last_daub_position).magnitude if delta < vps.stroke_spacing: return {'RUNNING_MODAL'} else: self.last_daub_position = mouse apply_daub = True update_mask = True else: update_mask = True if vps.stroke_method == 'TIME' and event.type == 'TIMER': now = time.time() delta = now - self.last_daub_time if delta < vps.stroke_timestep: return {'RUNNING_MODAL'} else: self.last_daub_time = now apply_daub = True if update_mask: if self.select_changed: mesh.verts.index_update() mesh.edges.index_update() mesh.faces.index_update() self.vert_select = [v.select for v in mesh.verts] self.edge_select = [e.select for e in mesh.edges] self.face_select = [f.select for f in mesh.faces] self.select_none = ( (True not in self.vert_select) and (True not in self.edge_select) and (True not in self.face_select)) self.select_all = ( (False not in self.vert_select) and (False not in self.edge_select) and (False not in self.face_select)) self.select_some = not (self.select_none or self.select_all) if self.select_some: self.mask_select_verts = set(v.index for v in mesh.verts if v.select) self.mask_select_faces = set(f.index for f in mesh.faces if f.select) self.select_changed = False occlude = (vps.mask == 'OCCLUDE') mesh_select_mode = tuple(context.tool_settings.mesh_select_mode) use_occlude_geometry = context.space_data.use_occlude_geometry context.tool_settings.mesh_select_mode = (True, True, True) context.space_data.use_occlude_geometry = occlude ops.mesh.select_all(action='DESELECT') ops.view3d.select_circle(x=mouse_x, y=mouse_y, radius=radius, gesture_mode=3) self.mask_daub_verts = set([vert.index for vert in mesh.verts if vert.select]) self.mask_daub_faces = set([face.index for face in mesh.faces if face.select]) context.tool_settings.mesh_select_mode = mesh_select_mode context.space_data.use_occlude_geometry = use_occlude_geometry ops.mesh.select_all(action='DESELECT') if self.select_all: ops.mesh.select_all(action='SELECT') scene.update() elif not self.select_none: if mesh_select_mode[0]: [v.select_set(s) for (v, s) in zip(mesh.verts, self.vert_select) if s] if mesh_select_mode[1]: [e.select_set(s) for (e, s) in zip(mesh.edges, self.edge_select) if s] if mesh_select_mode[2]: [f.select_set(s) for (f, s) in zip(mesh.faces, self.face_select) if s] bias = vps.bias scale = vps.scale weights = self.weights = {} if self.select_some: verts = self.mask_select_verts.intersection(self.mask_daub_verts) else: verts = self.mask_daub_verts falloff = lambda x: x if vps.falloff == 'SMOOTH': falloff = cc.utils.smooth elif vps.falloff == 'SHARP': falloff = math.sqrt elif vps.falloff == 'ROOT': falloff = lambda x: x * x for vid in verts: vert = mesh.verts[vid] p = obj.matrix_world * vert.co p = view3d_utils.location_3d_to_region_2d(region, region_3d, p) dist = min(1, max(0, (p - mouse).magnitude / radius)) dist = max(min((dist - bias) / scale, 1), 0) dist = falloff(dist) weights[vid] = 1 - dist if apply_daub: weights = self.weights layer = mesh.loops.layers.color.active if self.select_some: faces = self.mask_select_faces.intersection(self.mask_daub_faces) else: faces = self.mask_daub_faces b = vps.color white = mu.Color((1, 1, 1)) black = mu.Color((0, 0, 0)) strength = vps.strength nbias = vps.normal_bias nscale = vps.normal_scale if vps.mask == 'NORMAL': if vps.normal == 'VERTEX': normal_mask = lambda f, v: max(min(( (direction.dot(v.normal) * 0.5 + 0.5) - nbias) / nscale, 1), 0) else: normal_mask = lambda f, v: max(min(( (direction.dot(f.normal) * 0.5 + 0.5) - nbias) / nscale, 1), 0) else: normal_mask = lambda f, v: 1 if vps.blend == 'MIX': for fid in faces: face = mesh.faces[fid] for loop in face.loops: weight = weights[loop.vert.index] mask = normal_mask(face, loop.vert) a = loop[layer] a.r = lerp(a.r, b.r, weight * strength * mask) a.g = lerp(a.g, b.g, weight * strength * mask) a.b = lerp(a.b, b.b, weight * strength * mask) if vps.blend == 'MULTIPLY': for fid in faces: face = mesh.faces[fid] for loop in face.loops: weight = weights[loop.vert.index] mask = normal_mask(face, loop.vert) a = loop[layer] a.r *= lerp(1, b.r, weight * strength * mask) a.g *= lerp(1, b.g, weight * strength * mask) a.b *= lerp(1, b.b, weight * strength * mask) if vps.blend == 'ADD': for fid in faces: face = mesh.faces[fid] for loop in face.loops: weight = weights[loop.vert.index] mask = normal_mask(face, loop.vert) a = loop[layer] a.r += lerp(0, b.r, weight * strength * mask) a.g += lerp(0, b.g, weight * strength * mask) a.b += lerp(0, b.b, weight * strength * mask) if vps.blend == 'SUBTRACT': for fid in faces: face = mesh.faces[fid] for loop in face.loops: weight = weights[loop.vert.index] mask = normal_mask(face, loop.vert) a = loop[layer] a.r -= lerp(0, b.r, weight * strength * mask) a.g -= lerp(0, b.g, weight * strength * mask) a.b -= lerp(0, b.b, weight * strength * mask) bmesh.update_edit_mesh(obj.data) return {'RUNNING_MODAL'} else: if event.type == 'D' and event.value == 'PRESS': vps.color = sample_color(context, event) return {'RUNNING_MODAL'} if event.type in ('I', 'S') and event.value == 'PRESS': a = mu.Color((vps.color.r, vps.color.g, vps.color.b)) b = mu.Color((vps.swap.r, vps.swap.g, vps.swap.b)) vps.color = b vps.swap = a return {'RUNNING_MODAL'} if event.type == 'WHEELUPMOUSE': if event.ctrl: vps.radius = max(1, math.floor(vps.radius * 0.9)) return {'RUNNING_MODAL'} elif event.shift: if event.alt: vps.strength = max(0.01, cc.utils.roundq(vps.strength - 0.01, 0.01)) else: vps.strength = max(0.1, cc.utils.roundq(vps.strength - 0.1, 0.1)) return {'RUNNING_MODAL'} if event.type == 'WHEELDOWNMOUSE': if event.ctrl: vps.radius = max(1, math.ceil(vps.radius * 1.1)) return {'RUNNING_MODAL'} elif event.shift: if event.alt: vps.strength = min(1, cc.utils.roundq(vps.strength + 0.01, 0.01)) else: vps.strength = min(1, cc.utils.roundq(vps.strength + 0.1, 0.1)) return {'RUNNING_MODAL'} if event.type == 'ESC': self.cleanup(context, event) return {'CANCELLED'} if event.type == 'SPACE': self.cleanup(context, event) return {'FINISHED'} return {'PASS_THROUGH'}
def click_add_point(self, context, x, y): ''' x,y = event.mouse_region_x, event.mouse_region_y this will add a point into the bezier curve or close the curve into a cyclic curve ''' region = context.region rv3d = context.region_data coord = x, y view_vector = view3d_utils.region_2d_to_vector_3d(region, rv3d, coord) ray_origin = view3d_utils.region_2d_to_origin_3d(region, rv3d, coord) ray_target = ray_origin + (view_vector * 1000) crv_mx = self.crv_obj.matrix_world i_crv_mx = crv_mx.inverted() hit = False if self.snap_type == 'SCENE': mx = Matrix.Identity( 4 ) #loc is given in world loc...no need to multiply by obj matrix if bversion() < '002.077.000': res, obj, omx, loc, no = context.scene.ray_cast( ray_origin, ray_target) #changed in 2.77 else: res, loc, no, ind, obj, omx = context.scene.ray_cast( ray_origin, view_vector) hit = res if not hit: #cast the ray into a plane a #perpendicular to the view dir, at the last bez point of the curve view_direction = rv3d.view_rotation * Vector((0, 0, -1)) if len(self.b_pts): if self.hovered[0] == 'EDGE': plane_pt = self.b_pts[self.hovered[1]] else: plane_pt = self.b_pts[-1] else: plane_pt = context.scene.cursor_location loc = intersect_line_plane(ray_origin, ray_target, plane_pt, view_direction) hit = True elif self.snap_type == 'OBJECT': mx = self.snap_ob.matrix_world imx = mx.inverted() if bversion() < '002.077.000': loc, no, face_ind = self.snap_ob.ray_cast( imx * ray_origin, imx * ray_target) if face_ind != -1: hit = True else: ok, loc, no, face_ind = self.snap_ob.ray_cast( imx * ray_origin, imx * ray_target - imx * ray_origin) if ok: hit = True if face_ind != -1: hit = True if not hit: self.selected = -1 return if self.hovered[0] == None: #adding in a new point if self.started: self.crv_data.splines[0].bezier_points.add(count=1) bp = self.crv_data.splines[0].bezier_points[-1] bp.handle_right_type = 'AUTO' bp.handle_left_type = 'AUTO' bp.co = i_crv_mx * mx * loc self.b_pts.append(mx * loc) else: self.started = True delta = i_crv_mx * mx * loc - self.crv_data.splines[ 0].bezier_points[-1].co bp = self.crv_data.splines[0].bezier_points[0] bp.co += delta bp.handle_left += delta bp.handle_right += delta self.b_pts.append(mx * loc) if self.hovered[0] == 'POINT': self.selected = self.hovered[1] if self.hovered[1] == 0: #clicked on first bpt, close loop self.crv_data.splines[0].use_cyclic_u = self.crv_data.splines[ 0].use_cyclic_u == False return elif self.hovered[0] == 'EDGE': #cut in a new point self.b_pts.insert(self.hovered[1] + 1, mx * loc) self.update_blender_curve_data() return
def modal(self, context, event): self.mouse_x = event.mouse_region_x self.mouse_y = event.mouse_region_y self.region = context.region if context.space_data is not None: self.region_3d = context.space_data.region_3d self.updateSelectionModes(context) # self.flag_redraw -> one more redraw needed at the end of modals # (which block this very one) if self.flag and not self.flag_redraw: self.region.tag_redraw() self.flag_redraw = True elif self.flag_redraw: self.flag_redraw = False self.flag = False if (self.region_3d is not None and context.mode == 'EDIT_MESH' and self.running): coords = (self.mouse_x, self.mouse_y) self.ray_origin = region_2d_to_origin_3d( self.region, self.region_3d, coords) self.ray_direction = region_2d_to_vector_3d( self.region, self.region_3d, coords)*RAY_MAX obj = context.object matrix_world = obj.matrix_world if self.bmesh is None or not self.bmesh.is_valid: self.bmesh = from_edit_mesh(obj.data) df = self.draw_face de = self.draw_edges dv = self.draw_verts found = False if self.select_verts: min_dist_sqr = None point_vert = None for vert in self.bmesh.verts: if not vert.hide: point = matrix_world*vert.co point_2d = self.location_3d_to_region_2d(point) dist_sqr = self.testVert2D(point_2d) if dist_sqr is not None: if (min_dist_sqr is None or min_dist_sqr > dist_sqr): min_dist_sqr = dist_sqr point_vert = point if point_vert is None or min_dist_sqr > DIST_MAX_SQR: #???? self.draw_face = None self.draw_edges = [] self.draw_verts = [] else: found = True self.draw_face = None self.draw_edges = [] self.draw_verts = [point_vert] if self.select_edges and not found: min_dist_sqr = None points_edge = None for edge in self.bmesh.edges: if not edge.hide: points = [matrix_world*v.co for v in edge.verts] points_2d = [self.location_3d_to_region_2d(point) for point in points] dist_sqr = self.testEdge2D(points_2d) if dist_sqr is not None: if (min_dist_sqr is None or min_dist_sqr > dist_sqr): min_dist_sqr = dist_sqr points_edge = points if points_edge is None or min_dist_sqr > DIST_MAX_SQR: #???? self.draw_face = None self.draw_edges = [] self.draw_verts = [] else: found = True self.draw_face = None self.draw_edges = [points_edge] self.draw_verts = points_edge if self.select_faces and not found: min_dist = None points_face = None for face in self.bmesh.faces: if not face.hide: points = [matrix_world*v.co for v in face.verts] points_2d = [self.location_3d_to_region_2d(point) for point in points] tess_face = tessellate_polygon((points,)) possible_tris = [] for tri in tess_face: if self.testTri2D([points_2d[i] for i in tri]): possible_tris.append(tri) for tri in possible_tris: dist = self.testTri3D([points[i] for i in tri]) if dist is not None: if min_dist is None or min_dist > dist: min_dist = dist points_face = points break if points_face is None: self.draw_face = None self.draw_edges = [] # XXX TEMP!!! self.draw_verts = [] # XXX else: self.draw_face = points_face self.draw_edges = [(points_face[i - 1], v) for i, v in enumerate(points_face)] self.draw_verts = points_face if (df != self.draw_face or de != self.draw_edges or dv != self.draw_verts): self.region.tag_redraw() # forced redraw -> not another modal # -> no need for a new one self.flag_redraw = True
def Point2D_to_Ray(self, p2d): o = Point(region_2d_to_origin_3d(self.rgn, self.r3d, p2d)) d = Direction(region_2d_to_vector_3d(self.rgn, self.r3d, p2d)) return Ray(o, d)
def mouse_vector(op): return region_2d_to_vector_3d(region(), region3d(), op.mouse)
def modal(self, context, event): tag_redraw_all_view3d() if len(self.align_points) < 3: self.align_msg = "Pick at least %s more pts" % str( 3 - len(self.align_points)) else: self.align_msg = "More points optional" if len(self.base_points) < 3: self.base_msg = "Pick at last %s more pts" % str( 3 - len(self.base_points)) else: self.base_msg = "More points optional" if len(self.base_points) > 3 and len(self.align_points) > 3 and len( self.base_points) != len(self.align_points): if len(self.align_points) < len(self.base_points): self.align_msg = "Pick %s more pts to match" % str( len(self.base_points) - len(self.align_points)) else: self.base_msg = "Pick %s more pts to match" % str( len(self.align_points) - len(self.base_points)) if len(self.base_points) == len( self.align_points) and len(self.base_points) >= 3: self.base_msg = "Hit Enter to Align" self.align_msg = "Hit Enter to Align" if event.type == 'LEFTMOUSE' and event.value == 'PRESS': ray_max = 10000 if event.mouse_x > self.area_align.x and event.mouse_x < self.area_align.x + self.area_align.width: for reg in self.area_align.regions: if reg.type == 'WINDOW': region = reg for spc in self.area_align.spaces: if spc.type == 'VIEW_3D': rv3d = spc.region_3d #just transform the mouse window coords into the region coords coord = (event.mouse_x - region.x, event.mouse_y - region.y) #are the cords the problem print('align cords: ' + str(coord)) print(str((event.mouse_region_x, event.mouse_region_y))) view_vector = view3d_utils.region_2d_to_vector_3d( region, rv3d, coord) ray_origin = view3d_utils.region_2d_to_origin_3d( region, rv3d, coord) ray_target = ray_origin + (view_vector * ray_max) print('in the align object window') (d, (ok, hit, normal, face_index)) = ray_cast_region2d(region, rv3d, coord, self.obj_align) if hit: print('hit! align_obj %s' % self.obj_align.name) #local space of align object self.align_points.append(hit) else: for reg in self.area_base.regions: if reg.type == 'WINDOW': region = reg for spc in self.area_base.spaces: if spc.type == 'VIEW_3D': rv3d = spc.region_3d coord = (event.mouse_x - region.x, event.mouse_y - region.y) view_vector = view3d_utils.region_2d_to_vector_3d( region, rv3d, coord) ray_origin = view3d_utils.region_2d_to_origin_3d( region, rv3d, coord) ray_target = ray_origin + (view_vector * ray_max) print('in the base object window') (d, (ok, hit, normal, face_index)) = ray_cast_region2d(region, rv3d, coord, self.obj_base) if ok: print('hit! base_obj %s' % self.obj_base.name) #points in local space of align object self.base_points.append( self.obj_align.matrix_world.inverted() * self.obj_base.matrix_world * hit) return {'RUNNING_MODAL'} elif event.type == 'RIGHTMOUSE' and event.value == 'PRESS': if event.mouse_x > self.area_align.x and event.mouse_x < self.area_align.x + self.area_align.width: self.align_points.pop() else: self.base_points.pop() return {'RUNNING_MODAL'} if event.type in {'WHEELUPMOUSE', 'WHEELDOWNMOUSE'}: return {'PASS_THROUGH'} if self.modal_state == 'NAVIGATING': if (event.type in { 'MOUSEMOVE', 'MIDDLEMOUSE', 'NUMPAD_2', 'NUMPAD_4', 'NUMPAD_6', 'NUMPAD_8', 'NUMPAD_1', 'NUMPAD_3', 'NUMPAD_5', 'NUMPAD_7', 'NUMPAD_9' } and event.value == 'RELEASE'): self.modal_state = 'WAITING' return {'PASS_THROUGH'} if (event.type in { 'MIDDLEMOUSE', 'NUMPAD_2', 'NUMPAD_4', 'NUMPAD_6', 'NUMPAD_8', 'NUMPAD_1', 'NUMPAD_3', 'NUMPAD_5', 'NUMPAD_7', 'NUMPAD_9' } and event.value == 'PRESS'): self.modal_state = 'NAVIGATING' return {'PASS_THROUGH'} elif event.type in {'ESC'}: bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW') return {'CANCELLED'} elif event.type == 'RET': if len(self.align_points) >= 3 and len( self.base_points) >= 3 and len(self.align_points) == len( self.base_points): bpy.types.SpaceView3D.draw_handler_remove( self._handle, 'WINDOW') self.de_localize(context) self.align_obj(context) context.scene.objects.active = self.obj_align self.obj_align.select = True self.obj_base = True return {'FINISHED'} return {'RUNNING_MODAL'}
def modal(self, context, event): do_exit = False sprytile_data = context.scene.sprytile_data # Check that the mouse is inside the region region = context.region coord = Vector((event.mouse_region_x, event.mouse_region_y)) out_of_region = coord.x < 0 or coord.y < 0 or coord.x > region.width or coord.y > region.height if sprytile_data.is_running is False: do_exit = True if event.type == 'ESC': do_exit = True if event.type == 'RIGHTMOUSE' and out_of_region: do_exit = True if context.object.mode != 'EDIT': do_exit = True if do_exit: self.exit_modal(context) return {'CANCELLED'} if self.no_undo and sprytile_data.is_grid_translate is False: # print("no undo on, grid translate off") self.no_undo = False if event.type == 'TIMER': view_axis = self.find_view_axis(context) if view_axis is not None: if view_axis != sprytile_data.normal_mode: self.virtual_cursor.clear() sprytile_data.normal_mode = view_axis sprytile_data.lock_normal = False return {'PASS_THROUGH'} # Mouse in Sprytile UI, eat this event without doing anything if context.scene.sprytile_ui.use_mouse: self.set_preview_data(None, None) return {'RUNNING_MODAL'} # Mouse move triggers preview drawing draw_preview = sprytile_data.paint_mode in { 'MAKE_FACE', 'FILL', 'PAINT' } if draw_preview: if (event.alt or context.scene.sprytile_ui.use_mouse ) or sprytile_data.is_snapping: draw_preview = False # Refreshing the mesh, preview needs constantly refreshed # mesh or bad things seem to happen. This can potentially get expensive if self.refresh_mesh or self.bmesh.is_valid is False or draw_preview: self.update_bmesh_tree(context, True) self.refresh_mesh = False # Potentially expensive, test if there is a selected mesh element if event.type == 'MOUSEMOVE': sprytile_data.has_selection = False for v in self.bmesh.verts: if v.select: sprytile_data.has_selection = True break context.area.tag_redraw() # If outside the region, pass through if out_of_region: # If preview data exists, clear it if SprytileModalTool.preview_verts is not None: self.set_preview_data(None, None) return {'PASS_THROUGH'} modal_return = {'PASS_THROUGH'} # Process keyboard events, if returned something end here key_return = self.handle_keys(context, event) if key_return is not None: self.set_preview_data(None, None) modal_return = key_return # Didn't process keyboard, process mouse now else: mouse_return = self.handle_mouse(context, event, draw_preview) if mouse_return is not None: modal_return = mouse_return # Signals tools to draw preview self.draw_preview = draw_preview and self.refresh_mesh is False # Clear preview data if not drawing preview if not self.draw_preview: SprytileModalTool.preview_verts = None SprytileModalTool.preview_uvs = None # Build the data that will be used by tool observers region = context.region rv3d = context.region_data coord = event.mouse_region_x, event.mouse_region_y no_data = self.tree is None or rv3d is None if no_data is False: # get the ray from the viewport and mouse ray_vector = view3d_utils.region_2d_to_vector_3d( region, rv3d, coord) ray_origin = view3d_utils.region_2d_to_origin_3d( region, rv3d, coord) self.rx_data = DataObjectDict(context=context, ray_vector=ray_vector, ray_origin=ray_origin) else: self.rx_data = None # Push the event data out through rx_observer for tool observers if self.rx_observer is not None: self.rx_observer.on_next( DataObjectDict( paint_mode=sprytile_data.paint_mode, event=event, left_down=self.left_down, build_preview=self.draw_preview, )) return modal_return
def cursor_snap(self, context, event): if self.tree is None or context.scene.sprytile_ui.use_mouse is True: return # get the context arguments scene = context.scene region = context.region rv3d = context.region_data coord = event.mouse_region_x, event.mouse_region_y # get the ray from the viewport and mouse ray_vector = view3d_utils.region_2d_to_vector_3d(region, rv3d, coord) ray_origin = view3d_utils.region_2d_to_origin_3d(region, rv3d, coord) up_vector, right_vector, plane_normal = sprytile_utils.get_current_grid_vectors( scene) if event.type in self.is_keyboard_list and event.shift and event.value == 'PRESS': if scene.sprytile_data.cursor_snap == 'GRID': scene.sprytile_data.cursor_snap = 'VERTEX' else: scene.sprytile_data.cursor_snap = 'GRID' # Snap cursor, depending on setting if scene.sprytile_data.cursor_snap == 'GRID': location = intersect_line_plane(ray_origin, ray_origin + ray_vector, scene.cursor_location, plane_normal) if location is None: return world_pixels = scene.sprytile_data.world_pixels target_grid = sprytile_utils.get_grid( context, context.object.sprytile_gridid) grid_x = target_grid.grid[0] grid_y = target_grid.grid[1] grid_position, x_vector, y_vector = sprytile_utils.get_grid_pos( location, scene.cursor_location, right_vector.copy(), up_vector.copy(), world_pixels, grid_x, grid_y) scene.cursor_location = grid_position elif scene.sprytile_data.cursor_snap == 'VERTEX': location, normal, face_index, distance = self.raycast_object( context.object, ray_origin, ray_vector) if location is None: return # Location in world space, convert to object space matrix = context.object.matrix_world.copy() matrix_inv = matrix.inverted() location, normal, face_index, dist = self.tree.find_nearest( matrix_inv * location) if location is None: return # Found the nearest face, go to BMesh to find the nearest vertex if self.bmesh is None: self.refresh_mesh = True return if face_index >= len(self.bmesh.faces) or face_index < 0: return face = self.bmesh.faces[face_index] closest_vtx = -1 closest_dist = float('inf') # positions are in object space for vtx_idx, vertex in enumerate(face.verts): test_dist = (location - vertex.co).magnitude if test_dist < closest_dist: closest_vtx = vtx_idx closest_dist = test_dist # convert back to world space if closest_vtx != -1: scene.cursor_location = matrix * face.verts[closest_vtx].co
def find_face_tile(self, context, event): if self.tree is None or context.scene.sprytile_ui.use_mouse is True: return # get the context arguments region = context.region rv3d = context.region_data coord = event.mouse_region_x, event.mouse_region_y # get the ray from the viewport and mouse ray_vector = view3d_utils.region_2d_to_vector_3d(region, rv3d, coord) ray_origin = view3d_utils.region_2d_to_origin_3d(region, rv3d, coord) location, normal, face_index, distance = self.raycast_object( context.object, ray_origin, ray_vector) if location is None: return face = self.bmesh.faces[face_index] grid_id, tile_packed_id, width, height, origin_id = self.get_face_tiledata( face) if None in {grid_id, tile_packed_id}: return tilegrid = sprytile_utils.get_grid(context, grid_id) if tilegrid is None: return texture = sprytile_utils.get_grid_texture(context.object, tilegrid) if texture is None: return paint_setting_layer = self.bmesh.faces.layers.int.get('paint_settings') if paint_setting_layer is not None: paint_setting = face[paint_setting_layer] sprytile_utils.from_paint_settings(context.scene.sprytile_data, paint_setting) row_size = math.ceil(texture.size[0] / tilegrid.grid[0]) tile_y = math.floor(tile_packed_id / row_size) tile_x = tile_packed_id % row_size if event.ctrl: width = 1 height = 1 elif origin_id > -1: origin_y = math.floor(origin_id / row_size) origin_x = origin_id % row_size tile_x = min(origin_x, tile_x) tile_y = min(origin_y, tile_y) if width == 0: width = 1 if height == 0: height = 1 context.object.sprytile_gridid = grid_id tilegrid.tile_selection[0] = tile_x tilegrid.tile_selection[1] = tile_y tilegrid.tile_selection[2] = width tilegrid.tile_selection[3] = height bpy.ops.sprytile.build_grid_list()
def raycast(context, event, diff): controller = getLightController() ##### """Run this function on left mouse, execute the ray cast""" # get the context arguments scene = context.scene region = context.region rv3d = context.region_data coord = event.mouse_region_x, event.mouse_region_y # get the ray from the viewport and mouse view_vector = view3d_utils.region_2d_to_vector_3d(region, rv3d, coord) ray_origin = view3d_utils.region_2d_to_origin_3d(region, rv3d, coord) ray_target = ray_origin + view_vector def visible_objects_and_duplis(): """Loop over (object, matrix) pairs (mesh only)""" for obj in context.visible_objects: if isFamily(obj): continue if obj.type == 'MESH': yield (obj, obj.matrix_world.copy()) if obj.dupli_type != 'NONE': obj.dupli_list_create(scene) for dob in obj.dupli_list: obj_dupli = dob.object if obj_dupli.type == 'MESH': yield (obj_dupli, dob.matrix.copy()) obj.dupli_list_clear() def obj_ray_cast(obj, matrix): """Wrapper for ray casting that moves the ray into object space""" # get the ray relative to the object matrix_inv = matrix.inverted() ray_origin_obj = matrix_inv * ray_origin ray_target_obj = matrix_inv * ray_target ray_direction_obj = ray_target_obj - ray_origin_obj # cast the ray success, location, normal, face_index = obj.ray_cast(ray_origin_obj, ray_direction_obj) if success: return location, normal, face_index else: return None, None, None # cast rays and find the closest object best_length_squared = -1.0 best_obj = None normal = None location = None for obj, matrix in visible_objects_and_duplis(): if obj.type == 'MESH': hit, hit_normal, face_index = obj_ray_cast(obj, matrix) if hit is not None: hit_world = matrix * hit length_squared = (hit_world - ray_origin).length_squared if best_obj is None or length_squared < best_length_squared: best_length_squared = length_squared best_obj = obj normal = hit_normal # local space location = hit_world if best_obj is None: return {'RUNNING_MODAL'} # convert normal from local space to global matrix = best_obj.matrix_world matrix_new = matrix.to_3x3().inverted().transposed() normal = matrix_new * normal normal.normalize() ##### profile = findLightGrp(controller).parent handle = [ob for ob in profile.children if ob.name.startswith('BLS_HANDLE')][0] lightmesh = getLightMesh() position = intersect_line_sphere( location - handle.location, (normal if diff else view_vector.reflect(normal)) + location - handle.location, Vector((0,0,0)), lightmesh.location.x, False, )[0] if not position: return {'RUNNING_MODAL'} # ctrl x x,y,z = position ctrl_x = (degrees(atan2(-x, y)) % 360) * (4/360) -2 +0.015 # ctrl y deg = copysign(degrees(Vector.angle(Vector((x,y,z)), Vector((x,y,0)))), z) ctrl_y = deg / 90 controller.location.x = ctrl_x controller.location.y = ctrl_y
def Point2D_to_Direction(self, xy: Point2D): if xy is None: return None return Direction( region_2d_to_vector_3d(self.actions.region, self.actions.r3d, xy))
def modal(self, context, event): if context.area is None: self.exit(context) return {'CANCELLED'} if not sprytile_utils.get_current_tool(context).startswith("sprytile"): self.exit(context) return {'CANCELLED'} if context.mode != 'EDIT_MESH': self.exit(context) return {'CANCELLED'} elif not VIEW3D_OP_SprytileGui.is_running: VIEW3D_OP_SprytileGui.is_running = True # Check that the mouse is inside the region region = context.region coord = Vector((event.mouse_region_x, event.mouse_region_y)) VIEW3D_OP_SprytileGui.out_of_region = coord.x < 0 or coord.y < 0 or coord.x > region.width or coord.y > region.height if event.type == 'TIMER': self.update_view_axis(context) if self.label_counter > 0: self.label_counter -= 1 # Check if current_grid is different from current sprytile grid if context.object.sprytile_gridid != VIEW3D_OP_SprytileGui.current_grid: # Setup the offscreen texture for the new grid setup_off_return = VIEW3D_OP_SprytileGui.setup_offscreen( self, context) if setup_off_return is not None: return setup_off_return # Skip redrawing on this frame return {'PASS_THROUGH'} ret_val = self.handle_ui(context, event) VIEW3D_OP_SprytileGui.tile_ui_active = ret_val == 'RUNNING_MODAL' # Build the data that will be used by tool observers rv3d = context.region_data coord = event.mouse_region_x, event.mouse_region_y no_data = rv3d is None if no_data is False: # get the ray from the viewport and mouse ray_vector = view3d_utils.region_2d_to_vector_3d( region, rv3d, coord) ray_origin = view3d_utils.region_2d_to_origin_3d( region, rv3d, coord) mode = bpy.context.scene.sprytile_data.paint_mode if VIEW3D_OP_SprytileGui.build_previews[mode]: sprytile_modal.VIEW3D_OP_SprytileModalTool.verify_bmesh_layers( bmesh.from_edit_mesh(context.object.data)) VIEW3D_OP_SprytileGui.build_previews[mode].build_preview( context, context.scene, ray_origin, ray_vector) else: sprytile_preview.set_preview_data(None, None) context.scene.sprytile_ui.is_dirty = False context.area.tag_redraw() return {ret_val}
def click_add_point(self, context, x, y): ''' x,y = event.mouse_region_x, event.mouse_region_y this will add a point into the bezier curve or close the curve into a cyclic curve ''' region = context.region rv3d = context.region_data coord = x, y view_vector = view3d_utils.region_2d_to_vector_3d(region, rv3d, coord) ray_origin = view3d_utils.region_2d_to_origin_3d(region, rv3d, coord) ray_target = ray_origin + (view_vector * 1000) hit = False # ray cast on the any object if self.snap_type == 'SCENE': mx = Matrix.Identity(4) # loc is given in world loc...no need to multiply by obj matrix imx = Matrix.Identity(4) no_mx = Matrix.Identity(3) if bversion() < '002.077.000': hit, obj, omx, loc, no = context.scene.ray_cast(ray_origin, ray_target) #changed in 2.77 else: hit, loc, no, ind, obj, omx = context.scene.ray_cast(ray_origin, view_vector) iomx = omx.inverted() no_mx = iomx.to_3x3().transposed() # if not hit: # # cast the ray into a plane perpendicular to the view dir, at the last bez point of the curve # # view_direction = rv3d.view_rotation * Vector((0,0,-1)) # # if len(self.b_pts): # plane_pt = self.b_pts[-1].location # else: # plane_pt = context.scene.cursor_location # loc = intersect_line_plane(ray_origin, ray_target,plane_pt, view_direction) # hit = True # ray cast on self.snap_ob elif self.snap_type == 'OBJECT': mx = self.snap_ob.matrix_world imx = mx.inverted() no_mx = imx.to_3x3().transposed() if bversion() < '002.077.000': loc, no, face_ind = self.snap_ob.ray_cast(imx * ray_origin, imx * ray_target)[0 if bversion() < '002.077.000' else 1:] else: hit, loc, no, face_ind = self.snap_ob.ray_cast(imx * ray_origin, imx * ray_target - imx*ray_origin) if face_ind != -1: hit = True # no object was hit if not hit: self.selected = -1 return False # select existing point if self.hovered[0] == 'POINT': self.selected = self.hovered[1] return False # add new point elif self.hovered[0] == None: new_point = D3Point(location=mx * loc, surface_normal=no_mx * no, view_direction=view_vector, source_object=obj if self.snap_type == "SCENE" else self.snap_ob) self.b_pts.append(new_point) new_point.label = self.getLabel(len(self.b_pts) - 1) self.hovered = ['POINT', len(self.b_pts) - 1] return True
def getLoc(self, context, event): coord = event.mouse_region_x, event.mouse_region_y region = context.region rv3d = context.space_data.region_3d vec = region_2d_to_vector_3d(region, rv3d, coord) return region_2d_to_location_3d(region, rv3d, coord, vec)
def mft_pick_and_clone(self, context, event, ray_max=10000.0): """Run this function on left mouse, execute the ray cast""" # get the context arguments scene = context.scene region = context.region rv3d = context.region_data coord = event.mouse_region_x, event.mouse_region_y def mft_obj_ray_cast(obj, matrix, view_vector, ray_origin): """Wrapper for ray casting that moves the ray into object space""" ray_target = ray_origin + (view_vector * ray_max) # get the ray relative to the object matrix_inv = matrix.inverted() ray_origin_obj = matrix_inv * ray_origin ray_target_obj = matrix_inv * ray_target # cast the ray hit_result, hit, normal, face_index = obj.ray_cast( ray_origin_obj, ray_target_obj) if hit_result: hit_world = matrix * hit length_squared = (hit_world - ray_origin).length_squared if face_index != -1: #normal_world = (matrix.to_quaternion() * normal).normalized() normal_world = (matrix.to_quaternion() * normal).to_4d() normal_world.w = 0 normal_world = ( matrix.to_quaternion() * (matrix_inv * normal_world).to_3d()).normalized() return normal_world, hit_world, length_squared return None, None, None # cast rays and find the closest object best_length_squared = ray_max * ray_max best_obj, best_obj_nor, best_obj_pos = None, None, None best_obj_rand, best_obj_nor_rand, best_obj_pos_rand = None, None, None best_obj_hit = None mifthCloneTools = bpy.context.scene.mifthCloneTools thePressure = max(1.0 - mifthCloneTools.drawPressure, self.tabletPressure) # The pressure of a pen! for obj, matrix in self.dupliList: # get the ray from the viewport and mouse view_vector_mouse = view3d_utils.region_2d_to_vector_3d( region, rv3d, coord) ray_origin_mouse = view3d_utils.region_2d_to_origin_3d( region, rv3d, coord) # Do RayCast! t1,t2,t3,t4 - temp values t1, t2, t3 = mft_obj_ray_cast(obj, matrix, view_vector_mouse, ray_origin_mouse) if t1 is not None and t3 < best_length_squared: best_obj = obj best_obj_nor, best_obj_pos, best_length_squared = t1, t2, t3 best_obj_hit = t2 # Check for stroke length if best_obj is not None: previousHit = None if len(self.currentStrokeList) > 0: previousHit = self.currentStrokeList[-1] # get Last Element # Check for stroke strokeLength = mifthCloneTools.drawStrokeLength if mifthCloneTools.drawPressureScale is True and mifthCloneTools.drawPressureRelativeStroke is True and self.tabletPressure < 1.0 and mifthCloneTools.drawPressure > 0.0: strokeLength *= thePressure if previousHit is not None and ( best_obj_pos - previousHit.hitPoint).length < strokeLength: best_obj = None # Don't do cloning # random scatter things if mifthCloneTools.drawRandomStrokeScatter > 0.0 and best_obj is not None and t1 is not None: # Random Vec randX = random.uniform( -1.0, 1.0 ) * mifthCloneTools.drawRandomStrokeScatter # 3.0 is random addition randY = random.uniform( -1.0, 1.0 ) * mifthCloneTools.drawRandomStrokeScatter # 3.0 is random addition randZ = random.uniform(-1.0, 1.0) * mifthCloneTools.drawRandomStrokeScatter if mifthCloneTools.drawPressureScatter is True and self.tabletPressure < 1.0 and mifthCloneTools.drawPressure > 0.0: randX *= thePressure randY *= thePressure randZ *= thePressure randVect = Vector((randX, randY, randZ)) for obj, matrix in self.dupliList: ray_origin_rand = best_obj_pos + randVect ray_origin_rand_2d = view3d_utils.location_3d_to_region_2d( region, rv3d, ray_origin_rand) view_vector_rand = view3d_utils.region_2d_to_vector_3d( region, rv3d, (ray_origin_rand_2d.x, ray_origin_rand_2d.y)) t1, t2, t3 = mft_obj_ray_cast(obj, matrix, view_vector_rand, ray_origin_rand) # 3.0 is random addition if t1 is not None and ( t2 - best_obj_hit ).length <= mifthCloneTools.drawRandomStrokeScatter * 3.0: best_obj_nor, best_obj_pos = t1, t2 # now we have the object under the mouse cursor, if best_obj is not None: objToClone = bpy.data.objects.get(random.choice(drawForClonesObj)) best_obj_nor = best_obj_nor.normalized() # clone object newDup = bpy.data.objects.new(objToClone.name, objToClone.data) # copy draw type newDup.draw_type = objToClone.draw_type newDup.show_wire = objToClone.show_wire # copy transformation newDup.matrix_world = objToClone.matrix_world newDup.location = best_obj_pos newDup.scale = objToClone.scale newDup.rotation_euler = objToClone.rotation_euler # bpy.ops.object.rotation_clear() context.scene.objects.link(newDup) newDup.select = True context.scene.objects.active = newDup # Rotation To Normal if mifthCloneTools.drawClonesNormalRotate is True: # rotate To Normal newDupZAxis = get_obj_axis(newDup, 'Z') angleRotate = newDupZAxis.angle(best_obj_nor) rotateAxis = newDupZAxis.cross(best_obj_nor).normalized() bpy.ops.transform.rotate(value=angleRotate, axis=((rotateAxis.x, rotateAxis.y, rotateAxis.z)), proportional='DISABLED') # Change Axis if mifthCloneTools.drawClonesAxis == 'Y': bpy.ops.transform.rotate(value=math.radians(90), axis=get_obj_axis(newDup, 'X'), proportional='DISABLED') elif mifthCloneTools.drawClonesAxis == '-Y': bpy.ops.transform.rotate(value=math.radians(-90), axis=get_obj_axis(newDup, 'X'), proportional='DISABLED') bpy.ops.transform.rotate(value=math.radians(180), axis=get_obj_axis(newDup, 'Y'), proportional='DISABLED') elif mifthCloneTools.drawClonesAxis == '-Z': bpy.ops.transform.rotate(value=math.radians(180), axis=get_obj_axis(newDup, 'X'), proportional='DISABLED') elif mifthCloneTools.drawClonesAxis == 'X': bpy.ops.transform.rotate(value=math.radians(-90), axis=get_obj_axis(newDup, 'Y'), proportional='DISABLED') elif mifthCloneTools.drawClonesAxis == '-X': bpy.ops.transform.rotate(value=math.radians(90), axis=get_obj_axis(newDup, 'Y'), proportional='DISABLED') # Other rotate if mifthCloneTools.drawClonesRadialRotate is True or mifthCloneTools.drawClonesDirectionRotate is True: # Ratate to Direction newDirRotLookAtt = None if mifthCloneTools.drawClonesDirectionRotate is True: previousHit = None if len(self.currentStrokeList) > 0: previousHit = self.currentStrokeList[ -1] # get Last Element else: if len(self.allStrokesList) > 0: previousHit = self.allStrokesList[-1][ -1] # get Last Element of previous Stroke if previousHit is not None: newDirRotLookAtt = (best_obj_hit - previousHit.hitPoint).normalized() else: newDirRotLookAtt = Vector((0.0, 0.0, 1.0)) # Get the axis to rotate if newDirRotLookAtt is not None: tempYCross = best_obj_nor.cross(newDirRotLookAtt).normalized() if tempYCross != Vector((0.0, 0.0, 0.0)): tempYCross.negate() radialVec = best_obj_nor.cross(tempYCross).normalized() axisPickTemp = 'Y' if mifthCloneTools.drawClonesAxis == 'Y': axisPickTemp = 'Z' if mifthCloneTools.drawClonesAxis == '-Y': axisPickTemp = 'Z' if mifthCloneTools.drawClonesAxis == '-Z': axisPickTemp = 'Y' if mifthCloneTools.drawClonesAxis == 'X': axisPickTemp = 'Z' if mifthCloneTools.drawClonesAxis == '-X': axisPickTemp = 'Z' tempY = get_obj_axis(newDup, axisPickTemp) xyAngleRotate = Vector(radialVec).angle(tempY) if tempYCross.angle(tempY) > math.radians(90.0): xyAngleRotate = -xyAngleRotate bpy.ops.transform.rotate(value=xyAngleRotate, axis=best_obj_nor, proportional='DISABLED') # newDupMatrix2 = newDup.matrix_world # newDupZAxisTuple2 = ( # newDupMatrix2[0][2], newDupMatrix2[1][2], newDupMatrix2[2][2]) # newDupZAxis2 = ( # Vector(newDupZAxisTuple2)).normalized() # newDirRotVec2 = (newDirRotLookAtt.cross(best_obj_nor)).normalized().cross( # best_obj_nor).normalized() # newDirRotAngle = newDirRotVec2.angle(newDupZAxis2) # fixDirRotAngle = newDirRotLookAtt.cross( # best_obj_nor).angle(newDupZAxis2) # if fixDirRotAngle < math.radians(90.0): # newDirRotAngle = - \ # newDirRotAngle # As we do it in negative axis # Main rotation # bpy.ops.transform.rotate(value=newDirRotAngle, axis=( #(best_obj_nor.x, best_obj_nor.y, best_obj_nor.z)), proportional='DISABLED') # set PreviousClone position # self.prevClonePos = best_obj_hit # Random rotation along Picked Normal if mifthCloneTools.randNormalRotateClone > 0.0: randNorAngle = random.uniform( math.radians(-mifthCloneTools.randNormalRotateClone), math.radians(mifthCloneTools.randNormalRotateClone)) randNorAxis = (best_obj_nor.x, best_obj_nor.y, best_obj_nor.z) if mifthCloneTools.drawClonesRadialRotate is False and mifthCloneTools.drawClonesNormalRotate is False: randNorAxis = (0.0, 0.0, 1.0) bpy.ops.transform.rotate(value=randNorAngle, axis=(randNorAxis), proportional='DISABLED') # Random rotation along Picked Normal if mifthCloneTools.randDirectionRotateClone > 0.0: randDirX, randDirY, randDirZ = random.uniform( 0.0, 1.0), random.uniform(0.0, 1.0), random.uniform(0.0, 1.0) randDirVec = (Vector((randDirX, randDirY, randDirZ))).normalized() randDirAngle = random.uniform( math.radians(-mifthCloneTools.randDirectionRotateClone), math.radians(mifthCloneTools.randDirectionRotateClone)) bpy.ops.transform.rotate(value=randDirAngle, axis=(randDirVec), proportional='DISABLED') # change Size if mifthCloneTools.drawPressure > 0.0 or mifthCloneTools.randScaleClone > 0.0: newSize = newDup.scale if self.tabletPressure < 1.0 and mifthCloneTools.drawPressure > 0.0 and mifthCloneTools.drawPressureScale is True: newSize *= thePressure if mifthCloneTools.randScaleClone > 0.0: randScaleClone = 1.0 - \ (random.uniform(0.0, 0.99) * mifthCloneTools.randScaleClone) newSize *= randScaleClone # Add this point with all its stuff self.currentStrokeList.append( MTFDCPoint(newDup, objToClone, best_obj_hit, best_obj_nor)) # do optimization for a stroke or not if mifthCloneTools.drawClonesOptimize is False: copy_settings_clones(newDup, objToClone) newDup.select = False # Clear Selection
def get_ray_hit(context, x, y): current_obj = context.object current_obj_matrix = current_obj.matrix_world current_obj_matrix_inv = current_obj_matrix.inverted() scene = context.scene region = context.region rv3d = context.region_data coord = x, y view_vector = view3d_utils.region_2d_to_vector_3d(region, rv3d, coord) ray_origin = view3d_utils.region_2d_to_origin_3d(region, rv3d, coord) ray_target = ray_origin + view_vector def visible_objects_and_duplis(): """Loop over (object, matrix) pairs (mesh only)""" depsgraph = context.evaluated_depsgraph_get() for dup in depsgraph.object_instances: if dup.is_instance: # Real dupli instance obj = dup.instance_object yield (obj) else: # Usual object obj = dup.object yield (obj) def obj_ray_cast(obj): matrix = obj.matrix_world matrix_inv = matrix.inverted() ray_origin_obj = matrix_inv @ ray_origin ray_target_obj = matrix_inv @ ray_target ray_direction_obj = ray_target_obj - ray_origin_obj success, location, normal, face_index = obj.ray_cast( ray_origin_obj, ray_direction_obj) if success: return location, normal, face_index, matrix, matrix_inv else: return None, None, None, None, None best_length_squared = float("inf") best_obj = None best_normal = None best_face_index = -1 best_location = None for obj in visible_objects_and_duplis(): if obj.name in [ ob.name for ob in context.view_layer.objects if ob.visible_get() ] and obj.type == 'MESH' and obj.name != current_obj.name and len( obj.data.polygons) > 0: hit, normal, face_index, matrix, matrix_inv = obj_ray_cast(obj) if hit is not None: hit_world = matrix @ hit length_squared = (hit_world - ray_origin).length_squared if best_obj is None or length_squared < best_length_squared: best_length_squared = length_squared best_obj = obj best_normal = normal @ matrix_inv @ current_obj_matrix best_location = hit best_face_index = face_index if best_obj is not None: # and matrix != None: best_location = best_obj.matrix_world @ best_location best_location = current_obj_matrix_inv @ best_location return best_location, best_normal, best_obj, best_face_index else: return None, None, None, -1
def razor2(vertice1, vertice2, vertice3, anglecorte, anglefijo): oa = bpy.context.active_object obj = bpy.context.object me = obj.data bm = bmesh.from_edit_mesh(me) pi = math.pi # constante de pi v1 = vertice1 v2 = vertice2 v3 = vertice3 angulocorte = anglecorte angulofijo = anglefijo angulocomplementario = pi - (angulocorte + angulofijo) print(angulocorte) print("angulo corte: " + str((180 * angulocorte) / pi)) print("angulo fijo: " + str((180 * angulofijo) / pi)) print("angulo complementario: " + str((180 * angulocomplementario) / pi)) #distancia entre los dos vertices dv2v3 = math.sqrt( math.fabs( (((((v3.co.x) - (v2.co.x))**2)) + ((((v3.co.y) - (v2.co.y))**2)) + ((((v3.co.z) - (v2.co.z))**2))))) print("distancia entre vertices opuestos v2v3: " + str(dv2v3)) dv1v3 = math.sqrt( math.fabs( (((((v1.co.x) - (v3.co.x))**2)) + ((((v1.co.y) - (v3.co.y))**2)) + ((((v1.co.z) - (v3.co.z))**2))))) #dv1v3 = math.sqrt(math.fabs((((((v1.co.x)-(v2.co.x))**2))+((((v1.co.y)-(v2.co.y))**2))+((((v1.co.z)-(v2.co.z))**2))))) print("distancia entre vertices opuestos v1v3: " + str(dv1v3)) #distancia nuevo vertice: # asigno la distancia del nuevo vertice colinear respecto al vertice 1 dv1v2 = (dv1v3 * math.sin(angulocorte)) / (math.sin(angulocomplementario)) print("distancia entre vertices v1v2: " + str(dv1v2)) #formula para cordenadas del nuevo vertice u = (dv1v2 / dv2v3) cox = (1 - u) * (v3.co.x) + (u * v2.co.x) coy = (1 - u) * (v3.co.y) + (u * v2.co.y) coz = (1 - u) * (v3.co.z) + (u * v2.co.z) #creacion nuevo vertice colinear vnuevo = mathutils.Vector((tuple((cox, coy, coz)))) v3 = bm.verts.new((cox, coy, coz)) veje = v1.co vdestino = v3.co listado = [v3] bmesh.ops.dissolve_verts(bm, verts=listado) for area in bpy.context.screen.areas: if area.type == 'VIEW_3D': override = bpy.context.copy() viewport = area.regions[4] #coo in 3d space co_3d = oa.matrix_world * vdestino #coo in the 3d view area (2d) co_2d = view3d_utils.location_3d_to_region_2d( viewport, area.spaces[0].region_3d, co_3d) try: v4 = view3d_utils.region_2d_to_vector_3d( viewport, area.spaces[0].region_3d, co_2d) v4 = v4 + vdestino normal = mathutils.geometry.normal(veje, vdestino, v4) #print("thenormal " + str(normal)) plane_no = normal plane_co = veje #print('cordenadas plano:\n', plane_no, '\n', plane_co) dist = 0.0001 # hidden geometry will not be affected. visible_geom = [ g for g in bm.faces[:] + bm.verts[:] + bm.edges[:] if not g.hide ] newdata = bmesh.ops.bisect_plane(bm, geom=visible_geom, dist=dist, plane_co=plane_co, plane_no=plane_no, use_snap_center=False, clear_outer=False, clear_inner=False) except: print("normal zero") #cortador(v2,v3) bmesh.update_edit_mesh(me, True)
def invoke(self, context, event): if context.area.type == 'VIEW_3D': # the arguments we pass the the callback args = (self, context) # Add the region OpenGL drawing callback self._timer = context.window_manager.event_timer_add( .1, context.window) #bpy.types.WindoManager.event_time_add? self._handle = bpy.types.SpaceView3D.draw_handler_add( bgl_utils.draw_callback_crevice_walking, args, 'WINDOW', 'POST_PIXEL') #initialze important values and gather important info region = context.region rv3d = context.space_data.region_3d coord = event.mouse_region_x, event.mouse_region_y self.tracer_name = context.object.name #adding bpy here because getting weird error self.target_name = [ ob.name for ob in context.selected_editable_objects if ob.name != self.tracer_name ][0] tracer = bpy.data.objects[self.tracer_name] target = bpy.data.objects[self.target_name] #target.select = False tracer.select = True context.scene.objects.active = tracer #mod = tracer.modifers['Shrinkwrap'] #going to need to do some checking for dumb scenarios #self.target = mod.target vec = view3d_utils.region_2d_to_vector_3d(region, rv3d, coord) ray_origin = view3d_utils.region_2d_to_location_3d( region, rv3d, coord, vec) #raycast onto active object ray_target = ray_origin + 10000 * vec [hit, normal, face] = odcutils.obj_ray_cast(target, target.matrix_world, ray_origin, ray_target) self.max_iters = 50 self.keep_iterating = False #turn this off/on when we get convergence on the bias point or add a new bias point self.session_iterations = 0 if hit: self.current_bias_point = target.matrix_world * hit else: self.current_bias_point = target.location self.confirmed_path = [] self.pending_path = [tracer.location.copy()] self.bias_points = [] self.bias_normals = [ ] #gotta keep track of the normals so we can re-orient the tracker pointed the right way self.step_size = .5 self.convergence_limit = 5 context.window_manager.modal_handler_add(self) return {'RUNNING_MODAL'} else: self.report({'WARNING'}, "View3D not found, cannot run operator") return {'CANCELLED'}
def pick_object(context, event, pick_objects, ray_max=10000.0): """Run this function on left mouse, execute the ray cast""" # get the context arguments scene = context.scene region = context.region rv3d = context.region_data coord = event.mouse_region_x, event.mouse_region_y # get the ray from the viewport and mouse view_vector = view3d_utils.region_2d_to_vector_3d(region, rv3d, coord) ray_origin = view3d_utils.region_2d_to_origin_3d(region, rv3d, coord) ray_target = ray_origin + (view_vector * ray_max) scene.cursor.location = ray_target def visible_objects_and_duplis(): """Loop over (object, matrix) pairs (mesh only)""" for obj in context.visible_objects: # scene.objects: if obj.hide: continue if obj.type == 'MESH': yield (None, obj, obj.matrix_world.copy()) if obj.instance_type != 'NONE': print("DupliInst: %r" % obj) obj.dupli_list_create(scene) # matrix = obj.matrix_world.copy() for dob in obj.dupli_list: obj_dupli = dob.object if not obj_dupli.hide: # print("Dupli: %r" % obj_dupli) if obj_dupli.type == 'MESH': yield (obj, obj_dupli, dob.matrix.copy()) obj.dupli_list_clear() def obj_ray_cast(obj, matrix): """Wrapper for ray casting that moves the ray into object space""" # get the ray relative to the object matrix_inv = matrix.inverted() ray_origin_obj = matrix_inv * ray_origin ray_target_obj = matrix_inv * ray_target mesh = obj.data if not mesh.polygons: return None, None, None hit, normal, face_index = obj.ray_cast(ray_origin_obj, ray_target_obj) if face_index == -1: hit, normal, face_index = obj.ray_cast(ray_target_obj, ray_origin_obj) if face_index != -1: return hit, normal, face_index else: return None, None, None # cast rays and find the closest object best_length_squared = ray_max * ray_max best_obj = None best_obj_parent = None for obj_parent, obj, matrix in visible_objects_and_duplis(): if obj.type == 'MESH': hit, normal, face_index = obj_ray_cast(obj, matrix) if hit is not None: length_squared = (hit - ray_origin).length_squared if length_squared < best_length_squared: best_length_squared = length_squared best_obj = obj best_obj_parent = obj_parent # now we have the object under the mouse cursor, # we could do lots of stuff but for the example just select. if best_obj is not None: pick_objects.append((best_obj, best_obj.hide, best_obj.hide_render)) best_obj.hide = True best_obj.hide_render = True #if best_obj_parent: # best_obj_parent.update_tag(refresh={'OBJECT'}) #scene.update() return True else: print("found none") return False
def grab_mouse_move(self, context, x, y, normal=False): region = context.region rv3d = context.region_data coord = x, y view_vector = view3d_utils.region_2d_to_vector_3d(region, rv3d, coord) ray_origin = view3d_utils.region_2d_to_origin_3d(region, rv3d, coord) ray_target = ray_origin + (view_vector * 1000) hit = False if self.snap_type == 'SCENE': if bversion() < '002.077.000': res, obj, mx, loc, no = context.scene.ray_cast( ray_origin, ray_target) else: res, loc, no, ind, obj, mx = context.scene.ray_cast( ray_origin, view_vector) if res: hit = True elif self.snap_type == 'OBJECT': mx = self.snap_ob.matrix_world imx = mx.inverted() if bversion() < '002.077.000': loc, no, face_ind = self.snap_ob.ray_cast( imx * ray_origin, imx * ray_target) if face_ind != -1: hit = True else: ok, loc, no, face_ind = self.snap_ob.ray_cast( imx * ray_origin, imx * ray_target - imx * ray_origin) if ok: hit = True if not hit: self.grab_cancel() else: world_location = mx * loc imx = mx.inverted() #this will be the object Z axis world_normal = imx.transposed().to_3x3() * no if normal: ob_Z = world_normal ob_Z.normalize() view_Y = rv3d.view_rotation * Vector((0, 1, 0)) if self.bracket_obj.name.startswith( "U") or self.bracket_obj.name.startswith("u"): view_Y *= -1 #project view y into the tangetnt plane of teh surface ob_Y = view_Y - view_Y.dot(ob_Z) * ob_Z ob_Y.normalize() ob_X = ob_Y.cross(ob_Z) ob_X.normalize() #rotation matrix from principal axes T = Matrix.Identity(3) #make the columns of matrix X, Y, Z T[0][0], T[0][1], T[0][2] = ob_X[0], ob_Y[0], ob_Z[0] T[1][0], T[1][1], T[1][2] = ob_X[1], ob_Y[1], ob_Z[1] T[2][0], T[2][1], T[2][2] = ob_X[2], ob_Y[2], ob_Z[2] rot = T.to_4x4() else: rot = self.bracket_obj.matrix_world.to_3x3().to_4x4() loc = Matrix.Translation(world_location) self.bracket_obj.matrix_world = loc * rot
def pick_object(region, rv3d, x, y, near, far, objects): ''' Selects an object underneath given screen coordinates Parameters: region (bpy.types.Region): Section of the UI in which to do picking rv3d (bpy.types.RegionView3D): 3D view region data near (float): Near clipping plane far (float): Far clipping plane objects (seq<bpy.types.Object>): Sequence of pickable objects Returns: (obj, location, normal, index): Reference to picked object and raycast hit information; otherwise None obj (bpy.types.Object): Nearest object in path of the ray location (mathutils.Vector): Object space location of ray-face intersection normal (mathutils.Vector): Normal vector of intersected face index (int): Index of intersected face ''' blender_version = bpy.app.version # Determine ray extents. coord = mathutils.Vector((x, y)) ray_dir = region_2d_to_vector_3d(region, rv3d, coord) ray_start = region_2d_to_origin_3d(region, rv3d, coord) + ray_dir * near ray_end = ray_start + ray_dir * (far - near) # If the view is an orthographic projection, the ray's end may exist in # front of the mesh object. Therefore, it is necessary to move the ray's # end a sufficient distance parallel to the ray's direction to ensure that # the ray potentially intersects the mesh object. if rv3d.view_perspective == 'ORTHO': ray_end += sys.maxsize * ray_dir # Pick a mesh object underneath given screen coordinates. result = None min_dist_squared = sys.float_info.max for obj in objects: # Skip objects that cannot participate in ray casting. if (not obj.type == 'MESH' or obj.mode == 'EDIT' or not obj.data.polygons): continue # Cast ray in object space. inverse_model_matrix = obj.matrix_world.inverted() hit = obj.ray_cast(inverse_model_matrix @ ray_start, inverse_model_matrix @ ray_end) location, normal, index = hit[1:] # Compare intersection distances. if index != -1: dist_squared = (obj.matrix_world @ location - ray_start).length_squared # Record closer of the two hits. if dist_squared < min_dist_squared: min_dist_squared = dist_squared result = (obj, location, normal, index) return result
def modal(self, context, event): np_print('05_BglPlane_START', ';', 'NP020RM.flag = ', NP020RM.flag) context.area.tag_redraw() flag = NP020RM.flag mode = NP020RM.mode plane = NP020RM.plane helper = NP020RM.helper centerloc = NP020RM.centerloc if event.type == 'MOUSEMOVE': self.co2d = ((event.mouse_region_x, event.mouse_region_y)) np_print('05_BglPlane_mousemove', ';', 'NP020RM.flag = ', NP020RM.flag, 'NP020RM.mode = ', NP020RM.mode, 'NP020RM.plane = ', NP020RM.plane) elif event.type in ('LEFTMOUSE', 'RIGHTMOUSE', 'RET', 'NUMPAD_ENTER'): bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW') region = context.region rv3d = context.region_data co2d = self.co2d view_vector = view3d_utils.region_2d_to_vector_3d( region, rv3d, co2d) viewloc = view3d_utils.region_2d_to_origin_3d(region, rv3d, co2d) away = (centerloc - viewloc).length pointloc = viewloc + view_vector * away # to place the helper on centerloc to be in the vicinity of projection plane helper.location = pointloc helper.hide = False NP020RM.qdef = copy.deepcopy(NP020RM.q) NP020RM.ndef = copy.deepcopy(NP020RM.n) NP020RM.plane = 'SET' NP020RM.flag = 'RUNTRANSSTART' np_print('05_BglPlane_lmb_FINISHED', ';', 'NP020RM.flag = ', NP020RM.flag, 'NP020RM.mode = ', NP020RM.mode, 'NP020RM.plane = ', NP020RM.plane) return {'FINISHED'} elif event.ctrl and event.value == 'PRESS': bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW') if mode == 'FREE': NP020RM.mode = 'X' elif mode == 'X': NP020RM.mode = 'Y' elif mode == 'Y': NP020RM.mode = 'Z' else: NP020RM.mode = 'FREE' np_print('05_BglPlane_rmb_FINISHED', ';', 'NP020RM.flag = ', NP020RM.flag, 'NP020RM.mode = ', NP020RM.mode, 'NP020RM.plane = ', NP020RM.plane) elif event.type == 'ESC': bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW') NP020RM.flag = 'EXIT' np_print('05_BglPlane_esc_FINISHED', ';', 'NP020RM.flag = ', NP020RM.flag) return {'FINISHED'} elif event.type in {'MIDDLEMOUSE', 'WHEELUPMOUSE', 'WHEELDOWNMOUSE'}: np_print('05_BglPlane_middle_wheel_any_PASS_THROUGH') return {'PASS_THROUGH'} np_print('05_BglPlane_standard_RUNNING_MODAL', ';', 'NP020RM.flag = ', NP020RM.flag) return {'RUNNING_MODAL'}
def __stroke_apply(self, context, _): sc = context.scene objs = common.get_uv_editable_objects(context) for obj in objs: world_mat = obj.matrix_world bm = bmesh.from_edit_mesh(obj.data) uv_layer = bm.loops.layers.uv.verify() mco = self.current_mco if sc.muv_uv_sculpt_tools == 'GRAB': for info in self.__loop_info[obj]: diff_uv = (mco - self.__initial_mco) * info["strength"] l = bm.faces[info["face_idx"]].loops[info["loop_idx"]] l[uv_layer].uv = info["initial_uv"] + diff_uv / 100.0 elif sc.muv_uv_sculpt_tools == 'PINCH': _, region, space = common.get_space('VIEW_3D', 'WINDOW', 'VIEW_3D') loop_info = [] for f in bm.faces: if not f.select: continue for i, l in enumerate(f.loops): loc_2d = location_3d_to_region_2d_extra( region, space.region_3d, compat.matmul(world_mat, l.vert.co)) diff = loc_2d - self.__initial_mco if diff.length < sc.muv_uv_sculpt_radius: info = { "face_idx": f.index, "loop_idx": i, "initial_vco": l.vert.co.copy(), "initial_vco_2d": loc_2d, "initial_uv": l[uv_layer].uv.copy(), "strength": _get_strength(diff.length, sc.muv_uv_sculpt_radius, sc.muv_uv_sculpt_strength) } loop_info.append(info) # mouse coordinate to UV coordinate ray_vec = view3d_utils.region_2d_to_vector_3d( region, space.region_3d, mco) ray_vec.normalize() ray_orig = view3d_utils.region_2d_to_origin_3d( region, space.region_3d, mco) ray_tgt = ray_orig + ray_vec * 1000000.0 mwi = world_mat.inverted() ray_orig_obj = compat.matmul(mwi, ray_orig) ray_tgt_obj = compat.matmul(mwi, ray_tgt) ray_dir_obj = ray_tgt_obj - ray_orig_obj ray_dir_obj.normalize() tree = BVHTree.FromBMesh(bm) loc, _, fidx, _ = tree.ray_cast(ray_orig_obj, ray_dir_obj) if not loc: return loops = [l for l in bm.faces[fidx].loops] uvs = [ Vector((l[uv_layer].uv.x, l[uv_layer].uv.y, 0.0)) for l in loops ] target_uv = barycentric_transform(loc, loops[0].vert.co, loops[1].vert.co, loops[2].vert.co, uvs[0], uvs[1], uvs[2]) target_uv = Vector((target_uv.x, target_uv.y)) # move to target UV coordinate for info in loop_info: l = bm.faces[info["face_idx"]].loops[info["loop_idx"]] if sc.muv_uv_sculpt_pinch_invert: diff_uv = \ (l[uv_layer].uv - target_uv) * info["strength"] else: diff_uv = \ (target_uv - l[uv_layer].uv) * info["strength"] l[uv_layer].uv = l[uv_layer].uv + diff_uv / 10.0 elif sc.muv_uv_sculpt_tools == 'RELAX': _, region, space = common.get_space('VIEW_3D', 'WINDOW', 'VIEW_3D') # get vertex and loop relation vert_db = {} for f in bm.faces: for l in f.loops: if l.vert in vert_db: vert_db[l.vert]["loops"].append(l) else: vert_db[l.vert] = {"loops": [l]} # get relaxation information for k in vert_db.keys(): d = vert_db[k] d["uv_sum"] = Vector((0.0, 0.0)) d["uv_count"] = 0 for l in d["loops"]: ln = l.link_loop_next lp = l.link_loop_prev d["uv_sum"] = d["uv_sum"] + ln[uv_layer].uv d["uv_sum"] = d["uv_sum"] + lp[uv_layer].uv d["uv_count"] = d["uv_count"] + 2 d["uv_p"] = d["uv_sum"] / d["uv_count"] d["uv_b"] = d["uv_p"] - d["loops"][0][uv_layer].uv for k in vert_db.keys(): d = vert_db[k] d["uv_sum_b"] = Vector((0.0, 0.0)) for l in d["loops"]: ln = l.link_loop_next lp = l.link_loop_prev dn = vert_db[ln.vert] dp = vert_db[lp.vert] d["uv_sum_b"] = d["uv_sum_b"] + dn["uv_b"] + dp["uv_b"] # apply for f in bm.faces: if not f.select: continue for i, l in enumerate(f.loops): loc_2d = location_3d_to_region_2d_extra( region, space.region_3d, compat.matmul(world_mat, l.vert.co)) diff = loc_2d - self.__initial_mco if diff.length >= sc.muv_uv_sculpt_radius: continue db = vert_db[l.vert] strength = _get_strength(diff.length, sc.muv_uv_sculpt_radius, sc.muv_uv_sculpt_strength) base = (1.0 - strength) * l[uv_layer].uv if sc.muv_uv_sculpt_relax_method == 'HC': t = 0.5 * \ (db["uv_b"] + db["uv_sum_b"] / d["uv_count"]) diff = strength * (db["uv_p"] - t) target_uv = base + diff elif sc.muv_uv_sculpt_relax_method == 'LAPLACIAN': diff = strength * db["uv_p"] target_uv = base + diff else: continue l[uv_layer].uv = target_uv bmesh.update_edit_mesh(obj.data)
def modal(self, context, event): context.area.tag_redraw() if event.type in {'MIDDLEMOUSE', 'WHELLUPMOUSE', 'WHEELDOWNMOUSE'}: #let the user pan and rotate around (translate?) return {'PASS_THROUGH'} if event.type == 'TIMER': #iterate....check convergence. if self.keep_iterating: self.iterate(context) return {'PASS_THROUGH'} elif event.type == 'MOUSEMOVE': #change the active bias region = context.region rv3d = context.space_data.region_3d coord = event.mouse_region_x, event.mouse_region_y vec = view3d_utils.region_2d_to_vector_3d(region, rv3d, coord) ray_origin = view3d_utils.region_2d_to_location_3d( region, rv3d, coord, vec) - 5000 * vec target = bpy.data.objects[self.target_name] #raycast onto active object ray_target = ray_origin + 5000 * vec [hit, normal, face] = odcutils.obj_ray_cast(target, target.matrix_world, ray_origin, ray_target) if hit: self.current_bias_point = target.matrix_world * hit ''' if not self.keep_iterating: self.bias_point = (event.mouse_region_x, event.mouse_region_y) self.keep_iterating = True ''' elif event.type == 'LEFTMOUSE' and event.value == "PRESS": #confirm the existing walking cache #if continue_iterating still true, append the last confirmed point to the bias points #change the active bias region = context.region rv3d = context.space_data.region_3d coord = event.mouse_region_x, event.mouse_region_y target = bpy.data.objects[self.target_name] vec = view3d_utils.region_2d_to_vector_3d(region, rv3d, coord) ray_origin = view3d_utils.region_2d_to_location_3d( region, rv3d, coord, vec) #raycast onto active object ray_target = ray_origin + 3000 * vec [hit, normal, face] = odcutils.obj_ray_cast(target, target.matrix_world, ray_origin, ray_target) if hit: self.bias_points.append(target.matrix_world * hit) #keep iterating to mouse projection on model self.keep_iterating = not self.keep_iterating return {'RUNNING_MODAL'} elif event.type == 'RIGHTMOUSE' and event.value == "PRESS": #pop off a bias point self.bias_points.pop() #pop off a bias normal #pop off stack of confirmed crevice points prior to that #keep iterating to mouse projection on model self.keep_iterating = not self.keep_iterating return {'RUNNING_MODAL'} elif event.type in {'ESC'}: context.window_manager.event_timer_remove(self._timer) bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW') return {'CANCELLED'} elif event.type in {'RETURN'}: context.window_manager.event_time_remove(self._timer) bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW') return {'CANCELLED'} return {'RUNNING_MODAL'}