def out_Location(rv3d, orig, vector): view_matrix = rv3d.view_matrix v1 = (int(view_matrix[0][0]*1.5), int(view_matrix[0][1]*1.5), int(view_matrix[0][2]*1.5)) v2 = (int(view_matrix[1][0]*1.5), int(view_matrix[1][1]*1.5), int(view_matrix[1][2]*1.5)) hit = intersect_ray_tri((1,0,0), (0,1,0), (0,0,0), (vector), (orig), False) if hit is None: hit = intersect_ray_tri(v1, v2, (0,0,0), (vector), (orig), False) if hit is None: hit = intersect_ray_tri(v1, v2, (0,0,0), (-vector), (orig), False) if hit is None: hit = Vector() return hit
def out_Location(rv3d, region, orig, vector): view_matrix = rv3d.view_matrix v1 = Vector((int(view_matrix[0][0]*1.5),int(view_matrix[0][1]*1.5),int(view_matrix[0][2]*1.5))) v2 = Vector((int(view_matrix[1][0]*1.5),int(view_matrix[1][1]*1.5),int(view_matrix[1][2]*1.5))) hit = intersect_ray_tri(Vector((1,0,0)), Vector((0,1,0)), Vector((0,0,0)), (vector), (orig), False) if hit == None: hit = intersect_ray_tri(v1, v2, Vector((0,0,0)), (vector), (orig), False) if hit == None: hit = intersect_ray_tri(v1, v2, Vector((0,0,0)), (-vector), (orig), False) if hit == None: hit = Vector((0,0,0)) return hit
def get_click_point_info(x, y, ctx): cp = ClickPoint() view_orient, view_type = get_view_orientation(ctx) region = ctx.region region_data = ctx.space_data.region_3d if view_type in ['PERSP', 'CAMERA']: view_matrix = region_data.view_matrix.inverted() ray_start = view_matrix.to_translation() ray_depth = view_matrix @ Vector((0, 0, -100000)) #TODO from view ray_end = region_2d_to_location_3d(region, region_data, (x, y), ray_depth) p = get_triface_from_orient(view_orient) # cp.view = mathutils.geometry.intersect_ray_tri(p[0],p[1],p[2],ray_end,ray_start,False) cp.view = geometry.intersect_ray_tri(p[0], p[1], p[2], ray_end, ray_start, False) if cp.view == None: cp.view = Vector((0, 0, 0)) else: cp.view = region_2d_to_location_3d(region, region_data, (x, y), (0, 0, 0)) cp.screen = region_2d_to_location_3d(region, region_data, (x, y), (0, 0, 0)) cp.local = switch_axis_by_orient(view_orient, cp.view) cp.orient = Vector(get_rotation_from_orient(view_orient)) if view_type == 'ORTHO' and view_orient == 'USER': pass # TODO in orthografic user view not work correctly # r3d = ctx.area.spaces.active.region_3d # view_rot = r3d.view_matrix.to_euler() cp.view_name = view_orient return cp
def rayCastingObject(ob, ray_origin, ray_direction, culling=False): bm = bmesh.new() bm.from_object(ob, bpy.context.view_layer.depsgraph) bm.transform(ob.matrix_world) bmesh.ops.triangulate(bm, faces=bm.faces) ray_direction.normalize() points = {} for face in bm.faces: if (culling and ray_direction.dot(face.normal) > 0.0): continue intersection = geometry.intersect_ray_tri(face.verts[0].co, face.verts[1].co, face.verts[2].co, ray_direction, ray_origin) if (intersection): distance = (ray_origin - intersection).length_squared points[distance] = intersection if (points): min_distance = min(points) return min_distance, points[min_distance] else: return None, None
def face_intersect(face, pts): ray = Vector((0,0,1)) nrIntersections = np.zeros((np.shape(pts)[0],1), dtype=bool) for i in range(np.shape(pts)[0]): isect = intersect_ray_tri(face[0], face[1], face[2], ray, pts[i,:], 1) nrIntersections[i] = bool( isect and isect.z > pts[i, 2]) return np.array(nrIntersections, dtype=int)
def testTri3D(self, coords): inter = intersect_ray_tri(coords[0], coords[1], coords[2], self.ray_direction, self.ray_origin) if inter is None: return None return (inter - self.ray_origin).length
def calculateRay(laserAngle, mirrorAngle, laserMirrorDistance): reusable_ray.xyz = [0.0,-1.0,0.0] # A ray pointing down towards the mirror reusable_ray.rotate( Matrix.Rotation(laserAngle, 4, rotation_axis) ) [v1,v2,v3,n] = createMirror(mirrorAngle) incomingAngle = n.angle(reusable_ray)%(math.pi/2.0) reflectionAxis = n.cross(reusable_ray) # Calculate the axis around which the ray needs # to be rotated to created the reflection ray_to_mirror.xyz = [0.0,laserMirrorDistance,0.0] reflectionPoint = geometry.intersect_ray_tri(v1,v2,v3,reusable_ray,ray_to_mirror,False) reusable_ray.rotate( Matrix.Rotation(2*incomingAngle, 4,reflectionAxis) ) return [reusable_ray, reflectionPoint, math.pi/4-incomingAngle]
def isect_point_tri_persp_to_world(perspective_matrix, point, p1, p2, p3, clip=True): """ point: 透視投影座標。2d。3dの場合、zは無視される。 p1, p2, p3: 透視東映座標。3d pointから画面に垂直に伸びる直線との交差判定をworld座標で行う。 返り値はworld座標。 """ pmat = perspective_matrix pimat = pmat.inverted() orig = mul_persp(pimat, Vector((point[0], point[1], -1.0))) tag = mul_persp(pimat, Vector((point[0], point[1], 0.5))) ray = tag - orig v1 = mul_persp(pimat, p1) v2 = mul_persp(pimat, p2) v3 = mul_persp(pimat, p3) ivec = geo.intersect_ray_tri(v1, v2, v3, ray, orig, clip) return ivec
def _intersect_edge_face(self, edge, face, ix_points): """ Compute intersection for one edge and one face. Returns None if no intersection occured, and a IntersectionPoint if one is found. """ if (edge, face) in self._saved_results: return self._saved_results[(edge, face)] vec_t_vert = Vector((edge.t_vert.pos)) vec_b_vert = Vector((edge.b_vert.pos)) vec_dir = vec_t_vert - vec_b_vert point = intersect_ray_tri(Vector((face.verts[0].pos)), Vector((face.verts[1].pos)), Vector((face.verts[2].pos)), vec_dir, vec_b_vert, True) # restrict ix to tri face instead of plane # point = intersect_ray_tri(face.verts[0].get_blender_pos(), # face.verts[1].get_blender_pos(), # face.verts[2].get_blender_pos(), # edge.b_vert.get_blender_pos(), # dir_vector) if not point: ix_point = None elif (self._get_norm_squared(point - vec_b_vert) > self._get_norm_squared(vec_dir)): ix_point = None elif (point - vec_b_vert) * vec_dir < 0: ix_point = None else: # print ("Found ixpoint! " + str(point)) ix_point = IntersectionPoint(edge, face, point) ix_points.append(ix_point) self._saved_results[(edge, face)] = ix_point return ix_point
def get_intesection(ob, origin, ray_dir): if None not in (ob, origin, ray_dir) and ob.type == 'MESH': scene = bpy.context.scene bm = bmesh.new() bm.from_object(ob, scene) bm.transform(ob.matrix_world) bmesh.ops.triangulate(bm, faces=bm.faces) for face in bm.faces: intersection = intersect_ray_tri(face.verts[0].co, face.verts[1].co, face.verts[2].co, ray_dir, origin) if intersection is not None: draw_cross(intersection, "Intersection found:") # print("Intersection found:", intersection) # # drawing cross in place of intersection # inter = bpy.data.objects.new("Intersection", None) # inter.location = intersection # inter.empty_draw_size = 2 # scene.objects.link(inter) break # find the first intersection only return intersection
def execute(self, context): self.hcol = context.preferences.addons[ 'kekit'].preferences.modal_color_header self.tcol = context.preferences.addons[ 'kekit'].preferences.modal_color_text self.scol = context.preferences.addons[ 'kekit'].preferences.modal_color_subtext if self.ke_fitprim_option == "BOX": self.boxmode, self.world = True, False elif self.ke_fitprim_option == "CYL": self.boxmode, self.world = False, False elif self.ke_fitprim_option == "SPHERE": self.boxmode, self.world, self.sphere = False, False, True elif self.ke_fitprim_option == "QUADSPHERE": self.boxmode, self.world, self.sphere = False, False, True else: self.boxmode, self.world, self.sphere = False, False, False if bpy.context.scene.kekit.fitprim_item: self.itemize = True else: self.itemize = self.ke_fitprim_itemize self.modal = bpy.context.scene.kekit.fitprim_modal self.cyl_sides = bpy.context.scene.kekit.fitprim_sides self.select = bpy.context.scene.kekit.fitprim_select self.unit = round(bpy.context.scene.kekit.fitprim_unit, 4) self.sphere_ring = bpy.context.scene.kekit.fitprim_sphere_ring self.sphere_seg = bpy.context.scene.kekit.fitprim_sphere_seg self.quadsphere_seg = bpy.context.scene.kekit.fitprim_quadsphere_seg self.edit_mode = bpy.context.mode sel_verts = [] sel_verts2 = [] multi_object_mode = False cursor = context.scene.cursor self.og_cloc = cursor.location.copy() self.og_crot = cursor.rotation_euler.copy() og_orientation = str(context.scene.transform_orientation_slots[0].type) if self.itemize or self.edit_mode == "OBJECT" and context.object is not None: # make sure the new object is in the same layer as context object objc = context.object.users_collection[0] layer_collection = context.view_layer.layer_collection layer_coll = get_layer_collection(layer_collection, objc.name) alc = context.view_layer.active_layer_collection if objc.name != alc.name and alc.name != "Master Collection": context.view_layer.active_layer_collection = layer_coll elif objc.name != "Master Collection" and objc.name != alc.name: context.view_layer.active_layer_collection = layer_coll # ----------------------------------------------------------------------------------------- # MULTI OBJECT CHECK & SETUP # ----------------------------------------------------------------------------------------- if self.edit_mode == "EDIT_MESH": sel_mode = [b for b in bpy.context.tool_settings.mesh_select_mode] other_side = [] sel_obj = [ o for o in bpy.context.selected_objects if o.type == "MESH" ] if len(sel_obj) == 2: multi_object_mode = True obj = bpy.context.active_object obj_mtx = obj.matrix_world.copy() second_obj = [] bm = bmesh.from_edit_mesh(obj.data) bm.faces.ensure_lookup_table() bm.verts.ensure_lookup_table() sel_verts = [v for v in bm.verts if v.select] if sel_mode[2]: sel_poly = [p for p in bm.faces if p.select] active_face = bm.faces.active if active_face not in sel_poly: active_face = None if multi_object_mode: second_obj = [o for o in sel_obj if o != obj][0] bm2 = bmesh.from_edit_mesh(second_obj.data) obj_mtx2 = second_obj.matrix_world.copy() sel_poly2 = [p for p in bm2.faces if p.select] if sel_poly2: active_face2 = bm2.faces.active if active_face2 not in sel_poly2: active_face2 = None if active_face2: sel_verts2 = active_face2.verts else: sel_verts2 = sel_poly2[0].verts active_face2 = sel_poly2[0] # haxxfixxx else: sel_verts2 = [v for v in bm2.verts if v.select] if not sel_verts2: multi_object_mode = False elif sel_mode[0]: # Just for 2-vert mode in multiobj mode ole = sel_verts2[0].link_edges[:] other_side = get_shortest(obj_mtx2, ole) side = None distance = 0 vps = [] normal, setpos, setrot, center = None, None, None, None first_island = None island_mode = False # ----------------------------------------------------------------------------------------- # NO SELECTION MODE # ----------------------------------------------------------------------------------------- if not sel_verts or self.edit_mode == "OBJECT": # Check mouse over target if not self.edit_mode == "OBJECT": bpy.ops.object.mode_set(mode="OBJECT") hit_obj, hit_wloc, hit_normal, hit_face = mouse_raycast( context, self.mouse_pos, evaluated=True) if not self.edit_mode == "OBJECT": bpy.ops.object.mode_set(mode="EDIT") if self.edit_mode != "OBJECT" and hit_obj is not None: if len(hit_obj.modifiers) > 0: self.report({ "INFO" }, "FitPrim: Selected elements required in Edit Mode if Modifiers exist" ) return {"CANCELLED"} if hit_obj and hit_face is not None: # mouse over placement on face self.world = False mtx = hit_obj.matrix_world eks = hit_obj.data.polygons[hit_face].edge_keys vecs = [] for vp in eks: vc1 = mtx @ hit_obj.data.vertices[vp[0]].co vc2 = mtx @ hit_obj.data.vertices[vp[1]].co vecs.append(Vector(vc1 - vc2).normalized()) evps = [] for vp in eks: v1, v2 = hit_obj.data.vertices[ vp[0]], hit_obj.data.vertices[vp[1]] evps.append([v1, v2]) side, center, start_vec = get_sides(mtx, vecs, evps) if self.ke_fitprim_option == "PLANE" and self.edit_mode == "OBJECT": setpos = center # setpos = mtx @ hit_obj.data.polygons[hit_face].center side *= 2 else: offset = hit_normal * (side / 2) setpos = center + offset # setpos = mtx @ hit_obj.data.polygons[hit_face].center + offset setrot = rotation_from_vector(hit_normal, start_vec) else: # view placement compensation & Z0 drop view_vec = region_2d_to_vector_3d(context.region, context.space_data.region_3d, self.mouse_pos) view_pos = context.space_data.region_3d.view_matrix.inverted( ).translation if get_view_type() != "ORTHO": ground = ((0, 0, 0), (0, 1, 0), (1, 0, 0)) raypos = intersect_ray_tri(ground[0], ground[1], ground[2], view_vec, view_pos, False) snap_val = str(self.unit).split('.') if int(snap_val[0]) > 0: snap = 0 else: snap = len(snap_val[1]) setpos = Vector((round(raypos[0], snap), round(raypos[1], snap), self.unit / 2)) else: setpos = region_2d_to_location_3d( context.region, context.space_data.region_3d, self.mouse_pos, view_vec) self.world = True side = self.unit if self.ke_fitprim_option == "PLANE" and self.edit_mode == "OBJECT": side *= 2 distance = side sel_mode = (True, False, False) # ----------------------------------------------------------------------------------------- # VERT MODE(s) # ----------------------------------------------------------------------------------------- elif sel_mode[0]: if len(sel_verts) == 1 and not multi_object_mode: # 1-VERT MODE self.world = True side = get_shortest(obj_mtx, sel_verts[0].link_edges[:]) distance = side setpos = obj_mtx @ sel_verts[0].co elif len(sel_verts) == 2 and not multi_object_mode or \ multi_object_mode and len(sel_verts2) == 1 and len(sel_verts) == 1: # 2 Vert mode if multi_object_mode: p1 = obj_mtx @ sel_verts[0].co p2 = obj_mtx2 @ sel_verts2[0].co side = [other_side] con_edges = sel_verts[0].link_edges[:] side.append(get_shortest(obj_mtx, con_edges)) side = sorted(side)[0] else: p1 = obj_mtx @ sel_verts[0].co p2 = obj_mtx @ sel_verts[1].co con_edges = sel_verts[0].link_edges[:] + sel_verts[ 1].link_edges[:] side = get_shortest(obj_mtx, con_edges) v_1 = Vector(p1 - p2).normalized() v_2 = Vector((0, 0, 1)) if abs(v_1.dot(v_2)) == 1: v_2 = Vector((1, 0, 0)) n_v = v_1.cross(v_2).normalized() u_v = n_v.cross(v_1).normalized() t_v = u_v.cross(n_v).normalized() setrot = Matrix((u_v, n_v, t_v)).to_4x4().inverted() distance = get_distance(p1, p2) setpos = Vector(get_midpoint(p1, p2)) elif len(sel_verts) == 4 or multi_object_mode and len( sel_verts2) + len(sel_verts) <= 4: # 4-vert Rectangle mode # holy brute force batman if not second_obj: second_obj = obj tri = tri_order(((obj, sel_verts), (second_obj, sel_verts2))) q = tri[-1] # just placing a unit prim with min side in the center (for this one) so disregarding other vecs v1 = Vector(tri[0][0].matrix_world @ tri[0][1].co - tri[1][0].matrix_world @ tri[1][1].co) v2 = Vector(tri[0][0].matrix_world @ tri[0][1].co - tri[2][0].matrix_world @ tri[2][1].co) # v3 = Vector(q[0].matrix_world @ q[1].co - tri[1][0].matrix_world @ tri[1][1].co) # v4 = Vector(q[0].matrix_world @ q[1].co - tri[2][0].matrix_world @ tri[2][1].co) d1 = get_distance(tri[0][0].matrix_world @ tri[0][1].co, tri[1][0].matrix_world @ tri[1][1].co) # d2 = get_distance(tri[0][0].matrix_world @ tri[0][1].co, tri[2][0].matrix_world @ tri[2][1].co) # d3 = get_distance(q[0].matrix_world @ q[1].co, tri[1][0].matrix_world @ tri[1][1].co) d4 = get_distance(q[0].matrix_world @ q[1].co, tri[2][0].matrix_world @ tri[2][1].co) if d1 < d4: side = d1 else: side = d4 distance = side ap1 = average_vector([obj_mtx @ v.co for v in sel_verts]) if sel_verts2: ap2 = average_vector([obj_mtx2 @ v.co for v in sel_verts2]) setpos = average_vector((ap1, ap2)) else: setpos = ap1 n = v1.normalized().cross(v2.normalized()) u = n.cross(v1.normalized()) t = u.cross(n) setrot = Matrix((u, n, t)).to_4x4().inverted() else: self.report({ "INFO" }, "FitPrim: Invalid Vert Mode Selection: Select 1, 2 or 4 verts" ) return {"CANCELLED"} # ----------------------------------------------------------------------------------------- # EDGE MODE # ----------------------------------------------------------------------------------------- elif sel_mode[1]: one_line, loops, loops2 = False, [], [] sel_edges = [e for e in bm.edges if e.select] vps = [e.verts[:] for e in sel_edges] active_edge = bm.select_history.active if active_edge: active_edge_facenormals = [ correct_normal(obj_mtx, p.normal) for p in active_edge.link_faces ] active_edge_normal = Vector( average_vector(active_edge_facenormals)).normalized() # GET ISLANDS if multi_object_mode and active_edge: # print("multi obj mode") # todo: limited multiobj mode, rework one-for-all? sel_edges2 = [e for e in bm2.edges if e.select] vps2 = [e.verts for e in sel_edges2] p1 = get_loops(vps, legacy=True) p2 = get_loops(vps2, legacy=True) if p1 and p2: a1, a2 = obj_mtx @ p1[0][0].co, obj_mtx @ p1[0][-1].co b1, b2 = obj_mtx2 @ p2[0][0].co, obj_mtx2 @ p2[0][-1].co else: a1, a2 = obj_mtx @ sel_verts[0].co, obj_mtx @ sel_verts[ -1].co b1, b2 = obj_mtx2 @ sel_verts2[ 0].co, obj_mtx2 @ sel_verts2[-1].co b_avg = get_midpoint(b1, b2) spacing, mp = get_closest_midpoint(a1, a2, b_avg) u_v = Vector(a1 - b1).normalized() t_v = Vector(a1 - a2).normalized() n_v = u_v.cross(t_v).normalized() setrot = rotation_from_vector(n_v, t_v, rotate90=True) setpos = mp side = spacing distance = spacing elif active_edge: # same (active) obj island selections if len(sel_edges) > 1: if len(sel_edges) == 2 and not bool( set(sel_edges[0].verts).intersection( sel_edges[1].verts)): a1p, a2p = sel_edges[0].verts, sel_edges[1].verts a1, a2 = obj_mtx @ a1p[0].co, obj_mtx @ a1p[1].co b_avg = average_vector( [obj_mtx @ a2p[0].co, obj_mtx @ a2p[1].co]) lf1 = sel_edges[0].link_faces[:] lf = [ f for f in sel_edges[1].link_faces[:] if f in lf1 ] v1 = Vector((obj_mtx @ a1p[0].co - obj_mtx @ a1p[1].co)).normalized() v2 = Vector((obj_mtx @ a2p[0].co - obj_mtx @ a2p[1].co)).normalized() if not lf: u_v = Vector(a1 - (obj_mtx @ a2p[0].co)).normalized() t_v = Vector(a1 - a2).normalized() n_v = u_v.cross(t_v).normalized() setrot = rotation_from_vector(n_v, t_v, rotate90=True) else: n = correct_normal(obj_mtx, lf[0].normal) # t = average_vector((v1, v2)) # if sum(t) == 0: # print("AVG FAIL") t = v1 setrot = rotation_from_vector(n, t, rotate90=True) spacing, mp = get_closest_midpoint(a1, a2, b_avg) setpos = mp side = spacing distance = spacing else: loops = get_loops(vps, legacy=True) if len(loops) > 1: # print ("single obj - multi loop", len(loops)) # Check for closed loops a_ep1, a_ep2 = loops[0][0], loops[0][-1] b_ep1, b_ep2 = loops[1][0], loops[1][-1] if a_ep1 == a_ep2: a_ep2 = loops[0][-2] if b_ep1 == b_ep2: b_ep2 = loops[1][-2] # get coords & set vals a1, a2 = obj_mtx @ a_ep1.co, obj_mtx @ a_ep2.co b1, b2 = obj_mtx @ b_ep1.co, obj_mtx @ b_ep2.co b_avg = get_midpoint(b1, b2) spacing, mp = get_closest_midpoint(a1, a2, b_avg) u_v = Vector((0, 0, 1)) t_v = Vector(a1 - a2).normalized() if abs(u_v.dot(t_v)) == 1: u_v = Vector((1, 0, 0)) n_v = u_v.cross(t_v).normalized() setrot = rotation_from_vector(n_v, t_v, rotate90=False) setpos = mp side = spacing distance = spacing elif len(loops) == 1 or len(sel_edges) == 1: # print ("single obj - single loop") single_line = False if len(sel_edges) != 1: if loops[0][0] != loops[0][-1]: single_line = True if len(sel_edges) != 1 and not single_line: vecs = [ Vector((obj_mtx @ vp[0].co) - (obj_mtx @ vp[1].co)).normalized() for vp in vps ] normal = average_vector([ correct_normal(obj_mtx, v.normal) for v in flatten(vps) ]) side, center, start_vec = get_sides(obj_mtx, vecs, vps) distance = side setpos = center setrot = rotation_from_vector(normal, start_vec) elif len(sel_edges) == 1 or single_line: # print("1 edge --> one line", one_line) p1, p2 = obj_mtx @ sel_verts[ 0].co, obj_mtx @ sel_verts[1].co t_v = Vector(p1 - p2).normalized() n_v = active_edge_normal setrot = rotation_from_vector(n_v, t_v) distance = get_distance(p1, p2) setpos = get_midpoint(p1, p2) side = distance else: print("Unexpected: Aborting operation.") return {'CANCELLED'} # ----------------------------------------------------------------------------------------- # FACE MODE # ----------------------------------------------------------------------------------------- elif sel_mode[2] and active_face and sel_poly: fail_island = False # GET ISLANDS if multi_object_mode and active_face: first_island = sel_verts point = obj_mtx @ active_face.calc_center_median() firstcos = [obj_mtx @ v.co for v in active_face.verts] secondcos = [obj_mtx2 @ v.co for v in active_face2.verts] if firstcos and secondcos: island_mode = True distance = point_to_plane(point, firstcos, secondcos) if distance: distance = abs(distance) else: # print("Multi obj Point to plane failed for some reason - using single island mode.") # island_mode = False fail_island = True else: # same (active) obj island selections first_island, second_island = get_selection_islands( sel_poly, active_face) # Ofc, needs correct order for point to plane, and I'll just rely on face.verts for that: calc_island_1 = active_face.verts[:] calc_island_2 = [] for p in sel_poly: verts = p.verts[:] for v in verts: if v in second_island: calc_island_2 = verts break if len(first_island) != 0 and len(second_island) != 0: firstcos = [obj_mtx @ v.co for v in calc_island_1] secondcos = [obj_mtx @ v.co for v in calc_island_2] distance = point_to_plane( obj_mtx @ active_face.calc_center_median(), firstcos, secondcos) if distance: distance = abs(distance) island_mode = True else: # print("Point to plane failed for some reason - using single island mode.") fail_island = True # print(distance) else: # Ngon mode first_island = sel_verts # GETVALUES if island_mode or fail_island: bpy.ops.mesh.select_all(action='DESELECT') for v in first_island: bm.verts[v.index].select = True bmesh.update_edit_mesh(obj.data) bpy.ops.mesh.select_mode(type='VERT') bpy.ops.mesh.select_mode(type='FACE') bm.faces.ensure_lookup_table() faces = [f for f in bm.faces if f.select] normal = average_vector( [correct_normal(obj_mtx, f.normal) for f in faces]) bpy.ops.mesh.region_to_loop() sel_edges = [e for e in bm.edges if e.select] vps = [e.verts[:] for e in sel_edges] vecs = [ Vector((obj_mtx @ vp[0].co) - (obj_mtx @ vp[1].co)).normalized() for vp in vps ] side, center, start_vec = get_sides(obj_mtx, vecs, vps) setrot = rotation_from_vector(normal, start_vec) # ----------------------------------------------------------------------------------------- # PROCESS # ----------------------------------------------------------------------------------------- if side: if self.ke_fitprim_option == "PLANE" and self.edit_mode != "OBJECT": if sel_mode[2]: setpos = center elif len(sel_verts) == 0 or sel_mode[0] or sel_mode[1]: side *= .5 distance = distance / 2 if self.sphere and sel_mode[0]: if not len(sel_verts) == 0: side = distance elif sel_mode[2]: if island_mode: side *= .5 offset = normal * distance * .5 distance *= .5 if self.sphere: side = distance else: side *= .5 distance = side offset = normal * side setpos = center + offset if not island_mode and self.sphere: setpos = setpos - offset # SET FINAL ROTATION if self.world: setrot = (0, 0, 0) if self.ke_fitprim_option == "PLANE" and not sel_verts: setpos[2] = 0 else: setrot = setrot.to_euler() # RUN OP if not self.edit_mode == "OBJECT": bpy.ops.mesh.select_mode(type='FACE') if self.itemize: if self.edit_mode != "OBJECT": bpy.ops.object.mode_set(mode="OBJECT") bpy.ops.transform.select_orientation(orientation='GLOBAL') cursor.location = setpos cursor.rotation_euler = setrot # ----------------------------------------------------------------------------------------- # CUBE # ----------------------------------------------------------------------------------------- if self.boxmode: bpy.ops.mesh.primitive_box_add(width=side, depth=side, height=distance, align='WORLD', location=setpos, rotation=setrot) if self.itemize or self.edit_mode == "OBJECT": bpy.ops.object.shade_smooth() bpy.context.object.data.use_auto_smooth = True # ----------------------------------------------------------------------------------------- # PLANE # ----------------------------------------------------------------------------------------- elif self.ke_fitprim_option == "PLANE": # side *= 2 enter = True if self.edit_mode == 'OBJECT' or self.itemize: enter = False bpy.ops.mesh.primitive_plane_add(enter_editmode=enter, align='WORLD', location=setpos, rotation=setrot, size=side, scale=(1, 1, 1)) if self.itemize or self.edit_mode == "OBJECT": bpy.ops.object.shade_smooth() bpy.context.object.data.use_auto_smooth = True # ----------------------------------------------------------------------------------------- # SPHERE # ----------------------------------------------------------------------------------------- elif self.sphere and not self.ke_fitprim_option == "QUADSPHERE": bpy.ops.mesh.primitive_uv_sphere_add( segments=self.sphere_seg, ring_count=self.sphere_ring, radius=side, align='WORLD', location=setpos, rotation=setrot) if self.itemize or self.edit_mode == "OBJECT": bpy.ops.object.shade_smooth() bpy.context.object.data.use_auto_smooth = True # ----------------------------------------------------------------------------------------- # QUADSPHERE # ----------------------------------------------------------------------------------------- elif self.ke_fitprim_option == "QUADSPHERE": cutnr = self.quadsphere_seg # calc compensated subd radius from cube v1_pos = setpos[0] + side, setpos[1] + side, setpos[2] + side rad = sqrt(sum([(a - b)**2 for a, b in zip(setpos, v1_pos)])) diff = side / rad side = (side * diff) distance = side bpy.ops.mesh.primitive_box_add(width=side, depth=side, height=distance, align='WORLD', location=setpos, rotation=setrot, name="QuadSphere") if self.itemize or self.edit_mode == "OBJECT": bpy.ops.object.mode_set(mode="EDIT") bpy.ops.mesh.subdivide(number_cuts=cutnr, smoothness=1) bpy.ops.mesh.faces_shade_smooth() bpy.ops.object.mode_set(mode="OBJECT") else: bpy.ops.mesh.subdivide(number_cuts=cutnr, smoothness=1) bpy.ops.mesh.faces_shade_smooth() bpy.ops.ed.undo_push() # bpy.context.object.data.use_auto_smooth = as_check if self.itemize or self.edit_mode == "OBJECT": bpy.ops.object.shade_smooth() bpy.context.object.data.use_auto_smooth = True # ----------------------------------------------------------------------------------------- # CYLINDER # ----------------------------------------------------------------------------------------- else: bpy.ops.mesh.primitive_cylinder_add(vertices=self.cyl_sides, radius=side, depth=distance * 2, enter_editmode=False, align='WORLD', location=setpos, rotation=setrot) if self.modal: if self.itemize or self.edit_mode == "OBJECT": bpy.ops.object.mode_set(mode="EDIT") self.settings = (side, distance, setpos, setrot) context.window_manager.modal_handler_add(self) self._timer = context.window_manager.event_timer_add( 0.5, window=context.window) args = (self, context, self.screen_x) self._handle = bpy.types.SpaceView3D.draw_handler_add( draw_callback_px, args, 'WINDOW', 'POST_PIXEL') bpy.context.space_data.overlay.show_cursor = False return {'RUNNING_MODAL'} if multi_object_mode and not self.itemize: second_obj.select_set(state=True) obj.select_set(state=True) bpy.ops.object.editmode_toggle() bpy.ops.object.editmode_toggle() else: self.report({"INFO"}, "FitPrim: Invalid Selection / No Active Element?") if not self.select and not self.itemize and not self.edit_mode == "OBJECT": bpy.ops.mesh.select_all(action='DESELECT') if self.itemize: bpy.ops.transform.select_orientation(orientation=og_orientation) cursor.location = self.og_cloc cursor.rotation_euler = self.og_crot bpy.context.active_object.select_set(state=True) self.ke_fitprim_itemize = False return {"FINISHED"}
def snap_utilities(self, context, obj_matrix_world, bm_geom, bool_update, mcursor, outer_verts=False, constrain=None, previous_vert=None, ignore_obj=None, increment=0.0): rv3d = context.region_data region = context.region is_increment = False if not hasattr(self, 'snap_cache'): self.snap_cache = True self.type = 'OUT' self.bvert = None self.bedge = None self.bface = None self.hit = False self.out_obj = None if bool_update: #self.bvert = None self.bedge = None #self.bface = None if isinstance(bm_geom, bmesh.types.BMVert): self.type = 'VERT' if self.bvert != bm_geom: self.bvert = bm_geom self.vert = obj_matrix_world * self.bvert.co #self.Pvert = location_3d_to_region_2d(region, rv3d, self.vert) if constrain: #self.location = (self.vert-self.const).project(vector_constrain) + self.const location = intersect_point_line(self.vert, constrain[0], constrain[1]) #factor = location[1] self.location = location[0] else: self.location = self.vert elif isinstance(bm_geom, bmesh.types.BMEdge): if self.bedge != bm_geom: self.bedge = bm_geom self.vert0 = obj_matrix_world * self.bedge.verts[0].co self.vert1 = obj_matrix_world * self.bedge.verts[1].co self.po_cent = (self.vert0 + self.vert1) / 2 self.Pcent = location_3d_to_region_2d(region, rv3d, self.po_cent) self.Pvert0 = location_3d_to_region_2d(region, rv3d, self.vert0) self.Pvert1 = location_3d_to_region_2d(region, rv3d, self.vert1) if previous_vert and previous_vert not in self.bedge.verts: pvert_co = obj_matrix_world * previous_vert.co point_perpendicular = intersect_point_line( pvert_co, self.vert0, self.vert1) self.po_perp = point_perpendicular[0] #factor = point_perpendicular[1] self.Pperp = location_3d_to_region_2d(region, rv3d, self.po_perp) if constrain: location = intersect_line_line(constrain[0], constrain[1], self.vert0, self.vert1) if location == None: is_increment = True orig, view_vector = region_2d_to_orig_and_view_vector( region, rv3d, mcursor) end = orig + view_vector location = intersect_line_line(constrain[0], constrain[1], orig, end) if location: self.location = location[0] else: self.location = constrain[0] elif hasattr(self, 'Pperp') and abs(self.Pperp[0] - mcursor[0]) < 10 and abs( self.Pperp[1] - mcursor[1]) < 10: self.type = 'PERPENDICULAR' self.location = self.po_perp elif abs(self.Pcent[0] - mcursor[0]) < 10 and abs(self.Pcent[1] - mcursor[1]) < 10: self.type = 'CENTER' self.location = self.po_cent else: if increment and previous_vert in self.bedge.verts: is_increment = True self.type = 'EDGE' orig, view_vector = region_2d_to_orig_and_view_vector( region, rv3d, mcursor) end = orig + view_vector location = intersect_line_line(self.vert0, self.vert1, orig, end) if location: self.location = location[0] else: # Impossível, uma vez que não dá para selecionar essa edge. self.location = self.po_cent elif isinstance(bm_geom, bmesh.types.BMFace): is_increment = True self.type = 'FACE' if self.bface != bm_geom: self.bface = bm_geom self.face_center = obj_matrix_world * bm_geom.calc_center_median() self.face_normal = bm_geom.normal * obj_matrix_world.inverted() orig, view_vector = region_2d_to_orig_and_view_vector( region, rv3d, mcursor) end = orig + view_vector location = intersect_line_plane(orig, end, self.face_center, self.face_normal, False) if not location: l1 = self.bface.loops[0] l = l1.link_loop_next loc_orig = obj_matrix_world.inverted() * orig loc_ray = view_vector * obj_matrix_world while l != l1: location = intersect_ray_tri(l1.vert.co, l.vert.co, l.link_loop_next.vert.co, loc_ray, loc_orig, True) if location: location = obj_matrix_world * location print("intersect_ray_tri", location) break l = l.link_loop_next else: # Pelo Clip ser True as vezes location pode ser None e isso dá erro location = self.face_center if constrain: is_increment = False location = intersect_point_line(location, constrain[0], constrain[1])[0] self.location = location else: is_increment = True self.type = 'OUT' orig, view_vector = region_2d_to_orig_and_view_vector( region, rv3d, mcursor) if B_VERSION: result, self.location, normal, face_index, self.out_obj, self.out_mat = context.scene.ray_cast( orig, view_vector, 3.3e+38) if result and self.out_obj != ignore_obj: self.type = 'FACE' if outer_verts: if face_index != -1: try: verts = self.out_obj.data.polygons[ face_index].vertices v_dist = 100 for i in verts: v_co = self.out_mat * self.out_obj.data.vertices[ i].co v_2d = location_3d_to_region_2d( region, rv3d, v_co) dist = (Vector(mcursor) - v_2d).length_squared if dist < v_dist: is_increment = False self.type = 'VERT' v_dist = dist self.location = v_co except: print('Fail') if constrain: is_increment = False self.preloc = self.location self.location = intersect_point_line( self.preloc, constrain[0], constrain[1])[0] else: if constrain: location = intersect_line_line(constrain[0], constrain[1], orig, orig + view_vector) if location: self.location = location[0] else: self.location = constrain[0] else: self.location = out_Location(rv3d, region, orig, view_vector) else: ### VERSION 2.76 deprecated ### end = orig + view_vector * 1000 result, self.out_obj, self.out_mat, self.location, normal = context.scene.ray_cast( orig, end) if result and self.out_obj != ignore_obj: self.type = 'FACE' if outer_verts: # get the ray relative to the self.out_obj self.out_mat_inv = self.out_mat.inverted() ray_origin_obj = self.out_mat_inv * orig ray_target_obj = self.out_mat_inv * end try: location, normal, face_index = self.out_obj.ray_cast( ray_origin_obj, ray_target_obj) if face_index == -1: self.out_obj = None else: self.location = self.out_mat * location verts = self.out_obj.data.polygons[ face_index].vertices v_dist = 100 for i in verts: v_co = self.out_mat * self.out_obj.data.vertices[ i].co v_2d = location_3d_to_region_2d( region, rv3d, v_co) dist = (Vector(mcursor) - v_2d).length_squared if dist < v_dist: is_increment = False self.type = 'VERT' v_dist = dist self.location = v_co except Exception as e: print(e) if constrain: is_increment = False self.preloc = self.location self.location = intersect_point_line( self.preloc, constrain[0], constrain[1])[0] else: if constrain: location = intersect_line_line(constrain[0], constrain[1], orig, end) if location: self.location = location[0] else: self.location = constrain[0] else: self.location = out_Location(rv3d, region, orig, view_vector) ### END 2.76 VERSION ### if previous_vert: pvert_co = obj_matrix_world * previous_vert.co vec = self.location - pvert_co if is_increment and increment: pvert_co = obj_matrix_world * previous_vert.co vec = self.location - pvert_co self.len = round((1 / increment) * vec.length) * increment self.location = self.len * vec.normalized() + pvert_co else: self.len = vec.length
def perform_face_intersection(): ''' (currently) only points that are found on the face that is active, are accepted - In practice this means the last selected face will be used to receive intersection points. - step 2 (not implemented) will do the reverse... thus completing the loop. ''' def rays_from_face(face): ''' per edge (v1, v2) this returns the reverse edge too (v2, v1) in case the face intersection happens on the origin of the ray ''' indices = [v.index for v in face.verts] indices.append(indices[0]) # makes cyclic edges_f = [(indices[i], indices[i + 1]) for i in range(len(indices) - 1)] # forward edges_b = [(indices[i + 1], indices[i]) for i in range(len(indices) - 1)] # backward return edges_f + edges_b def triangulated(face): return tessellate([[v.co for v in face.verts]]) def get_selected_minus_active(bm_faces, active_idx): return [f for f in bm_faces if f.select and not (f.index == active_idx)] # Get the active mesh obj = bpy.context.edit_object me = obj.data bm = bmesh.from_edit_mesh(me) bm.verts.ensure_lookup_table() # get 2.73+ bm_verts = bm.verts bm_faces = bm.faces # Prime Face active = bm_faces.active # triangulate face to intersect tris_to_intersect = triangulated(active) # list of collections of 3 coordinates av = active.verts tris = [[av[idx1].co, av[idx2].co, av[idx3].co] for idx1, idx2, idx3 in tris_to_intersect] # this will hold the set of edges used to raycast, using set will avoid many duplicates of touching # faces that share edges. test_rays = set() # check intersection with bidirectional of all edges of all faces that are selected but not active # Non Prime Faces faces_to_intersect_with = get_selected_minus_active(bm_faces, active.index) for face in faces_to_intersect_with: rays = rays_from_face(face) for ray in rays: test_rays.add(ray) vert_set = set() for v1, v2, v3 in tris: for ray_idx, orig_idx in test_rays: orig = bm_verts[orig_idx].co ray_original = bm_verts[ray_idx].co ray = (bm_verts[ray_idx].co - orig).normalized() pt = intersect_ray_tri(v1, v2, v3, ray, orig) if pt: # filter new verts, # they must lie on the line described by (origin, ray_original) then add. itx_res = intersect_point_line(pt, ray_original, orig) if itx_res: v, dist = itx_res if (0.0 < dist < 1.0): vert_set.add(pt[:]) print('found {0} unique new verts'.format(len(vert_set))) for v in vert_set: bm.verts.new(v) bmesh.update_edit_mesh(me, True)
def intersect_ray_quad_3d(quad, origin, destination): ray = destination - origin p = intersect_ray_tri(quad[0], quad[1], quad[2], ray, origin) if p is None: p = intersect_ray_tri(quad[2], quad[3], quad[0], ray, origin) return p
def intersect_ray_quad_3d(quad, origin, destination): ray = destination - origin p = intersect_ray_tri(quad[0],quad[1],quad[2],ray,origin) if p is None: p = intersect_ray_tri(quad[2],quad[3],quad[0],ray,origin) return p
def execute(self, context): o = context.object M = o.matrix_world if not context.mode == 'EDIT_MESH': self.report({'ERROR'}, 'Only works in edit mode') bpy.ops.object.mode_set(mode='EDIT') return {'FINISHED'} bpy.ops.object.mode_set(mode='OBJECT') selectedEdges = [e for e in bpy.context.object.data.edges if e.select] if len(selectedEdges) < 2: self.report({'ERROR'}, 'Insufficient number of edges selected') bpy.ops.object.mode_set(mode='EDIT') return {'FINISHED'} if len(selectedEdges) == 2: # Get the line intersection of the two edges Pints = intersect_line_line( M * o.data.vertices[selectedEdges[0].vertices[0]].co, M * o.data.vertices[selectedEdges[0].vertices[1]].co, M * o.data.vertices[selectedEdges[1].vertices[0]].co, M * o.data.vertices[selectedEdges[1].vertices[1]].co) if Pints is None: self.report({'ERROR'}, 'Selected edges are PARALLEL') bpy.ops.object.mode_set(mode='EDIT') return {'FINISHED'} bpy.context.scene.cursor_location = (Pints[0] + Pints[1]) / 2.0 bpy.ops.object.mode_set(mode='EDIT') return {'FINISHED'} if len(selectedEdges) > 2: # Get the intersection of an edge and a face selectedEdges = selectedEdges[:3] # Find the edges that form a face (share a common vertex) commonEdges = [] for e in selectedEdges: for ee in selectedEdges: if e.index == ee.index: continue if (e.key[0] in ee.key) or (e.key[1] in ee.key): commonEdges = [e, ee] if len(commonEdges) == 0: self.report({ 'ERROR' }, 'No common edges of a TRIANGULAR FACE found. Please select TWO common edges of a face and another edge.' ) bpy.ops.object.mode_set(mode='EDIT') return {'FINISHED'} lonelyEdge = set(selectedEdges).difference(set(commonEdges)).pop() # Get the intersection of the lonelyEdge and the face (assuming it is triangular) rayOrigin = o.data.vertices[lonelyEdge.vertices[0]].co rayDirection = o.data.vertices[ lonelyEdge.vertices[1]].co - rayOrigin triVerts = [] for e in commonEdges: triVerts += [v for v in e.vertices] triVerts = list(set(triVerts)) if not len(triVerts) == 3: self.report( {'ERROR'}, 'Something is wrong with your triangle edge selection') bpy.ops.object.mode_set(mode='EDIT') return {'FINISHED'} Pint = intersect_ray_tri(M * o.data.vertices[triVerts[0]].co, M * o.data.vertices[triVerts[1]].co, M * o.data.vertices[triVerts[2]].co, rayDirection, rayOrigin) if Pint is None: self.report({ 'ERROR' }, 'Edge and triangle do not intersect. Try selecting another triangle.' ) bpy.ops.object.mode_set(mode='EDIT') return {'FINISHED'} bpy.context.scene.cursor_location = Pint bpy.ops.object.mode_set(mode='EDIT') return {'FINISHED'}
def perform_face_intersection(): ''' (currently) only points that are found on the face that is active, are accepted - In practice this means the last selected face will be used to receive intersection points. - step 2 (not implemented) will do the reverse... thus completing the loop. ''' def rays_from_face(face): ''' per edge (v1, v2) this returns the reverse edge too (v2, v1) in case the face intersection happens on the origin of the ray ''' indices = [v.index for v in face.verts] indices.append(indices[0]) # makes cyclic edges_f = [(indices[i], indices[i + 1]) for i in range(len(indices) - 1)] # forward edges_b = [(indices[i + 1], indices[i]) for i in range(len(indices) - 1)] # backward return edges_f + edges_b def triangulated(face): return tessellate([[v.co for v in face.verts]]) def get_selected_minus_active(bm_faces, active_idx): return [ f for f in bm_faces if f.select and not (f.index == active_idx) ] # Get the active mesh obj = bpy.context.edit_object me = obj.data bm = bmesh.from_edit_mesh(me) bm.verts.ensure_lookup_table() # get 2.73+ bm_verts = bm.verts bm_faces = bm.faces # Prime Face active = bm_faces.active # triangulate face to intersect tris_to_intersect = triangulated( active) # list of collections of 3 coordinates av = active.verts tris = [[av[idx1].co, av[idx2].co, av[idx3].co] for idx1, idx2, idx3 in tris_to_intersect] # this will hold the set of edges used to raycast, using set will avoid many duplicates of touching # faces that share edges. test_rays = set() # check intersection with bidirectional of all edges of all faces that are selected but not active # Non Prime Faces faces_to_intersect_with = get_selected_minus_active(bm_faces, active.index) for face in faces_to_intersect_with: rays = rays_from_face(face) for ray in rays: test_rays.add(ray) vert_set = set() for v1, v2, v3 in tris: for ray_idx, orig_idx in test_rays: orig = bm_verts[orig_idx].co ray_original = bm_verts[ray_idx].co ray = (bm_verts[ray_idx].co - orig).normalized() pt = intersect_ray_tri(v1, v2, v3, ray, orig) if pt: # filter new verts, # they must lie on the line described by (origin, ray_original) then add. itx_res = intersect_point_line(pt, ray_original, orig) if itx_res: v, dist = itx_res if (0.0 < dist < 1.0): vert_set.add(pt[:]) print('found {0} unique new verts'.format(len(vert_set))) for v in vert_set: bm.verts.new(v) bmesh.update_edit_mesh(me, True)