def get_grid_intersection(mousepos): region = bpy.context.region region_data = bpy.context.region_data origin_3d = region_2d_to_origin_3d(region, region_data, mousepos) vector_3d = region_2d_to_vector_3d(region, region_data, mousepos) xdot = round(Vector((1, 0, 0)).dot(vector_3d), 2) ydot = round(Vector((0, 1, 0)).dot(vector_3d), 2) zdot = round(Vector((0, 0, 1)).dot(vector_3d), 2) # prefer alignment on z if abs(zdot * 2) >= max([abs(xdot), abs(ydot)]): angle = 0 if zdot <= 0 else 180 return intersect_line_plane(origin_3d, origin_3d + vector_3d, Vector((0, 0, 0)), Vector( (0, 0, 1))), Matrix.Rotation( radians(angle), 4, "X") elif abs(ydot) > abs(xdot): angle = 90 if ydot >= 0 else -90 return intersect_line_plane(origin_3d, origin_3d + vector_3d, Vector((0, 0, 0)), Vector( (0, 1, 0))), Matrix.Rotation( radians(angle), 4, "X") else: angle = 90 if xdot <= 0 else -90 return intersect_line_plane(origin_3d, origin_3d + vector_3d, Vector((0, 0, 0)), Vector( (1, 0, 0))), Matrix.Rotation( radians(angle), 4, "Y")
def find_distant_bmedge_crossing_plane(pt, no, edges, epsilon, e_ind_from, co_from): ''' returns the farthest edge that *crosses* plane and corresponding intersection point ''' if(len(edges)==3): # shortcut (no need to find farthest... just find first) for edge in edges: if edge.index == e_ind_from: continue v0,v1 = edge.verts co0,co1 = v0.co,v1.co s0,s1 = no.dot(co0 - pt), no.dot(co1 - pt) no_cross = not ((s0>epsilon and s1<-epsilon) or (s0<-epsilon and s1>epsilon)) if no_cross: continue i = intersect_line_plane(co0, co1, pt, no) return (edge,i) d_max,edge_max,i_max = -1.0,None,None for edge in edges: if edge.index == e_ind_from: continue v0,v1 = edge.verts co0,co1 = v0.co, v1.co s0,s1 = no.dot(co0 - pt), no.dot(co1 - pt) if s0 > epsilon and s1 > epsilon: continue if s0 < -epsilon and s1 < -epsilon: continue #if not ((s0>epsilon and s1<-epsilon) or (s0<-epsilon and s1>epsilon)): # edge cross plane? # continue i = intersect_line_plane(co0, co1, pt, no) d = (co_from - i).length if d > d_max: d_max,edge_max,i_max = d,edge,i return (edge_max,i_max)
def deform_cylinder(self, e: Edge): # Cylindracisations # for each edge, apply it's cylindrical transform [r, magnitude] e_id = e.p_id r = self.params[e_id, 0] mag = self.params[e_id, 1] if r == 0 or mag == 0: return mid = Vector(e.coord) mask = np.array(mid, dtype=bool) normal = Vector(np.invert(mask).astype(dtype=float)) plane = np.where(mask)[0] def _inrange(vertex, ctrl): vc = self.vertices[vertex] - ctrl if abs(vc[0]) <= r and abs(vc[1]) <= r and abs(vc[2]) <= r: return True return False vertices = [] for c in [e.ctp_n, e.ctp_p]: # filter quadrant normal to edge for quadrant in [[c.a_yz, c.a_xz, c.a_xy][i] for i in plane]: vertices.extend(quadrant.reshape((1, -1))) vertices = np.unique(vertices) c_p = (mid + normal) c_n = (mid - normal) for v in vertices: control = geometry.intersect_line_plane(c_p, c_n, self.vertices[v], normal) center = geometry.intersect_line_plane(normal, normal * -2, self.vertices[v], normal) center = center + (control - center) * (1 - r) if _inrange(v, control): self.vertices[v] = self.round_corner(center, self.vertices[v], r, mag) vertices = [] for c in [e.ctp_n, e.ctp_p]: # filter quadrant of planar curve for quadrant in [[c.a_yz, c.a_xz, c.a_xy][i] for i in np.where(normal)[0]]: vertices.extend(quadrant[1:, 1:].reshape((1, -1))) vertices = np.unique(vertices) for v in vertices: control = geometry.intersect_line_plane(c_p, c_n, self.vertices[v], normal) center = geometry.intersect_line_plane(normal, normal * -2, self.vertices[v], normal) center = center + (control - center) * (1 - r) if (control - self.vertices[v]).length < (control - center).length: # continue self.vertices[v] = self.vertices[v] + ( center - self.vertices[v]) * .3 * (center - self.vertices[v]).length for e in self.m_edges.reshape((1, -1))[0]: self.clean_dual_edge(e)
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 return None if the point is behind camera view """ is_perspective, orig, vec = self.region_2d_to_orig_and_vect( context, event) pt = intersect_line_plane(orig, orig + vec, origin, normal, False) # fix issue with parallel plane if pt is None: pt = intersect_line_plane(orig, orig + vec, origin, vec, False) if pt is None: return None if is_perspective: # Check if point is behind point of view (mouse over horizon) y = Vector((0, 0, 1)) x = vec.cross(y) x = y.cross(vec) itM = Matrix([[x.x, y.x, vec.x, orig.x], [x.y, y.y, vec.y, orig.y], [x.z, y.z, vec.z, orig.z], [0, 0, 0, 1]]).inverted() res = itM * pt if res.z < 0: return None return pt
def f_(me, list_0, arg, context, ob_act): cb = context.scene.pt_custom_props.b cen1 = context.scene.pt_custom_props.en1 dict_0 = {} dict_1 = {} if arg == 'x': pn = Vector((0, 1, 0)) pp = Vector((1, 0, 0)) elif arg == 'y': pn = Vector((1, 0, 0)) pp = Vector((0, 1, 0)) elif arg == 'z': pn = Vector((0, 0, 1)) pp = Vector((0, 1, 0)) if cb == False: for vi in list_0: v = (me.vertices[vi].co).copy() p = v + (pn * 0.1) if cen1 == 'opt0': p3 = intersect_line_plane(v, p, pp, pn) elif cen1 == 'opt1': p1 = ob_act.matrix_world * v gp = p1 + (pn * 0.1) p2 = intersect_line_plane(p1, gp, pp, pn) p3 = (ob_act.matrix_world).inverted() * p2 dict_0[vi] = p3 for j in dict_0: me.vertices[j].co = dict_0[j] elif cb == True: for vi in list_0: v = (me.vertices[vi].co).copy() p = v + (pn * 0.1) if cen1 == 'opt0': p3 = intersect_line_plane(v, p, pp, pn) elif cen1 == 'opt1': p1 = ob_act.matrix_world * v gp = p1 + (pn * 0.1) p2 = intersect_line_plane(p1, gp, pp, pn) p3 = (ob_act.matrix_world).inverted() * p2 me.vertices.add(1) me.vertices[-1].co = p3 me.vertices[-1].select = False dict_1[vi] = me.vertices[-1].index edge_copy_(me, dict_1) faces_copy_(me, dict_1)
def intersect_plane_plane_plane(p1c, p1n, p2c, p2n, p3c, p3n): point = None line = geometry.intersect_plane_plane(p1c, p1n, p2c, p2n) if line is not None and line[0] is not None: point = geometry.intersect_line_plane(line[0], line[0] + line[1], p3c, p3n) return point
def extend_vertex(): obj = bpy.context.edit_object me = obj.data bm = bmesh.from_edit_mesh(me) verts = bm.verts faces = bm.faces plane = [f for f in faces if f.select][0] plane_vert_indices = [v for v in plane.verts[:]] all_selected_vert_indices = [v for v in verts if v.select] M = set(plane_vert_indices) N = set(all_selected_vert_indices) O = N.difference(M) O = list(O) (v1_ref, v1_idx, v1), (v2_ref, v2_idx, v2) = [(i, i.index, i.co) for i in O] plane_co = plane.calc_center_median() plane_no = plane.normal new_co = intersect_line_plane(v1, v2, plane_co, plane_no, False) new_vertex = verts.new(new_co) A_len = (v1 - new_co).length B_len = (v2 - new_co).length vertex_reference = v1_ref if (A_len < B_len) else v2_ref bm.edges.new([vertex_reference, new_vertex]) bmesh.update_edit_mesh(me, True)
def project_point_onto_camera(self, point): return self.ifc_cutter.camera_obj.matrix_world.inverted( ) @ geometry.intersect_line_plane( point.xyz, point.xyz - Vector(self.ifc_cutter.section_box['projection']), self.ifc_cutter.camera_obj.location, Vector(self.ifc_cutter.section_box['projection']))
def get_ideal_position(empty_name): # Set Maximum Distance max_distance = 1000 # Get Empty Positions main_c = camera_position_array[empty_name + '.R'] sub_c = camera_position_array[empty_name + '.L'] # Vector to Target Point From Camera target_vector = (sub_c - camera_position ).normalized() * max_distance # Get Line A = get_mirrored_vector(camera_position, empty_position, normal) B = get_mirrored_vector( camera_position + (main_c - camera_position).normalized() * max_distance, empty_position, normal) # Calculate Intersection intersection = intersect_line_line( camera_position, target_vector, A, B) mirrored_intersection = get_mirrored_vector( Vector(intersection[1]), empty_position, normal) # Here, You have to get position on the screen insect = intersect_line_plane( camera_position, Vector(intersection[1]), Vector((0, -8 + distance, 0)), Vector((0, 1, 0))) # If it's not intersecting, just return ridiculous value if (insect is None): insect = Vector((100, 100, 100)) return mirrored_intersection, (insect - sub_c).length
def get_3d_for_2d(self, pos_2d, context): result = None scene = context.scene origin, direction = get_origin_and_direction(pos_2d, context) # Try to hit an object in the scene if self._hit is None: hit, self._hit, self._normal, face, hit_obj, *_ = scene.ray_cast( context.view_layer, origin, direction) if hit: self._snap_to_target = True result = self._hit.copy() # Object was hit before else: result = intersect_line_plane(origin, origin + direction, self._hit, self._normal) if result is not None: result += self._normal.normalized() * scene.snap_offset return result
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 get_mouse_3d_on_plane(self, event, context): origin, direction = self.get_origin_and_direction(event, context) # get the intersection point on infinite plane return intersect_line_plane(origin, origin + direction, self.hit, self.normal)
def execute(self, context): edit_mode_out() ob_act = context.active_object me = ob_act.data list_0 = [v.index for v in me.vertices if v.select] if len(va_buf.list_f) == 0: self.report({'INFO'}, 'No face stored in memory') edit_mode_in() return {'CANCELLED'} elif len(va_buf.list_f) != 0: if len(list_0) == 0: self.report({'INFO'}, 'No vertices selected') edit_mode_in() return {'CANCELLED'} elif len(list_0) != 0: f = me.faces[va_buf.list_f[0]] for i in list_0: v = (me.vertices[i].co).copy() p = v + ((f.normal).copy() * 0.1) pp = (me.vertices[f.vertices[0]].co).copy() pn = (f.normal).copy() me.vertices[i].co = intersect_line_plane(v, p, pp, pn) edit_mode_in() return {'FINISHED'}
def clean_dual_edge(self, e): """ Get a dual list from an edge find the 2 quadrants sharing it """ if self.sub > 2: for dual in [e.dual_0, e.dual_1]: quads = [] for c in [e.ctp_n, e.ctp_p]: for quadrant in [c.a_xy, c.a_xz, c.a_yz]: if dual[0] == quadrant[-1, 1]: quads.append(quadrant) break if dual[0] == quadrant[1, -1]: quads.append(quadrant.T) break for i in range(self.sub - 1): a = Vector(e.coord) # self.vertices[quads[0][-1][i]] b = self.vertices[quads[0][-2][i]] c = self.vertices[quads[1][-2][i]] mask = np.array(a, dtype=bool) n = np.invert(mask).astype(dtype=float) p = geometry.intersect_line_plane( c, b, a, Vector((n[0], n[1], n[2]))) self.vertices[quads[0][-1][i]] = Vector( (p[0] * mask[0], p[1] * mask[1], p[2] * mask[2]))
def mutual_cut(sli1, sli2, cut_spec={}): """Given two slices, add the appropriate cuts to them if they intersect""" if sli1.rot == sli2.rot: return # same orientation, so nothing to intersect cut_dir = sli1.cut_direction(sli2) verts_3d = [sli1.to_3d(pt) for pt in sli1.poly.exterior.coords] points = [] for (pta, ptb) in pairwise(verts_3d): intersect = intersect_line_plane(pta, ptb, sli2.co, sli2.normal()) # Is the intersecting point between the ends of the segment? if intersect and (pta - intersect).dot(ptb - intersect) <= 0: points.append(intersect) if len(points) > 2: print("More than one intersection between slices. Not yet supported!") # There should always be an even number of crossings assert len(points) % 2 == 0 z_factor = cut_spec.get('z_factor', 0.5) if len(points) >= 2: midpt = points[0].lerp(points[1], z_factor) sli1.add_cut(midpt, cut_dir, sli2.thickness) sli2.add_cut(midpt, -cut_dir, sli1.thickness)
def handle_raycast(self, event): if not self.mousepaint: return coord = event.mouse_region_x, event.mouse_region_y if coord == self.lastcoord: return self.lastcoord = coord view_vector = region_2d_to_vector_3d(bpy.context.region, bpy.context.region_data, coord) ray_origin = region_2d_to_origin_3d(bpy.context.region, bpy.context.region_data, coord) pos, rot, scl = self.root.matrix_world.decompose() z = self.cursor.pos.z # z persists offset = Vector((0, 0, z)) offset = rot * offset pos = pos + offset nml = rot * UP plane_pos = intersect_line_plane(ray_origin, ray_origin + view_vector, pos, nml) if not plane_pos: return # workaround for quad view? mat = self.root.matrix_world.inverted() plane_pos = mat * plane_pos round_vector(plane_pos) if plane_pos != self.lastpos: d = plane_pos - self.lastpos self.on_move(d)
def get_pos3d(self, context, normal=Vector((0, 0, 1))): """ convert mouse pos to 3d point over plane defined by origin and normal pt is in world space """ region = context.region rv3d = context.region_data view_vector_mouse = view3d_utils.region_2d_to_vector_3d(region, rv3d, self.mouse_pos) ray_origin_mouse = view3d_utils.region_2d_to_origin_3d(region, rv3d, self.mouse_pos) pt = intersect_line_plane(ray_origin_mouse, ray_origin_mouse + view_vector_mouse, self.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, self.origin, view_vector_mouse, False) return pt
def doPlane(self, context): mode = context.object.mode #for blender to update selected verts, faces bpy.ops.object.mode_set(mode='OBJECT') selectedFaces = [ f for f in bpy.context.object.data.polygons if f.select ] selectedVerts = [ v for v in bpy.context.object.data.vertices if v.select ] if len(selectedFaces) == 0: self.report({'ERROR'}, 'select one meshs at least.') else: norms = Vector() centers = Vector() for f in selectedFaces: norms += f.normal centers += f.center count = len(selectedFaces) norms_aver = norms / count center_aver = centers / count try: for v in selectedVerts: res = geometry.intersect_line_plane( v.co, v.co + norms_aver, center_aver, norms_aver) v.co = res except: self.report({'ERROR'}, 'sorry for unknown error, please retry.') bpy.ops.object.mode_set(mode=mode)
def raycast_grid(scene, context, up_vector, right_vector, plane_normal, ray_origin, ray_vector): """ Raycast to a plane on the scene cursor, and return the grid snapped position :param scene: :param context: :param up_vector: :param right_vector: :param plane_normal: :param ray_origin: :param ray_vector: :return: grid_position, x_vector, y_vector, plane_pos """ plane_pos = intersect_line_plane(ray_origin, ray_origin + ray_vector, scene.cursor_location, plane_normal) # Didn't hit the plane exit if plane_pos is None: return None, None, None, None world_pixels = scene.sprytile_data.world_pixels target_grid = 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 = get_grid_pos(plane_pos, scene.cursor_location, right_vector.copy(), up_vector.copy(), world_pixels, grid_x, grid_y) return grid_position, x_vector, y_vector, plane_pos
def extend_vertex(context): """Computes Edge Extension to Face. Args: context: Blender bpy.context instance. Returns: Nothing. """ obj = bpy.context.edit_object pg = context.scene.pdt_pg if all([bool(obj), obj.type == "MESH", obj.mode == "EDIT"]): object_data = obj.data bm = bmesh.from_edit_mesh(object_data) verts = bm.verts faces = bm.faces planes = [f for f in faces if f.select] if not len(planes) == 1: failure_message(context) return plane = planes[0] plane_vert_indices = plane.verts[:] all_selected_vert_indices = [v for v in verts if v.select] plane_verts = set(plane_vert_indices) all_verts = set(all_selected_vert_indices) diff_verts = all_verts.difference(plane_verts) diff_verts = list(diff_verts) if not len(diff_verts) == 2: failure_message(context) return (v1_ref, v1), (v2_ref, v2) = [(i, i.co) for i in diff_verts] plane_co = plane.calc_center_median() plane_no = plane.normal new_co = intersect_line_plane(v1, v2, plane_co, plane_no, False) if new_co: new_vertex = verts.new(new_co) a_len = (v1 - new_co).length b_len = (v2 - new_co).length vertex_reference = v1_ref if (a_len < b_len) else v2_ref bm.edges.new([vertex_reference, new_vertex]) bmesh.update_edit_mesh(object_data, loop_triangles=True) else: failure_message_on_plane(context) else: pg.error = f"{PDT_ERR_EDOB_MODE},{obj.mode})" context.window_manager.popup_menu(oops, title="Error", icon="ERROR") return
def project_point_onto_camera(self, point): return self.camera.matrix_world.inverted( ) @ geometry.intersect_line_plane( point.xyz, point.xyz - Vector(self.camera_projection), self.camera.location, Vector(self.camera_projection), )
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': if bversion() < '002.077.000': res, obj, omx, 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) mx = Matrix.Identity(4) 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() loc, no, face_ind = self.snap_ob.ray_cast(imx * ray_origin, imx * ray_target) 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 triangle_intersection(self, points: List[Point]): l = len(points) assert l == 3, 'triangle intersection on non triangle (%d)' % (l,) s0, s1, s2 = map(self.side, points) if abs(s0 + s1 + s2) == 3: return [] # all points on same side of plane p0, p1, p2 = map(Point, points) if s0 == 0 or s1 == 0 or s2 == 0: # at least one point on plane # handle if all points in plane if s0 == 0 and s1 == 0 and s2 == 0: return [(p0, p1), (p1, p2), (p2, p0)] # handle if two points in plane if s0 == 0 and s1 == 0: return [(p0, p1)] if s1 == 0 and s2 == 0: return [(p1, p2)] if s2 == 0 and s0 == 0: return [(p2, p0)] # one point on plane, two on same side if s0 == 0 and s1 == s2: return [(p0, p0)] if s1 == 0 and s2 == s0: return [(p1, p1)] if s2 == 0 and s0 == s1: return [(p2, p2)] # two points on one side, one point on the other p01 = intersect_line_plane(p0, p1, self.o, self.n) p12 = intersect_line_plane(p1, p2, self.o, self.n) p20 = intersect_line_plane(p2, p0, self.o, self.n) if s0 == 0: return [(p0, p12)] if s1 == 0: return [(p1, p20)] if s2 == 0: return [(p2, p01)] if s0 != s1 and s0 != s2 and p01 and p20: return [(p01, p20)] if s1 != s0 and s1 != s2 and p01 and p12: return [(p01, p12)] if s2 != s0 and s2 != s1 and p12 and p20: return [(p12, p20)] print('%s %s %s' % (str(p0), str(p1), str(p2))) print('%s %s %s' % (str(s0), str(s1), str(s2))) print('%s %s %s' % (str(p01), str(p12), str(p20))) assert False
def triangle_intersection(self, points: List[Point]): l = len(points) assert l == 3, 'triangle intersection on non triangle (%d)' % (l, ) s0, s1, s2 = map(self.side, points) if abs(s0 + s1 + s2) == 3: return [] # all points on same side of plane p0, p1, p2 = map(Point, points) if s0 == 0 or s1 == 0 or s2 == 0: # at least one point on plane # handle if all points in plane if s0 == 0 and s1 == 0 and s2 == 0: return [(p0, p1), (p1, p2), (p2, p0)] # handle if two points in plane if s0 == 0 and s1 == 0: return [(p0, p1)] if s1 == 0 and s2 == 0: return [(p1, p2)] if s2 == 0 and s0 == 0: return [(p2, p0)] # one point on plane, two on same side if s0 == 0 and s1 == s2: return [(p0, p0)] if s1 == 0 and s2 == s0: return [(p1, p1)] if s2 == 0 and s0 == s1: return [(p2, p2)] # two points on one side, one point on the other p01 = intersect_line_plane(p0, p1, self.o, self.n) p12 = intersect_line_plane(p1, p2, self.o, self.n) p20 = intersect_line_plane(p2, p0, self.o, self.n) if s0 == 0: return [(p0, p12)] if s1 == 0: return [(p1, p20)] if s2 == 0: return [(p2, p01)] if s0 != s1 and s0 != s2 and p01 and p20: return [(p01, p20)] if s1 != s0 and s1 != s2 and p01 and p12: return [(p01, p12)] if s2 != s0 and s2 != s1 and p12 and p20: return [(p12, p20)] print('%s %s %s' % (str(p0), str(p1), str(p2))) print('%s %s %s' % (str(s0), str(s1), str(s2))) print('%s %s %s' % (str(p01), str(p12), str(p20))) assert False
def execute(self, context): C = context D = bpy.data ob = C.active_object #if bpy.app.debug != True: # bpy.app.debug = True # if C.active_object.show_extra_indices != True: # C.active_object.show_extra_indices = True if ob.mode == 'OBJECT': me = C.object.data bm = bmesh.new() bm.from_mesh(me) else: obj = C.edit_object me = obj.data bm = bmesh.from_edit_mesh(me) bm.select_history.validate() if len(bm.select_history) < 3: self.report({'INFO'}, 'Pick three vertices first') return {'CANCELLED'} points3Index = [] points3 = [] _ordering = bm.select_history if self.ref_order == "first" else list( bm.select_history)[::-1] for i in _ordering: if len(points3) >= 3: break elif isinstance(i, bmesh.types.BMVert): points3.append(i.co) points3Index.append(i.index) print(points3Index) if len(points3) < 3: self.report({'INFO'}, 'At least three vertices are needed being selected') return {'CANCELLED'} points3Normal = normal(*points3) for v in bm.verts: if v.select and v.index not in points3Index: _move = True if self.filter_distance > 0.0: _move = abs( distance_point_to_plane( v.co, points3[0], points3Normal)) < self.filter_distance if _move == True: v.co = intersect_line_plane(v.co, v.co + points3Normal, points3[0], points3Normal) if ob.mode == 'OBJECT': bm.to_mesh(me) bm.free() else: bmesh.update_edit_mesh(me, True) return {'FINISHED'}
def follow_mouse_intersection(sensor): ray_p0 = sensor.raySource ray_p1 = sensor.rayTarget # print ("rays ", ray_p0, ray_p1) intersection = geometry.intersect_line_plane(ray_p0, ray_p1, G.plane.p, G.plane.n) if intersection: # print (intersection) G.stimLocation = [intersection.x, intersection.y, G.stim.worldPosition[2]] G.holeLocation = [intersection.x, intersection.y, G.hole.worldPosition[2]]
def calc_line_plane_intersection(start, end, plane_normal, plane_origin, fallback=None): isect_point = intersect_line_plane(start, end, plane_origin, plane_normal) if isect_point is not None: return utils.math.constrain_point_to_line_seg(start, isect_point, end) else: return fallback
def flip_edge(bm: BMesh, e: BMEdge, pivot_vert: BMVert) \ -> BMLoop: print("Flipping edge {}".format(e)) # TODO: If the angle is completely flat # we could do a simpler edge flipping # As we don't do the projection with the signpost datastructure # what we'll do is simulate an edge flip performing an intersection # and creating 4 faces op_vert_1: BMVert = get_opposed_vert(e.link_faces[0], e) op_vert_2: BMVert = get_opposed_vert(e.link_faces[1], e) start_v = [loop for loop in e.link_loops if loop.vert == pivot_vert][0] \ .link_loop_next.edge.other_vert(e.other_vert(pivot_vert)) # end_v = [loop for loop in e.link_loops if loop.vert == pivot_vert][0] \ # .link_loop_radial_next.link_loop_next.edge.other_vert(pivot_vert) plane_no = e.verts[0].co - e.verts[1].co p = intersect_line_plane(e.verts[0].co, e.verts[1].co, start_v.co, plane_no) print("New intersection point was {}".format(p)) for face in list(e.link_faces): bm.faces.remove(face) intersection_vert = bm.verts.new(p) create_face_with_ccw_normal(bm, intersection_vert, op_vert_1, e.verts[0]) create_face_with_ccw_normal(bm, intersection_vert, op_vert_1, e.verts[1]) create_face_with_ccw_normal(bm, intersection_vert, op_vert_2, e.verts[0]) create_face_with_ccw_normal(bm, intersection_vert, op_vert_2, e.verts[1]) intersection_vert.normal = (start_v.co - p).cross(pivot_vert.co - p) print("Removing edge {}".format(e)) bm.edges.remove(e) bm.verts.ensure_lookup_table() bm.verts.index_update() bm.edges.ensure_lookup_table() bm.edges.index_update() bm.faces.ensure_lookup_table() bm.faces.index_update() loop = [ lo for lo in intersection_vert.link_loops if lo.edge.other_vert(intersection_vert) == pivot_vert ][0] return loop
def GetMouseLocation(self, event, context): region = bpy.context.region rv3d = bpy.context.region_data coord = event.mouse_region_x, event.mouse_region_y 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) V_a = ray_origin_mouse + view_vector_mouse V_b = rv3d.view_rotation @ Vector((0.0, 0.0, -1.0)) pointLoc = intersect_line_plane(ray_origin_mouse, V_a, context.object.location, V_b) loc = (self.GeneralNormal @ pointLoc) * -1 return loc
def get_pos3d(self, context): """ convert mouse pos to 3d point over plane defined by origin and normal """ region = context.region rv3d = context.region_data rM = context.active_object.matrix_world.to_3x3() view_vector_mouse = view3d_utils.region_2d_to_vector_3d( region, rv3d, self.mouse_pos) ray_origin_mouse = view3d_utils.region_2d_to_origin_3d( region, rv3d, self.mouse_pos) pt = intersect_line_plane(ray_origin_mouse, ray_origin_mouse + view_vector_mouse, self.origin, rM * self.glprovider.normal, False) # attempt to fix issue with parallel plane if pt is None: pt = intersect_line_plane(ray_origin_mouse, ray_origin_mouse + view_vector_mouse, self.origin, view_vector_mouse, False) return pt
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) hit = False if self.snap_type == 'SCENE': mx = Matrix.Identity(4) #scene ray cast returns world coords imx = Matrix.Identity(4) no_mx = imx.to_3x3().transposed() res, loc, no, ind, obj, omx = context.scene.ray_cast( context.view_layer, ray_origin, view_vector) #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) no = view_direction 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) # 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: self.b_pts[self.selected].location = mx @ loc self.b_pts[self.selected].surface_normal = no_mx @ no self.update_ui()
def execute(self, context): bpy.ops.object.editmode_toggle() bm = bmesh.new() bm.from_mesh(context.active_object.data) # For easy access to verts, edges, and faces: bVerts = bm.verts bEdges = bm.edges bFaces = bm.faces fVerts = [] normal = None # Find the selected face. This will provide the plane to project onto: for f in bFaces: if f.select: for v in f.verts: fVerts.append(v) f.normal_update() normal = f.normal f.select = False break for e in bEdges: if e.select: v1 = e.verts[0] v2 = e.verts[1] if v1 in fVerts and v2 in fVerts: e.select = False continue intersection = intersect_line_plane(v1.co, v2.co, fVerts[0].co, normal) if intersection != None: d1 = distance_point_to_plane(v1.co, fVerts[0].co, normal) d2 = distance_point_to_plane(v2.co, fVerts[0].co, normal) # If they have different signs, then the edge crosses the plane: if abs(d1 + d2) < abs(d1 - d2): # Make the first vertice the positive vertice: if d1 < d2: v2, v1 = v1, v2 new = list(bmesh.utils.edge_split(e, v1, 0.5)) new[1].co = intersection e.select = False new[0].select = False if self.pos: bEdges.remove(new[0]) if self.neg: bEdges.remove(e) bm.to_mesh(context.active_object.data) bpy.ops.object.editmode_toggle() ## bpy.ops.mesh.remove_doubles() return {'FINISHED'}
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 region_2d_to_location_3d(region, rv3d, coord, depth_location): """ Return a 3d location from the region relative 2d coords, aligned with *depth_location*. :arg region: region of the 3D viewport, typically bpy.context.region. :type region: :class:`bpy.types.Region` :arg rv3d: 3D region data, typically bpy.context.space_data.region_3d. :type rv3d: :class:`bpy.types.RegionView3D` :arg coord: 2d coordinates relative to the region; (event.mouse_region_x, event.mouse_region_y) for example. :type coord: 2d vector :arg depth_location: the returned vectors depth is aligned with this since there is no defined depth with a 2d region input. :type depth_location: 3d vector :return: normalized 3d vector. :rtype: :class:`mathutils.Vector` """ from mathutils import Vector from mathutils.geometry import intersect_point_line persmat = rv3d.perspective_matrix.copy() viewinv = rv3d.view_matrix.inverted() coord_vec = region_2d_to_vector_3d(region, rv3d, coord) depth_location = Vector(depth_location) if rv3d.is_perspective: from mathutils.geometry import intersect_line_plane origin_start = viewinv.translation.copy() origin_end = origin_start + coord_vec view_vec = viewinv.col[2].copy() return intersect_line_plane( origin_start, origin_end, depth_location, view_vec, 1, ) else: dx = (2.0 * coord[0] / region.width) - 1.0 dy = (2.0 * coord[1] / region.height) - 1.0 persinv = persmat.inverted() viewinv = rv3d.view_matrix.inverted() origin_start = ((persinv.col[0].xyz * dx) + (persinv.col[1].xyz * dy) + viewinv.translation) origin_end = origin_start + coord_vec return intersect_point_line( depth_location, origin_start, origin_end, )[0]
def edge_intersection(self, points: List[Point]): s0, s1 = map(self.side, points) if abs(s0 + s1) == 2: return [] # points on same side p0, p1 = map(Point, points) if s0 == 0 and s1 == 0: return [(p0, p1)] if s0 == 0: return [(p0, p0)] if s1 == 0: return [(p1, p1)] p01 = Point(intersect_line_plane(p0, p1, self.o, self.n)) return [(p01, p01)]
def get_world_coords(coords): cont = logic.getCurrentController() ray = cont.sensors["MouseRay"] ray_start = ray.raySource ray_end = ray.rayTarget plane_origin = Vector((0, 0, 0)) plane_normal = Vector((0, 0, 1)) intersection = geometry.intersect_line_plane(ray_start, ray_end, plane_origin, plane_normal) if intersection: return intersection
def region_2d_to_location_3d(region, rv3d, coord, depth_location): """ Return a 3d location from the region relative 2d coords, aligned with *depth_location*. :arg region: region of the 3D viewport, typically bpy.context.region. :type region: :class:`bpy.types.Region` :arg rv3d: 3D region data, typically bpy.context.space_data.region_3d. :type rv3d: :class:`bpy.types.RegionView3D` :arg coord: 2d coordinates relative to the region; (event.mouse_region_x, event.mouse_region_y) for example. :type coord: 2d vector :arg depth_location: the returned vectors depth is aligned with this since there is no defined depth with a 2d region input. :type depth_location: 3d vector :return: normalized 3d vector. :rtype: :class:`mathutils.Vector` """ from mathutils import Vector from mathutils.geometry import intersect_point_line persmat = rv3d.perspective_matrix.copy() viewinv = rv3d.view_matrix.inverted() coord_vec = region_2d_to_vector_3d(region, rv3d, coord) depth_location = Vector(depth_location) if rv3d.is_perspective: from mathutils.geometry import intersect_line_plane origin_start = viewinv.translation.copy() origin_end = origin_start + coord_vec view_vec = viewinv.col[2].copy() return intersect_line_plane(origin_start, origin_end, depth_location, view_vec, 1, ) else: dx = (2.0 * coord[0] / region.width) - 1.0 dy = (2.0 * coord[1] / region.height) - 1.0 persinv = persmat.inverted() viewinv = rv3d.view_matrix.inverted() origin_start = ((persinv.col[0].xyz * dx) + (persinv.col[1].xyz * dy) + viewinv.translation) origin_end = origin_start + coord_vec return intersect_point_line(depth_location, origin_start, origin_end, )[0]
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 _intersect_edge_plane(self, edge, orientation, position, ix_points): """ Find the intersection between this edge and plane. Append the IntersectionPoint (if any) to ix_points. """ if (edge, None) in self._saved_results: return self._saved_results[(edge, None)] if (edge.t_vert.pos[orientation] > position and edge.b_vert.pos[orientation] > position): point = None elif (edge.t_vert.pos[orientation] < position and edge.b_vert.pos[orientation] < position): point = None else: vec_t_vert = Vector((edge.t_vert.pos)) vec_b_vert = Vector((edge.b_vert.pos)) plane_co = Vector(([0,0,0])) plane_co[orientation] = position plane_norm = Vector(([0,0,0])) plane_norm[orientation] = 1. point = intersect_line_plane(vec_t_vert, vec_b_vert, plane_co, plane_norm, False) # only intersect segment if not point: ix_point = None # print("Not found!") # print(" t_vert: " + str(edge.t_vert.pos)) # print(" b_vert: " + str(edge.b_vert.pos)) # print(" pl_pos: " + str(position)) else: # print("Found ixpoint! " + str(point)) # print(" t_vert: " + str(vec_t_vert)) # print(" b_vert: " + str(vec_b_vert)) # print(" pl_pos: " + str(position)) ix_point = IntersectionPoint(edge, None, point) ix_points.append(ix_point) self._saved_results[(edge, None)] = ix_point return ix_point
def f_1(me, list_0, list_1): dict_1 = {vi: [] for vi in list_1} for fi in list_0: f = me.faces[fi] for vi in list_1: v = (me.vertices[vi].co).copy() p = v + ((f.normal).copy() * 0.1) pp = (me.vertices[f.vertices[0]].co).copy() pn = (f.normal).copy() p1 = intersect_line_plane(v, p, pp, pn) d = (p1 - v).length p2 = p1 + ((f.normal).copy() * d) me.vertices.add(1) me.vertices[-1].co = p2 me.vertices[-1].select = False dict_1[vi].append(me.vertices[-1].index) n = len(list_0) edge_copy_1(me, dict_1, n) faces_copy_1(me, dict_1, n)
def f_3(me, list_f, list_0): dict_1 = {vi: [] for vi in list_0} for fi in list_f: f = me.faces[fi] pp = (me.vertices[f.vertices[0]].co).copy() pn = (f.normal).copy() for vi in list_0: v = (me.vertices[vi].co).copy() p = v + ((f.normal).copy() * 0.1) p1 = intersect_line_plane(v, p, pp, pn) me.vertices.add(1) me.vertices[-1].co = p1 me.vertices[-1].select = False dict_1[vi].append(me.vertices[-1].index) n = len(list_f) edge_copy_1(me, dict_1, n) faces_copy_1(me, dict_1, n)
def com_line_cross_test(com1, com2, pt, no, factor = 2): ''' test used to make sure a cut is reasoably between 2 other cuts higher factor requires better aligned cuts ''' v = intersect_line_plane(com1,com2,pt,no) if v: #if the distance between the intersection is less than #than 1/factor the distance between the current pair #than this pair is invalide because there is a loop #in between check = intersect_point_line(v,com1,com2) invalid_length = (com2 - com1).length/factor #length beyond which an intersection is invalid test_length = (v - pt).length #this makes sure the plane is between A and B #meaning the test plane is between the two COM's in_between = check[1] >= 0 and check[1] <= 1 if in_between and test_length < invalid_length: return True
def execute(self, context): bpy.ops.object.editmode_toggle() bm = bmesh.new() bm.from_mesh(context.active_object.data) bFaces = bm.faces bEdges = bm.edges bVerts = bm.verts fVerts = [] # Find the selected face. This will provide the plane to project onto: for f in bFaces: if f.select: for v in f.verts: fVerts.append(v) f.normal_update() normal = f.normal f.select = False break for e in bEdges: if e.select: v1 = e.verts[0] v2 = e.verts[1] if v1 in fVerts or v2 in fVerts: e.select = False continue intersection = intersect_line_plane(v1.co, v2.co, fVerts[0].co, normal) if intersection != None: print("Made it this far: intersection != None") # Use abs because we don't care what side of plane we're on: d1 = distance_point_to_plane(v1.co, fVerts[0].co, normal) d2 = distance_point_to_plane(v2.co, fVerts[0].co, normal) # If d1 is closer than we use v1 as our vertice: # "xor" with 'use_force': if (abs(d1) < abs(d2)) is not self.use_force: print("Made it this far: (abs(d1) < abs(d2)) xor force") if self.make_copy: v1 = bVerts.new() v1.co = e.verts[0].co if self.use_normal: vector = normal vector.length = abs(d1) v1.co = v1.co - (vector * sign(d1)) else: print("New coordinate", end = ": ") print(intersection) v1.co = intersection else: if self.make_copy: v2 = bVerts.new() v2.co = e.verts[1].co if self.use_normal: vector = normal vector.length = abs(d2) v2.co = v2.co - (vector * sign(d2)) else: v2.co = intersection e.select = False bm.to_mesh(context.active_object.data) bpy.ops.object.editmode_toggle() return {'FINISHED'}
face = f fedges = f.edges if not fedges: print('no face') else: for e in bm.edges: if e.select == True and not isEdgeOfFace(e, fedges): sedges.append(e) if not sedges: print('no edge') else: # <HEART> for sedge in sedges: iv = geometry.intersect_line_plane( sedge.verts[0].co, sedge.verts[1].co, face.verts[0].co, face.normal, False ) if iv == None: print('coplanar edge is selected') else: sedge.select = False bm.verts.new(iv).select = True face.select = False bmesh.update_edit_mesh(me) # vert mode highligts created verts bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type='VERT') # ------------------------------------------- # select a face plus some other mesh edges # run script, VERTICES PLACED where that plane crossed an edge # .. face edges considered to be planar
def draw_callback_px(self, context): if context.mode == "EDIT_MESH": en0 = context.scene.dt_custom_props.en0 font_id = 0 font_size = context.scene.dt_custom_props.fs ob_act = context.active_object bme = bmesh.from_edit_mesh(ob_act.data) mtrx = ob_act.matrix_world list_0 = [v.index for v in bme.verts if v.select] if len(list_0) != 0: p = bme.verts[list_0[0]].co.copy() p_loc_2d = location_3d_to_region_2d(context.region, context.space_data.region_3d, p) q = mtrx * bme.verts[list_0[0]].co.copy() q_loc_2d = location_3d_to_region_2d(context.region, context.space_data.region_3d, q) # -- -- -- -- distance to adjacent vertices if context.scene.dt_custom_props.b0 == True: list_ = [[v.index for v in e.verts] for e in bme.verts[list_0[0]].link_edges] for ek in list_: vi = [i for i in ek if i != list_0[0]][0] p1 = bme.verts[vi].co.copy() loc_0_3d = mtrx * ((p + p1) * 0.5) loc_0_2d = location_3d_to_region_2d(context.region, context.space_data.region_3d, loc_0_3d) bgl.glColor4f(1.0, 1.0, 0.0, context.scene.dt_custom_props.a) blf.position(font_id, loc_0_2d[0] + 4, loc_0_2d[1] + 4, 0) blf.size(font_id, font_size, context.user_preferences.system.dpi) blf.draw(font_id, str(round((p - p1).length, 4))) bgl.glLineStipple(4, 0xAAAA) bgl.glEnable(bgl.GL_LINE_STIPPLE) # -- -- -- -- distance to axis local global if context.scene.dt_custom_props.b1 == True: # -- -- -- -- local if en0 == 'opt0': # -- -- -- -- x axis px = mtrx * Vector((0.0, p[1], p[2])) px_loc_2d = location_3d_to_region_2d(context.region, context.space_data.region_3d, px) bgl.glEnable(bgl.GL_BLEND) bgl.glColor4f(1.0, 0.0, 0.0, context.scene.dt_custom_props.a) bgl.glBegin(bgl.GL_LINES) bgl.glVertex2f(q_loc_2d[0], q_loc_2d[1]) bgl.glVertex2f(px_loc_2d[0], px_loc_2d[1]) bgl.glEnd() bgl.glDisable(bgl.GL_BLEND) if context.scene.dt_custom_props.b2 == False: lx = (q_loc_2d + px_loc_2d) * 0.5 bgl.glColor4f(1.0, 0.0, 0.0, context.scene.dt_custom_props.a) blf.position(font_id, lx[0] + 4, lx[1] + 4, 0) blf.size(font_id, font_size, context.user_preferences.system.dpi) blf.draw(font_id, str(round(p[0], 4))) # -- -- -- -- y axis py = mtrx * Vector((p[0], 0.0, p[2])) py_loc_2d = location_3d_to_region_2d(context.region, context.space_data.region_3d, py) bgl.glEnable(bgl.GL_BLEND) bgl.glColor4f(0.0, 1.0, 0.0, context.scene.dt_custom_props.a) bgl.glBegin(bgl.GL_LINES) bgl.glVertex2f(q_loc_2d[0], q_loc_2d[1]) bgl.glVertex2f(py_loc_2d[0], py_loc_2d[1]) bgl.glEnd() bgl.glDisable(bgl.GL_BLEND) if context.scene.dt_custom_props.b2 == False: ly = (q_loc_2d + py_loc_2d) * 0.5 bgl.glColor4f(0.0, 1.0, 0.0, context.scene.dt_custom_props.a) blf.position(font_id, ly[0] + 4, ly[1] + 4, 0) blf.size(font_id, font_size, context.user_preferences.system.dpi) blf.draw(font_id, str(round(p[1], 4))) # -- -- -- -- z axis pz = mtrx * Vector((p[0], p[1], 0.0)) pz_loc_2d = location_3d_to_region_2d(context.region, context.space_data.region_3d, pz) bgl.glEnable(bgl.GL_BLEND) bgl.glColor4f(0.0, 0.0, 1.0, context.scene.dt_custom_props.a) bgl.glBegin(bgl.GL_LINES) bgl.glVertex2f(q_loc_2d[0], q_loc_2d[1]) bgl.glVertex2f(pz_loc_2d[0], pz_loc_2d[1]) bgl.glEnd() bgl.glDisable(bgl.GL_BLEND) if context.scene.dt_custom_props.b2 == False: lz = (q_loc_2d + pz_loc_2d) * 0.5 bgl.glColor4f(0.0, 0.0, 1.0, context.scene.dt_custom_props.a) blf.position(font_id, lz[0] + 4, lz[1] + 4, 0) blf.size(font_id, font_size, context.user_preferences.system.dpi) blf.draw(font_id, str(round(p[2], 4))) # -- -- -- -- if context.scene.dt_custom_props.b2 == True and context.scene.dt_custom_props.b1 == True: blf.size(font_id, font_size, context.user_preferences.system.dpi) bgl.glColor4f(1.0, 0.0, 0.0, context.scene.dt_custom_props.a) blf.position(font_id, q_loc_2d[0] + 4, q_loc_2d[1] + 4 + font_size + 4 + font_size + 4, 0) blf.draw(font_id, 'x ' + str(round(p[0], 4))) bgl.glColor4f(0.0, 1.0, 0.0, context.scene.dt_custom_props.a) blf.position(font_id, q_loc_2d[0] + 4, q_loc_2d[1] + 4 + font_size + 4, 0) blf.draw(font_id, 'y ' + str(round(p[1], 4))) bgl.glColor4f(0.0, 0.0, 1.0, context.scene.dt_custom_props.a) blf.position(font_id, q_loc_2d[0] + 4, q_loc_2d[1] + 4, 0) blf.draw(font_id, 'z ' + str(round(p[2], 4))) # -- -- -- -- global elif en0 == 'opt1': # -- -- -- -- x axis ip_x = intersect_line_plane(q, q + (Vector((1.0, 0.0, 0.0)) * 0.1), Vector((0.0, 1.0, 0.0)), Vector((1.0, 0.0, 0.0))) ip_x_loc_2d = location_3d_to_region_2d(context.region, context.space_data.region_3d, ip_x) bgl.glEnable(bgl.GL_BLEND) bgl.glColor4f(1.0, 0.0, 0.0, context.scene.dt_custom_props.a) bgl.glBegin(bgl.GL_LINES) bgl.glVertex2f(q_loc_2d[0], q_loc_2d[1]) bgl.glVertex2f(ip_x_loc_2d[0], ip_x_loc_2d[1]) bgl.glEnd() bgl.glDisable(bgl.GL_BLEND) if context.scene.dt_custom_props.b2 == False: loc_1_2d = (q_loc_2d + ip_x_loc_2d) * 0.5 bgl.glColor4f(1.0, 0.0, 0.0, context.scene.dt_custom_props.a) blf.position(font_id, loc_1_2d[0] + 4, loc_1_2d[1] + 4, 0) blf.size(font_id, font_size, context.user_preferences.system.dpi) blf.draw(font_id, str(round((q - ip_x).length, 4))) # -- -- -- -- y axis ip_y = intersect_line_plane(q, q + (Vector((0.0, 1.0, 0.0)) * 0.1), Vector((1.0, 0.0, 0.0)), Vector((0.0, 1.0, 0.0))) ip_y_loc_2d = location_3d_to_region_2d(context.region, context.space_data.region_3d, ip_y) bgl.glEnable(bgl.GL_BLEND) bgl.glColor4f(0.0, 1.0, 0.0, context.scene.dt_custom_props.a) bgl.glBegin(bgl.GL_LINES) bgl.glVertex2f(q_loc_2d[0], q_loc_2d[1]) bgl.glVertex2f(ip_y_loc_2d[0], ip_y_loc_2d[1]) bgl.glEnd() bgl.glDisable(bgl.GL_BLEND) if context.scene.dt_custom_props.b2 == False: loc_2_2d = (q_loc_2d + ip_y_loc_2d) * 0.5 bgl.glColor4f(0.0, 1.0, 0.0, context.scene.dt_custom_props.a) blf.position(font_id, loc_2_2d[0] + 4, loc_2_2d[1] + 4, 0) blf.size(font_id, font_size, context.user_preferences.system.dpi) blf.draw(font_id, str(round((q - ip_y).length, 4))) # -- -- -- -- z axis ip_z = intersect_line_plane(q, q + (Vector((0.0, 0.0, 1.0)) * 0.1), Vector((1.0, 0.0, 0.0)), Vector((0.0, 0.0, 1.0))) ip_z_loc_2d = location_3d_to_region_2d(context.region, context.space_data.region_3d, ip_z) bgl.glEnable(bgl.GL_BLEND) bgl.glColor4f(0.0, 0.0, 1.0, context.scene.dt_custom_props.a) bgl.glBegin(bgl.GL_LINES) bgl.glVertex2f(q_loc_2d[0], q_loc_2d[1]) bgl.glVertex2f(ip_z_loc_2d[0], ip_z_loc_2d[1]) bgl.glEnd() bgl.glDisable(bgl.GL_BLEND) if context.scene.dt_custom_props.b2 == False: loc_3_2d = (q_loc_2d + ip_z_loc_2d) * 0.5 bgl.glColor4f(0.0, 0.0, 1.0, context.scene.dt_custom_props.a) blf.position(font_id, loc_3_2d[0] + 4, loc_3_2d[1] + 4, 0) blf.size(font_id, font_size, context.user_preferences.system.dpi) blf.draw(font_id, str(round((q - ip_z).length, 4))) # -- -- -- -- if context.scene.dt_custom_props.b2 == True and context.scene.dt_custom_props.b1 == True: blf.size(font_id, font_size, context.user_preferences.system.dpi) bgl.glColor4f(1.0, 0.0, 0.0, context.scene.dt_custom_props.a) blf.position(font_id, q_loc_2d[0] + 4, q_loc_2d[1] + 4 + font_size + 4 + font_size + 4, 0) blf.draw(font_id, 'x ' + str(round((q - ip_x).length, 4))) bgl.glColor4f(0.0, 1.0, 0.0, context.scene.dt_custom_props.a) blf.position(font_id, q_loc_2d[0] + 4, q_loc_2d[1] + 4 + font_size + 4, 0) blf.draw(font_id, 'y ' + str(round((q - ip_y).length, 4))) bgl.glColor4f(0.0, 0.0, 1.0, context.scene.dt_custom_props.a) blf.position(font_id, q_loc_2d[0] + 4, q_loc_2d[1] + 4, 0) blf.draw(font_id, 'z ' + str(round((q - ip_z).length, 4))) # -- -- -- -- mouse location if context.scene.dt_custom_props.b4 == True: rgn = context.region # region rgn_3d = context.space_data.region_3d # region 3d bgl.glEnable(bgl.GL_BLEND) bgl.glColor4f(1.0, 1.0, 1.0, context.scene.dt_custom_props.a) bgl.glBegin(bgl.GL_LINES) bgl.glVertex2f(0, dt_buf.y ) bgl.glVertex2f(dt_buf.x - 15, dt_buf.y) bgl.glEnd() bgl.glDisable(bgl.GL_BLEND) bgl.glEnable(bgl.GL_BLEND) bgl.glColor4f(1.0, 1.0, 1.0, context.scene.dt_custom_props.a) bgl.glBegin(bgl.GL_LINES) bgl.glVertex2f(rgn.width, dt_buf.y ) bgl.glVertex2f(dt_buf.x + 15, dt_buf.y) bgl.glEnd() bgl.glDisable(bgl.GL_BLEND) bgl.glEnable(bgl.GL_BLEND) bgl.glColor4f(1.0, 1.0, 1.0, context.scene.dt_custom_props.a) bgl.glBegin(bgl.GL_LINES) bgl.glVertex2f(dt_buf.x, 0 ) bgl.glVertex2f(dt_buf.x, dt_buf.y - 15) bgl.glEnd() bgl.glDisable(bgl.GL_BLEND) bgl.glEnable(bgl.GL_BLEND) bgl.glColor4f(1.0, 1.0, 1.0, context.scene.dt_custom_props.a) bgl.glBegin(bgl.GL_LINES) bgl.glVertex2f(dt_buf.x, rgn.height ) bgl.glVertex2f(dt_buf.x, dt_buf.y + 15) bgl.glEnd() bgl.glDisable(bgl.GL_BLEND) bgl.glDisable(bgl.GL_LINE_STIPPLE) t = str(dt_buf.x) + ', ' + str(dt_buf.y) lo = region_2d_to_location_3d(context.region, context.space_data.region_3d, Vector((dt_buf.x, dt_buf.y)), Vector((0.0, 0.0, 0.0))) t1 = '( ' + str(round(lo[0], 4)) + ', ' + str(round(lo[1], 4)) + ', ' + str(round(lo[2], 4)) + ' )' bgl.glColor4f(1.0, 1.0, 1.0, context.scene.dt_custom_props.a) blf.position(font_id, dt_buf.x + 15, dt_buf.y + 15, 0) blf.size(font_id, 14, context.user_preferences.system.dpi) blf.draw(font_id, t1 if context.scene.dt_custom_props.b5 == True else t) bgl.glDisable(bgl.GL_LINE_STIPPLE) # -- -- -- -- angles if context.scene.dt_custom_props.b3 == True: list_ek = [[v.index for v in e.verts] for e in bme.verts[list_0[0]].link_edges] n1 = len(list_ek) for j in range(n1): vec1 = p - bme.verts[[ i for i in list_ek[j] if i != list_0[0] ][0]].co.copy() vec2 = p - bme.verts[[ i for i in list_ek[(j + 1) % n1] if i != list_0[0]][0]].co.copy() ang = vec1.angle(vec2) a_loc_2d = location_3d_to_region_2d(context.region, context.space_data.region_3d, mtrx * (((p - (vec1.normalized() * 0.1)) + (p - (vec2.normalized() * 0.1))) * 0.5)) bgl.glColor4f(0.0, 0.757, 1.0, context.scene.dt_custom_props.a) blf.position(font_id, a_loc_2d[0], a_loc_2d[1], 0) blf.size(font_id, font_size, context.user_preferences.system.dpi) blf.draw(font_id, str(round(ang, 4) if context.scene.dt_custom_props.b6 == True else round(degrees(ang), 2))) # -- -- -- -- tool on/off bgl.glColor4f(1.0, 1.0, 1.0, 1.0) blf.position(font_id, 150, 10, 0) blf.size(font_id, 20, context.user_preferences.system.dpi) blf.draw(font_id, 'Ruler On')
def cross_section(bme, mx, point, normal, debug = True): ''' Takes a mesh and associated world matrix of the object and returns a cross secion in local space. Args: mesh: Blender BMesh mx: World matrix (type Mathutils.Matrix) point: any point on the cut plane in world coords (type Mathutils.Vector) normal: plane normal direction (type Mathutisl.Vector) ''' times = [] times.append(time.time()) #bme = bmesh.new() #bme.from_mesh(me) #bme.normal_update() #if debug: #n = len(times) #times.append(time.time()) #print('succesfully created bmesh in %f sec' % (times[n]-times[n-1])) verts =[] eds = [] #convert point and normal into local coords #in the mesh into world space.This saves 2*(Nverts -1) matrix multiplications imx = mx.inverted() pt = imx * point no = imx.to_3x3() * normal #local normal edge_mapping = {} #perhaps we should use bmesh becaus it stores the great cycles..answer yup for ed in bme.edges: A = ed.verts[0].co B = ed.verts[1].co V = B - A proj = V.project(no).length #perp to normal = parallel to plane #only calc 2nd projection if necessary if proj == 0: #make sure not coplanar p_to_A = A - pt a_proj = p_to_A.project(no).length if a_proj == 0: edge_mapping[len(verts)] = ed.link_faces verts.append(1/2 * (A +B)) #put a midpoing since both are coplanar else: #this handles the one point on plane case v = intersect_line_plane(A,B,pt,no) if v: check = intersect_point_line(v,A,B) if check[1] >= 0 and check[1] <= 1: #the vert coord index = the face indices it came from edge_mapping[len(verts)] = [f.index for f in ed.link_faces] verts.append(v) if debug: n = len(times) times.append(time.time()) print('calced intersections %f sec' % (times[n]-times[n-1])) #iterate through smartly to create edge keys for i in range(0,len(verts)): a_faces = set(edge_mapping[i]) for m in range(i,len(verts)): if m != i: b_faces = set(edge_mapping[m]) if a_faces & b_faces: eds.append((i,m)) if debug: n = len(times) times.append(time.time()) print('calced connectivity %f sec' % (times[n]-times[n-1])) if len(verts): #new_me = bpy.data.meshes.new('Cross Section') #new_me.from_pydata(verts,eds,[]) #if debug: #n = len(times) #times.append(time.time()) #print('Total Time: %f sec' % (times[-1]-times[0])) return (verts, eds) else: return None
def intersect(self, v1, v2): """平面とv1-v2からなる直線の交点を求める""" return geom.intersect_line_plane(v1, v2, self.location, self.normal)
def cross_edge(A,B,pt,no): ''' wrapper of intersect_line_plane that limits intersection to within the line segment. args: A - Vector endpoint of line segment B - Vector enpoint of line segment pt - pt on plane to intersect no - normal of plane to intersect return: list [Intersection Type, Intersection Point, Intersection Point2] eg... ['CROSS',Vector((0,1,0)), None] eg... ['POINT',Vector((0,1,0)), None] eg....['COPLANAR', Vector((0,1,0)),Vector((0,2,0))] eg....[None,None,None] ''' ret_val = [None]*3 #list [intersect type, pt 1, pt 2] V = B - A #vect representation of the edge proj = V.project(no).length #perp to normal = parallel to plane #worst case is a coplanar issue where the whole face is coplanar..we will get there if proj == 0: #test coplanar #don't test both points. We have already tested once for paralellism #simply proving one out of two points is/isn't in the plane will #prove/disprove coplanar p_to_A = A - pt #truly, we could precalc all these projections to save time but use mem. #because in the multiple edges coplanar case, we wil be testing #their verts over and over again that share edges. So for a mesh with #a lot of n poles, precalcing the vert projections may save time! #Hint to future self, look at Nfaces vs Nedges vs Nverts #may prove to be a good predictor of which method to use. a_proj = p_to_A.project(no).length if a_proj == 0: print('special case co planar edge') ret_val = ['COPLANAR',A,B] else: #this handles the one point on plane case v = intersect_line_plane(A,B,pt,no) if v: check = intersect_point_line(v,A,B) if check[1] > 0 and check[1] < 1: #this is the purest cross...no co-points #the vert coord index = the face indices it came from ret_val = ['CROSS',v,None] elif check[1] == 0 or check[1] == 1: print('special case coplanar point') #now add all edges that have that point into the already checked list #this takes care of poles ret_val = ['POINT',v,None] return ret_val
def push_mesh_data(self,context, re_order = True): if len(self.cut_lines) < 2: print('waiting on other cut lines') return imx = self.original_form.matrix_world.inverted() total_verts = [] total_edges = [] total_faces = [] valid_cuts = [c_line for c_line in self.cut_lines if c_line.verts != [] and c_line.verts_simple != []] self.cut_lines = valid_cuts if len(valid_cuts) < 2: return #####order the cuts#### #first dictionary their current indices planes = [(cut.plane_pt, cut.plane_no) for cut in valid_cuts] valid_pairs = [] #test validity of all pairs #a pair is valid if the line segment corosses #none of the other planes reasonable close to #the plane origin. This will fail in hairpin turns #but that case should be relatively uncommon for i in range(0,len(valid_cuts)): for m in range(i,len(valid_cuts)): if m != i: pair = (i,m) valid_pairs.append(pair) A = valid_cuts[i].plane_pt B = valid_cuts[m].plane_pt C = .5 * (A + B) ray1 = A - valid_cuts[i].plane_tan.world_position ray2 = B - valid_cuts[m].plane_tan.world_position ray = ray1.lerp(ray2,.5).normalized() hit = self.original_form.ray_cast(imx * (C + 100 * ray), imx * (C - 100 * ray)) for j, plane in enumerate(planes): if j != i and j != m: pt = plane[0] no = plane[1] v = intersect_line_plane(A,B,pt,no) if v: check = intersect_point_line(v,A,B) pair_length = (B - A).length/2 inval_length = (v - pt).length if (check[1] >= 0 and check[1] <= 1 and inval_length < pair_length) or hit[2] == -1: print('invalid pair %s' % str(pair)) if pair in valid_pairs: valid_pairs.remove(pair) print(valid_pairs) if re_order and len(valid_pairs) > 0: #sort the pairs new_order = [] new_order.append(valid_pairs[-1][0]) new_order.append(valid_pairs[-1][1]) valid_pairs.pop() tests = 0 max_tests = len(valid_pairs) + 2 while len(valid_pairs) and tests < max_tests: tests += 1 for pair in valid_pairs: end = set(pair) & {new_order[-1]} beg = set(pair) & {new_order[0]} if end or beg: valid_pairs.remove(pair) if end: new_order.append(list(set(pair) - end)[0]) else: new_order.insert(0, list(set(pair)-beg)[0]) break print('the new order is') print(new_order) cuts_copy = valid_cuts.copy() valid_cuts = [] for i, n in enumerate(new_order): valid_cuts.append(cuts_copy[n]) del cuts_copy self.valid_cuts = valid_cuts #now n_rings = len(self.valid_cuts) n_lines = len(self.valid_cuts[0].verts_simple) #align verts for i in range(0,n_rings-1): vs_1 = self.valid_cuts[i].verts_simple vs_2 = self.valid_cuts[i+1].verts_simple es_1 = self.valid_cuts[i].eds_simple es_2 = self.valid_cuts[i+1].eds_simple self.valid_cuts[i+1].verts_simple = contour_utilities.align_edge_loops(vs_1, vs_2, es_1, es_2) #work out the connectivity edges for i, cut_line in enumerate(self.valid_cuts): for v in cut_line.verts_simple: total_verts.append(imx * v) for ed in cut_line.eds_simple: total_edges.append((ed[0]+i*n_lines,ed[1]+i*n_lines)) if i < n_rings - 1: #make connections between loops for j in range(0,n_lines): total_edges.append((i*n_lines + j, (i+1)*n_lines + j)) cyclic = 0 in valid_cuts[0].eds_simple[-1] #work out the connectivity faces: for j in range(0,len(valid_cuts) - 1): for i in range(0,n_lines-1): ind0 = j * n_lines + i ind1 = j * n_lines + (i + 1) ind2 = (j + 1) * n_lines + (i + 1) ind3 = (j + 1) * n_lines + i total_faces.append((ind0,ind1,ind2,ind3)) if cyclic: ind0 = (j + 1) * n_lines - 1 ind1 = j * n_lines + int(math.fmod((j+1)*n_lines, n_lines)) ind2 = ind0 + 1 ind3 = ind0 + n_lines total_faces.append((ind0,ind1,ind2,ind3)) print('part implemented') print(len(total_verts)) print(total_faces) self.follow_lines = [] for i in range(0,len(self.valid_cuts[0].verts_simple)): tmp_line = [] for cut_line in self.valid_cuts: tmp_line.append(cut_line.verts_simple[i]) self.follow_lines.append(tmp_line) self.verts = total_verts self.faces = total_faces self.edges = total_edges '''
def find_sorted_bmedges_crossing_plane(pt, no, edges, epsilon, e_ind_from, co_from): ''' pt - point on cutting plane: mathutils.Vector no - normal of cutting plane: mathutils.Vector edges - edges of BMeshFace epsilon - error for coplanar verts: Float e_ind_from - index of the previous bmesh edge the walker just crossed co_from - location where the cutting plane crosses the lats BMEdge (e_ind_from) #e_exclude - a dictionary of edges previulsy crossed. dictionary should return order in which edges were crossed. returns a list of bmedges that *cross* plane and corresponding intersection points If BMFace happens to be a convex NGon with > 2 crosses, the list will be sorted such that the 0th item is the only valid intersection which makes an edge in teh cross section with e_ind_from. ''' if(len(edges)<=4): # shortcut (no need to find multiple... just find first) for edge in edges: if edge.index == e_ind_from: continue v0,v1 = edge.verts co0,co1 = v0.co,v1.co s0,s1 = no.dot(co0 - pt), no.dot(co1 - pt) no_cross = not ((s0>epsilon and s1<-epsilon) or (s0<-epsilon and s1>epsilon)) if no_cross: continue i = intersect_line_plane(co0, co1, pt, no) return [(edge,i)] #http://stackoverflow.com/questions/6618515/sorting-list-based-on-values-from-another-list i_edges = [] intersects = [] ds = [] coords = {} #cache these to prevent twice per vert calcing for edge in edges: v0,v1 = edge.verts if v0 not in coords: coords[v0] = no.dot(v0.co-pt) if v1 not in coords: coords[v1] = no.dot(v1.co-pt) for edge in edges: #if edge.index == e_ind_from: continue #<--we do need the e_ind_from edge because it helps us sort #if edge.index in e_ind_exclude: continue #<-maybe don't need this because of the ordering :-) v0,v1 = edge.verts s0,s1 = coords[v0],coords[v1] if s0 > epsilon and s1 > epsilon: continue if s0 < -epsilon and s1 < -epsilon: continue #if not ((s0>epsilon and s1<-epsilon) or (s0<-epsilon and s1>epsilon)): # edge cross plane? # continue i = intersect_line_plane(v0.co, v1.co, pt, no) d = (i - co_from).length i_edges += [edge] intersects += [i] ds += [d] if len(i_edges) == 2: ed = [e for e in i_edges if e.index != e_ind_from][0] return [(ed, intersects[i_edges.index(ed)])] elif len(i_edges) > 2: #a concave ngon with 4,6,8.. crossings print('there are %i crossings' % len(i_edges)) #all the crossings are colinear if ngon is planar, so sorting them is easy edge_from = [e for e in i_edges if e.index == e_ind_from][0] min_i = intersects[i_edges.index(edge_from)] max_i = intersects[ds.index(max(ds))] direction = (max_i - min_i).normalized() signed_ds = [] for j, ed in enumerate(i_edges): signed_ds += [(intersects[j] - min_i).dot(direction)] #[x for (y,x) in sorted(zip(Y,X))] X sorted by Y sorted_edges = [ed for (d,ed) in sorted(zip(signed_ds,i_edges))] sorted_is = [i for (d,i) in sorted(zip(signed_ds, intersects))] n = sorted_edges.index(edge_from) if n % 2 == 0: print('odd crossings, this is the problem or a bad face') sorted_edges = list(reversed(list_shift(sorted_edges, n + 2))) sorted_is = list(reversed(list_shift(sorted_is, n + 2))) else: sorted_edges = list_shift(sorted_edges, n - 1) sorted_is =list_shift(sorted_is, n - 1) print('came from this edge ' + str(e_ind_from)) print('leaving to this edge' + str(sorted_edges[0].index)) return list(zip(sorted_edges, sorted_is)) else: print('no crossings perhaps') print([e.index for e in edges]) print(pt) print(no) return []
def find_bmedges_crossing_plane(pt, no, edges, epsilon): ''' pt - pt on cutting plane: mathutils.Vector no - normal of cutting plane: mathutils.Vector edges - edges of BMFace: bmesh.BMEdge epsilon - dist from plane < epsilon means coplanar returns a list of tupples (edge, intersection): [(BMEdges, mathutils.Vector)] if there are more than two (indicating a concave NGon) edges will be returned ordered across the ngon (in an arbitrary direction) such that only intersections (0,1), (2,3), (4,5) can make valid edges. this information is useful for special cases like E or pac-man shaped Ngons (dumb scenarios) see this image #TODO link also, for God's sake, the NGon better be planar ''' coords = {} for edge in edges: v0,v1 = edge.verts if v0 not in coords: coords[v0] = no.dot(v0.co-pt) if v1 not in coords: coords[v1] = no.dot(v1.co-pt) #print(str(coords)) i_edges = [] intersects = [] ds = [] signed_ds = [] for edge in edges: v0,v1 = edge.verts s0,s1 = coords[v0],coords[v1] if s0 > epsilon and s1 > epsilon: continue if s0 < -epsilon and s1 < -epsilon: continue #if not ((s0>epsilon and s1<-epsilon) or (s0<-epsilon and s1>epsilon)): # edge cross plane? # continue i = intersect_line_plane(v0.co, v1.co, pt, no) if not i: continue d = (i - pt).length if d == 0.00: d = epsilon i_edges += [edge] intersects += [i] ds += [d] if len(i_edges) > 2: #a concave ngon with 4,6,8.. crossings print('There are %i crossed edges' % len(i_edges)) print('There are %i total edges' % len(edges)) #all the crossings are colinear if ngon is planar, so sorting them is easy min_i = intersects[ds.index(min(ds))] min_ed = i_edges[ds.index(min(ds))] max_i = intersects[ds.index(max(ds))] direction = (max_i - min_i).normalized() signed_ds = [] for j, ed in enumerate(i_edges): signed_ds += [(intersects[j] - min_i).dot(direction)] print('signed_ds') print(signed_ds) #[x for (y,x) in sorted(zip(Y,X))] X sorted by Y sorted_edges = [ed for (d,ed) in sorted(zip(signed_ds,i_edges))] sorted_is = [i for (d,i) in sorted(zip(signed_ds, intersects))] n = sorted_edges.index(min_ed) if n % 2: print('shifting and reversing') sorted_edges = list(reversed(list_shift(sorted_edges, n + 1))) sorted_is = list(reversed(list_shift(sorted_is, n + 1))) elif len(i_edges) == 0: print('no edges crossing plane') sorted_edges = [] sorted_is = [] else: sorted_edges = i_edges sorted_is = intersects return list(zip(sorted_edges,sorted_is))
def line_intersection(self, p0:Point, p1:Point): return intersect_line_plane(p0, p1, self.o, self.n)
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) self.location = location[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 self.location = intersect_line_line(self.vert0, self.vert1, orig, end)[0] 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 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) end = orig + view_vector * 1000 if not outer_verts or self.out_obj == None: result, self.out_obj, self.out_mat, self.location, normal = context.scene.ray_cast(orig, end) self.out_mat_inv = self.out_mat.inverted() #print(self.location) if self.out_obj and self.out_obj != ignore_obj: self.type = 'FACE' if outer_verts: # get the ray relative to the self.out_obj ray_origin_obj = self.out_mat_inv * orig ray_target_obj = self.out_mat_inv * end 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 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: self.location = intersect_line_line(constrain[0], constrain[1], orig, end)[0] else: self.location = out_Location(rv3d, region, orig, view_vector) 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 C_Beam(self): mid = self.w / 2 web = self.tw / 2 # Bottom of the stringer: baseZ = -self.rise - self.hT - self.h # Top of the strigner: topZ = -self.rise - self.hT # Vertical taper amount: taper = self.tf * self.tp if self.dis or self.nS == 1: offset = (self.wT / (self.nS + 1)) - mid else: offset = 0 # taper < 100%: if self.tp > 0: for i in range(self.nS): coords = [] coords.append(Vector([0, offset, baseZ])) coords.append(Vector([0, offset, baseZ + taper])) coords.append(Vector([0, offset + (mid - web), baseZ + self.tf])) coords.append(Vector([0, offset + (mid - web), topZ - self.tf])) coords.append(Vector([0, offset, topZ - taper])) coords.append(Vector([0, offset, topZ])) coords.append(Vector([0, offset + (mid - web), topZ])) coords.append(Vector([0, offset + (mid + web), topZ])) coords.append(Vector([0, offset + self.w, topZ])) coords.append(Vector([0, offset + self.w, topZ - taper])) coords.append(Vector([0, offset + (mid + web), topZ - self.tf])) coords.append(Vector([0, offset + (mid + web), baseZ + self.tf])) coords.append(Vector([0, offset + self.w, baseZ + taper])) coords.append(Vector([0, offset + self.w, baseZ])) coords.append(Vector([0, offset + (mid + web), baseZ])) coords.append(Vector([0, offset + (mid - web), baseZ])) for j in range(16): coords.append(coords[j]+Vector([self.run * self.nT, 0, self.rise * self.nT])) # If the bottom meets the ground: # Bottom be flat with the xy plane, but shifted down. # Either project onto the plane along a vector (hard) or use the built in # interest found in mathutils.geometry (easy). Using intersect: if self.g: for j in range(16): coords[j] = intersect_line_plane(coords[j], coords[j + 16], Vector([0, 0, topZ]), Vector([0, 0, 1])) self.G.Make_mesh(coords, self.faces3a, 'stringer') if self.dis or self.nS == 1: offset += self.wT / (self.nS + 1) else: offset += (self.wT - self.w) / (self.nS - 1) # taper = 100%: else: for i in range(self.nS): coords = [] coords.append(Vector([0, offset, baseZ])) coords.append(Vector([0, offset + (mid - web), baseZ + self.tf])) coords.append(Vector([0, offset + (mid - web), topZ - self.tf])) coords.append(Vector([0, offset, topZ])) coords.append(Vector([0, offset + self.w, topZ])) coords.append(Vector([0, offset + (mid + web), topZ - self.tf])) coords.append(Vector([0, offset + (mid + web), baseZ + self.tf])) coords.append(Vector([0, offset + self.w, baseZ])) for j in range(8): coords.append(coords[j]+Vector([self.run * self.nT, 0, self.rise * self.nT])) self.G.Make_mesh(coords, self.faces3b, 'stringer') offset += self.wT / (self.nS + 1) return {'FINISHED'}