def camera_as_planes(scene, obj): """ Return planes in world-space which represent the camera view bounds. """ from mathutils.geometry import normal camera = obj.data # normalize to ignore camera scale matrix = obj.matrix_world.normalized() frame = [matrix * Vector(v) for v in camera.view_frame(scene)] origin = matrix.to_translation() planes = [] is_persp = (camera.type != 'ORTHO') for i in range(4): # find the 3rd point to define the planes direction if is_persp: frame_other = origin else: frame_other = frame[i] + matrix.col[2].xyz n = normal(frame_other, frame[i - 1], frame[i]) d = -n.dot(frame_other) planes.append((n, d)) if not is_persp: # add a 5th plane to ignore objects behind the view n = normal(frame[0], frame[1], frame[2]) d = -n.dot(origin) planes.append((n, d)) return planes
def normal(*vecs): # 3~4個のVectorのNormalを求める if len(vecs) == 3: return geom.normal(*vecs) elif len(vecs) == 4: n1 = geom.normal(vecs[0], vecs[1], vecs[3]) n2 = geom.normal(vecs[1], vecs[2], vecs[3]) if n1.dot(n2) < 0: n2.negate() return (n1 + n2).normalized()
def get_color_from_normal(dvk, pol, num_verts, vectorlight, colo): if num_verts <= 4: normal_no = normal(dvk[pol[0]], dvk[pol[1]], dvk[pol[2]]) else: normal_no = normal(dvk[pol[0]], dvk[pol[1]], dvk[pol[2]], dvk[pol[3]]) normal_no = (normal_no.angle(vectorlight, 0)) / pi r = (normal_no * colo[0]) - 0.1 g = (normal_no * colo[1]) - 0.1 b = (normal_no * colo[2]) - 0.1 return (r + 0.2, g + 0.2, b + 0.2)
def get_color_from_normal(dvk, pol, num_verts, vectorlight, colo): if num_verts <= 4: normal_no = normal(dvk[pol[0]], dvk[pol[1]], dvk[pol[2]]) else: normal_no = normal(dvk[pol[0]], dvk[pol[1]], dvk[pol[2]], dvk[pol[3]]) normal_no = (normal_no.angle(vectorlight, 0)) / pi r = (normal_no * colo[0]) - 0.1 g = (normal_no * colo[1]) - 0.1 b = (normal_no * colo[2]) - 0.1 return (r+0.2, g+0.2, b+0.2)
def generate_3PT_mode_1(pts, obj, nv): origin = obj.location mw = obj.matrix_world V = Vector nv = max(3, nv) # construction v1, v2, v3, v4 = V(pts[0]), V(pts[1]), V(pts[1]), V(pts[2]) edge1_mid = v1.lerp(v2, 0.5) edge2_mid = v3.lerp(v4, 0.5) axis = geometry.normal(v1, v2, v4) mat_rot = mathutils.Matrix.Rotation(math.radians(90.0), 4, axis) # triangle edges v1_ = ((v1 - edge1_mid) * mat_rot) + edge1_mid v2_ = ((v2 - edge1_mid) * mat_rot) + edge1_mid v3_ = ((v3 - edge2_mid) * mat_rot) + edge2_mid v4_ = ((v4 - edge2_mid) * mat_rot) + edge2_mid r = geometry.intersect_line_line(v1_, v2_, v3_, v4_) if r: p1, _ = r cp = mw * p1 bpy.context.scene.cursor_location = cp layer = get_layer() generate_gp3d_stroke(layer, p1, v1, axis, mw, origin, nv) else: print('not on a circle')
def write_triangle(f, tri): f.write("facet normal %f %f %f\n" % normal(*tri)[:]) f.write(" outer loop\n") for vert in tri: f.write(" vertex %f %f %f\n" % vert[:]) f.write(" endloop\n") f.write("endfacet\n")
def _binary_write(filepath, faces): with open(filepath, 'wb') as data: fw = data.write # header # we write padding at header beginning to avoid to # call len(list(faces)) which may be expensive fw(struct.calcsize('<80sI') * b'\0') # 3 vertex == 9f pack = struct.Struct('<9f').pack # number of vertices written nb = 0 for face in faces: # calculate face normal # write normal + vertexes + pad as attributes fw(struct.pack('<3f', *normal(*face)) + pack(*itertools.chain.from_iterable(face))) # attribute byte count (unused) fw(b'\0\0') nb += 1 # header, with correct value now data.seek(0) fw(struct.pack('<80sI', _header_version().encode('ascii'), nb))
def calc_vert_normal(self, vert, looptris, fallback=Vector((0, 0, 0))): normal = Vector() num = 0 for tri in looptris: normal += geom.normal(*[loop.vert.co for loop in tri]) num += 1 return normal / num
def __init__(self, v1, v2, v3): self.verts = [v1, v2, v3] self.normal = geom.normal(v1.co, v2.co, v3.co) self.edge_keys = [tuple(sorted((self.verts[i - 1], self.verts[i]), key=lambda v: v.index)) for i in range(3)] self.outer_verts = []
def generate_3PT(pts, obj, nv, mode=1): mw = obj.matrix_world V = Vector nv = max(3, nv) # construction v1, v2, v3, v4 = V(pts[0]), V(pts[1]), V(pts[1]), V(pts[2]) edge1_mid = v1.lerp(v2, 0.5) edge2_mid = v3.lerp(v4, 0.5) axis = geometry.normal(v1, v2, v4) mat_rot = mathutils.Matrix.Rotation(math.radians(90.0), 4, axis) # triangle edges v1_ = ((v1 - edge1_mid) * mat_rot) + edge1_mid v2_ = ((v2 - edge1_mid) * mat_rot) + edge1_mid v3_ = ((v3 - edge2_mid) * mat_rot) + edge2_mid v4_ = ((v4 - edge2_mid) * mat_rot) + edge2_mid r = geometry.intersect_line_line(v1_, v2_, v3_, v4_) if r: p1, _ = r cp = mw * p1 bpy.context.scene.cursor_location = cp if mode == 0: pass elif mode == 1: generate_bmesh_repr(p1, v1, axis, nv) else: print('not on a circle')
def generate_3PT_mode_1_(pts, obj): origin = obj.location transform_matrix = obj.matrix_local V = Vector # construction v1, v2, v3, v4 = V(pts[0]), V(pts[1]), V(pts[1]), V(pts[2]) edge1_mid = v1.lerp(v2, 0.5) edge2_mid = v3.lerp(v4, 0.5) axis = geometry.normal(v1, v2, v4) mat_rot = mathutils.Matrix.Rotation(math.radians(90.0), 4, axis) # triangle edges v1_ = ((v1 - edge1_mid) * mat_rot) + edge1_mid v2_ = ((v2 - edge1_mid) * mat_rot) + edge1_mid v3_ = ((v3 - edge2_mid) * mat_rot) + edge2_mid v4_ = ((v4 - edge2_mid) * mat_rot) + edge2_mid r = geometry.intersect_line_line(v1_, v2_, v3_, v4_) if r: p1, _ = r # cp = transform_matrix * (p1 + origin) cp = transform_matrix * p1 bpy.context.scene.cursor_location = cp # generate_gp3d_stroke(cp, axis, obj, radius=(p1-v1).length) else: print('not on a circle')
def calc_normals(self): """ Face normals """ normals = np.empty((len(self.faces), 3)) for idx, face in enumerate(self.faces): normals[idx] = normal(self.vertices[face]) self.faces.normals = normals
def getPolyNormal(poly): """ Returns the normal of poligon based on the position of their vertex. It calculates the normal, it doesn't return manually modified normals. :param poly: The poligon. :type poly: |KX_PolyProxy| """ mesh = poly.getMesh() s = poly.getNumVertex() v1 = mesh.getVertex(0, poly.v1) v2 = mesh.getVertex(0, poly.v2) v3 = mesh.getVertex(0, poly.v3) if s == 4: v4v = mesh.getVertex(0, poly.v4).XYZ else: v4v = None if v4v: normal = geometry.normal(v1.XYZ, v2.XYZ, v3.XYZ, v4v) else: normal = geometry.normal(v1.XYZ, v2.XYZ, v3.XYZ) return normal
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 createMirror(angle): mirror[0].xyz = [0,-1,-1] mirror[1].xyz = [-1,1,1] mirror[2].xyz = [1,1,1] mirror[0].rotate( Matrix.Rotation( angle, 4, norm_mirror) ) mirror[1].rotate( Matrix.Rotation( angle, 4, norm_mirror) ) mirror[2].rotate( Matrix.Rotation( angle, 4, norm_mirror) ) n = geometry.normal(mirror[2],mirror[1],mirror[0]) return [mirror[2],mirror[1],mirror[0],n]
def flatFace(verts): #print(" Flatface: original vertices %s %s %s"%(verts[0],verts[1],verts[2])) quatr = mg.normal(verts).rotation_difference(Vector((0, 0, 1))) eul = quatr.to_euler() eul.z = 0.0 #print(" Flatface called Euler is x=%2.2g deg y=%2.2g deg"%(degrees(eul.x),degrees(eul.y))) for v in verts: v.rotate(eul) v.z = 0
def execute(self, context): nor = normal(p.co for p in self.points[:3]) co = Vector(self.points[-1].co) + nor * self.offset bpy.ops.mesh.bisect(plane_co=co, plane_no=nor, use_fill=self.use_fill, clear_inner=self.clear_inner, clear_outer=self.clear_outer) return {'FINISHED'}
def execute(self, context): obj = context.edit_object me = obj.data bm = bmesh.from_edit_mesh(me) bm.select_history.validate() vertCount = 0 selectedVerts = [] for vert in (vert for vert in bm.verts if vert.select): #count loop verts vertCount+=1 if len(selectedVerts)<3: selectedVerts.append(vert) if vertCount < 1: self.report({'INFO'}, 'Select at least one vertex') return {'CANCELLED'} vertsMedianLoc = mathutils.Vector((0.0 ,0.0 ,0.0)) vertsMedianNorm = mathutils.Vector((0.0 ,0.0 ,0.0)) vertsCoordList = [] vectOffset = mathutils.Vector((0.0, 0.0, self.Depth)) offsetMatrix = mathutils.Matrix.Translation(vectOffset) yawMatrix = mathutils.Matrix.Rotation(radians(self.TrimRoll), 4, 'X') pitchMatrix = mathutils.Matrix.Rotation(radians(self.TripPitch), 4, 'Y') for vert in selectedVerts: vertsMedianLoc+=vert.co vertsCoordList.append(vert.co) vertsMedianNorm+=vert.normal vertsMedianLoc /= len(selectedVerts) if vertCount>2: trisNorm=normal(vertsCoordList) if degrees(vertsMedianNorm.angle(trisNorm))>90: vertsMedianNorm= trisNorm*(-1) else: vertsMedianNorm = trisNorm if vertCount==2: #fixes normal for 2 verts vectorV1_V2=selectedVerts[0].co-selectedVerts[1].co perpendicular= vectorV1_V2.cross(vertsMedianNorm) vertsMedianNorm = perpendicular.cross(vectorV1_V2) normToRotation = vertsMedianNorm.to_track_quat('Z', 'X').to_euler() selectionLocalMatrix = mathutils.Matrix.Translation(vertsMedianLoc) selectionLocalMatrix *= selectionLocalMatrix.Rotation(normToRotation[2], 4, 'Z') selectionLocalMatrix *= selectionLocalMatrix.Rotation(normToRotation[1], 4, 'Y') selectionLocalMatrix *= selectionLocalMatrix.Rotation(normToRotation[0], 4, 'X') spaceMatrixTransform = context.object.matrix_world*selectionLocalMatrix*offsetMatrix*yawMatrix*pitchMatrix loc = spaceMatrixTransform.to_translation() norm = spaceMatrixTransform.to_3x3()*mathutils.Vector((0.0, 0.0, 1.0)) bpy.ops.mesh.select_all(action='SELECT') bpy.ops.mesh.bisect(plane_co=loc, plane_no=norm, use_fill=True, clear_inner= not self.clearOut, clear_outer=self.clearOut) bpy.ops.mesh.select_all(action='DESELECT') bmesh.update_edit_mesh(me, True) return {"FINISHED"}
def compute_intersect_circle_circle(params, result, gates): '''Compute two circles intersection(s) Result has to be [[],[],[]] to host the solutions Gates are as follow: 0: Is there a intersection? 1: First Intersection 2: Second intersection 3: The working plane is defined by its normal (True) or by a third point (False)''' center_a, radius_a, center_b, radius_b, plane_pt, plane_normal = params local_result = [] sphere_loc_a = V(center_a) sphere_loc_b = V(center_b) if gates[3]: norm = V(plane_normal).normalized() else: v_in_plane = V(plane_pt) norm = normal([sphere_loc_a, sphere_loc_b, v_in_plane]) if norm.length == 0: if gates[3]: print("Circle Intersection Error: the Normal can't be (0,0,0)") else: print("Circle Intersection Error: the point in plane is aligned with origins") is_2d = norm.x == 0 and norm.y == 0 if is_2d and False: z_coord = sphere_loc_a.z inter_p = intersect_sphere_sphere_2d(sphere_loc_a.to_2d(), radius_a, sphere_loc_b.to_2d(), radius_b) if inter_p[0]: intersect = True vec1 = list(inter_p[1]) + [z_coord] vec0 = list(inter_p[0]) + [z_coord] local_result = [intersect, vec0, vec1] else: dist = (sphere_loc_a - sphere_loc_b).length new_a = V([0, 0, 0]) new_b = V([dist, 0, 0]) inter_p = intersect_sphere_sphere_2d(new_a.to_2d(), radius_a, new_b.to_2d(), radius_b) if inter_p[0]: intersect_num = True trird_point = sphere_loc_a + norm new_c = V([0, 0, 1]) vec0 = barycentric_transform(inter_p[0].to_3d(), new_a, new_b, new_c, sphere_loc_a, sphere_loc_b, trird_point) vec1 = barycentric_transform(inter_p[1].to_3d(), new_a, new_b, new_c, sphere_loc_a, sphere_loc_b, trird_point) local_result = [intersect_num, list(vec0), list(vec1)] if not local_result: direc = (sphere_loc_b - sphere_loc_a).normalized() intersect_num = False vec0 = sphere_loc_a + direc * radius_a vec1 = sphere_loc_b - direc * radius_b local_result = [intersect_num, list(vec0), list(vec1)] for i, res in enumerate(result): if gates[i]: res.append(local_result[i])
def createMirror(angle): mirror[0].xyz = [0, -1, -1] mirror[1].xyz = [-1, 1, 1] mirror[2].xyz = [1, 1, 1] mirror[0].rotate(Matrix.Rotation(angle, 4, norm_mirror)) mirror[1].rotate(Matrix.Rotation(angle, 4, norm_mirror)) mirror[2].rotate(Matrix.Rotation(angle, 4, norm_mirror)) n = geometry.normal(mirror[2], mirror[1], mirror[0]) return [mirror[2], mirror[1], mirror[0], n]
def __init__(self, v1, v2, v3): self.verts = [v1, v2, v3] self.normal = geom.normal(v1.co, v2.co, v3.co) self.edge_keys = [ tuple( sorted((self.verts[i - 1], self.verts[i]), key=lambda v: v.index)) for i in range(3) ] self.outer_verts = []
def get_medians_and_normals(oper, context, mode): """ because this is a post hoc implementation to cater for 2.8 ability to multi-select objects in edit mode, this is a verbose routine. """ medians = [] normals = [] extra_data = None if mode == "ONE": obj_main = bpy.context.edit_object me = obj_main.data bm = bmesh.from_edit_mesh(me) # get active face indices faces = [f for f in bm.faces if f.select] if oper.flip_u: faces = list(reversed(faces)) for f in faces: if len(medians) > 2: # dont select more than 2 faces. break normals.append(f.normal) medians.append(median(f)) bevel_depth = (medians[0] - (faces[0].verts[0].co)).length scale2 = (medians[1] - (faces[1].verts[0].co)).length op2_scale = scale2 / bevel_depth extra_data = bevel_depth, scale2, op2_scale elif mode == "TWO": obj_one = bpy.context.selected_objects[0] obj_two = bpy.context.selected_objects[1] first_coords = [] objs = [obj_two, obj_one] if oper.flip_u else [obj_one, obj_two] for obj in objs: m = obj.matrix_world bm = bmesh.from_edit_mesh(obj.data) # instead of transforming the entire bm using bmesh.ops.transform # we can multiply only the selected geometry. hopefully f = [f for f in bm.faces if f.select][0] first_coords.append(m @ f.verts[0].co) normals.append(normal([(m @ v.co) for v in f.verts])) medians.append(m @ median(f)) bevel_depth = (medians[0] - first_coords[0]).length scale2 = (medians[1] - first_coords[1]).length op2_scale = scale2 / bevel_depth extra_data = bevel_depth, scale2, op2_scale return medians, normals, extra_data
def execute(self, context): """ method called from ui """ # init local catt_export = context.scene.catt_export # check for non flat faces if self.arg == 'check_nonflat_faces': # discard if no object selected sel_objects = bpy.context.selected_objects if( len(sel_objects) == 0 ): self.report({'INFO'}, 'No object selected.') return {'CANCELLED'} # switch to edit mode bpy.ops.object.mode_set(mode = 'EDIT') # context = bpy.context # obj = context.edit_object mesh = bpy.context.edit_object.data # select None bpy.ops.mesh.select_all(action='DESELECT') bm = bmesh.from_edit_mesh(mesh) ngons = [f for f in bm.faces if len(f.verts) > 3] # init locals has_non_flat_faces = False planar_tolerance = 1e-6 # loop over faces for ngon in ngons: # define a plane from first 3 points co = ngon.verts[0].co norm = normal([v.co for v in ngon.verts[:3]]) # set face selected ngon.select = not all( [abs(distance_point_to_plane(v.co, co, norm)) < planar_tolerance for v in ngon.verts[3:]] ) # flag at least one non flat face detected if ngon.select: has_non_flat_faces = True # update mesh bmesh.update_edit_mesh(mesh) # disable edit mode if no non-flat face detected if not has_non_flat_faces: bpy.ops.object.mode_set(mode = 'OBJECT') self.report({'INFO'}, 'No non-flat face detected.') return {'FINISHED'}
def outline_normal(self, edge, face, center_to_edge=None): # outlineとedgeが平面ならcenter_to_edgeを返す vec1, vec2 = edge.vertices[0].co, edge.vertices[1].co if center_to_edge is None: # 外部でまだ計算していない場合 v1c = face.center - vec1 center_to_edge = v1c.project(vec2 - vec1) - v1c center_to_edge.normalize() # == normal # normal長を調整 cvs = [None, None] for i, v in enumerate(edge.vertices): for e in (e for e in v.edges if e != edge): if (e.vertices[0].co - e.vertices[1].co).length > MIN_NUMBER: if not self.usehidden and e.hide: if len(e.faces) == 1: cv = e.vert_another(v) cvs[i] = cv.co break if cvs[0] is None and cvs[1] is None: # 隣接無し。普通は有り得ない return center_to_edge elif cvs[0] and cvs[1]: # 2 if geo.area_tri(vec1, vec2, cvs[1]) >= MIN_NUMBER or \ geo.area_tri(vec2, cvs[1], cvs[0]) >= MIN_NUMBER: edge_normal = geo.normal(vec1, vec2, cvs[1], cvs[0]) else: return center_to_edge else: # 1 if cvs[0] and geo.area_tri(cvs[0], vec1, vec2): edge_normal = geo.normal(cvs[0], vec1, vec2) elif cvs[1] and geo.area_tri(vec1, vec2, cvs[1]): edge_normal = geo.normal(vec1, vec2, cvs[1]) else: return center_to_edge if edge_normal.dot(center_to_edge) < 0.0: edge_normal.negate() angle = center_to_edge.angle(edge_normal) if math.pi / 2 - angle >= self.threthold: # 平行でない center_to_edge /= math.cos(angle) return edge_normal
def _ascii_write(filepath, faces): with open(filepath, 'w') as data: fw = data.write header = _header_version() fw('solid %s\n' % header) for face in faces: # calculate face normal fw('facet normal %f %f %f\nouter loop\n' % normal(*face)[:]) for vert in face: fw('vertex %f %f %f\n' % vert[:]) fw('endloop\nendfacet\n') fw('endsolid %s\n' % header)
def build_mirror_pivot(f, verts, ob): v0 = mathutils.Vector(verts[f[0]]) v1 = mathutils.Vector(verts[f[1]]) v2 = mathutils.Vector(verts[f[2]]) if len(f) == 4: v3 = mathutils.Vector(verts[f[3]]) norm = normal(v0, v1, v2, v3) else: v3 = None norm = normal(v0, v1, v2) rot = norm.to_track_quat('X', 'Z') r = rot.to_euler('XYZ') p0 = v0.lerp(v1, 0.5) if v3 == None: p1 = v2 else: p1 = v2.lerp(v3, 0.5) center = p0.lerp(p1, 0.5) dummy = bpy.data.objects.new(ob.name + "_mirror_pivot", None) dummy.location = center.to_tuple() print("ROTATING TO ", r.x, " ", r.y, " ", r.z) dummy.rotation_euler = (r.x, r.y, r.z) bpy.context.scene.objects.link(dummy) return dummy
def compute_palm(source_armature, suffix): h = get_bone_position(source_armature, 'hand.' + suffix) r = get_bone_position(source_armature, 'f_ring.01.' + suffix) i = get_bone_position(source_armature, 'f_index.01.' + suffix) m = get_bone_position(source_armature, 'f_middle.01.' + suffix) if not h: return None, None if not r or not i or not m: hand = get_bone(source_armature, 'hand.' + suffix ) return (hand.head + hand.tail) / 2, hand.x_axis palm_co = (h + m) / 2 palm_no = -geometry.normal((h, i, r)) palm_no *= (h - m).length * 1 return palm_co, palm_no
def build_mirror_pivot(f,verts,ob): v0 = mathutils.Vector(verts[f[0]]) v1 = mathutils.Vector(verts[f[1]]) v2 = mathutils.Vector(verts[f[2]]) if len(f) == 4: v3 = mathutils.Vector(verts[f[3]]) norm = normal(v0,v1,v2,v3) else: v3 = None norm = normal(v0,v1,v2) rot = norm.to_track_quat('X','Z') r = rot.to_euler('XYZ') p0 = v0.lerp(v1,0.5) if v3 == None: p1 = v2 else: p1 = v2.lerp(v3,0.5) center = p0.lerp(p1,0.5) dummy = bpy.data.objects.new(ob.name + "_mirror_pivot",None) dummy.location = center.to_tuple() print("ROTATING TO ",r.x," ",r.y," ",r.z) dummy.rotation_euler = (r.x,r.y,r.z) bpy.context.scene.objects.link(dummy) return dummy
def generate_3PT_mode_1(pts=None, num_verts=20, make_edges=False): ''' Arc from start - throught - Eend - call this function only if you have 3 pts, - do your error checking before passing to it. ''' num_verts -= 1 verts, edges = [], [] V = Vector # construction v1, v2, v3, v4 = V(pts[0]), V(pts[1]), V(pts[1]), V(pts[2]) edge1_mid = v1.lerp(v2, 0.5) edge2_mid = v3.lerp(v4, 0.5) axis = geometry.normal(v1, v2, v4) mat_rot = mathutils.Matrix.Rotation(math.radians(90.0), 4, axis) # triangle edges v1_ = ((v1 - edge1_mid) * mat_rot) + edge1_mid v2_ = ((v2 - edge1_mid) * mat_rot) + edge1_mid v3_ = ((v3 - edge2_mid) * mat_rot) + edge2_mid v4_ = ((v4 - edge2_mid) * mat_rot) + edge2_mid r = geometry.intersect_line_line(v1_, v2_, v3_, v4_) if r: # do arc p1, _ = r # find arc angle. a = (v1 - p1).angle((v4 - p1), 0) s = (2 * math.pi) - a interior_angle = (v1 - v2).angle(v4 - v3, 0) if interior_angle > 0.5 * math.pi: s = math.pi + 2 * (0.5 * math.pi - interior_angle) for i in range(num_verts + 1): mat_rot = mathutils.Matrix.Rotation(((s / num_verts) * i), 4, axis) vec = ((v4 - p1) * mat_rot) + p1 verts.append(vec[:]) else: # do straight line step_size = 1 / num_verts verts = [v1_.lerp(v4_, i * step_size)[:] for i in range(num_verts + 1)] if make_edges: edges = [(n, n + 1) for n in range(len(verts) - 1)] return verts, edges
def compute_distances_mu(plane, pts, result, gates, tolerance): plane_origin = V(plane[0]) plane_a, plane_b = V(plane[1]), V(plane[2]) norm = normal([plane_origin, plane_a, plane_b]) if norm.length == 0: print("Error: the three points of the plane are aligned. Not valid plane") local_result = [[] for res in result] for p in pts: data = compute_point_tri_dist(V(p), plane_origin, plane_a, plane_b, norm, tolerance) for i, r in enumerate(local_result): r.append(data[i]) for i, res in enumerate(result): if gates[i]: res.append(local_result[i])
def generate_3PT_mode_1(pts, obj, nv): origin = obj.location mw = obj.matrix_world V = Vector nv = max(3, nv) # construction v1, v2, v3, v4 = V(pts[0]), V(pts[1]), V(pts[1]), V(pts[2]) edge1_mid = v1.lerp(v2, 0.5) edge2_mid = v3.lerp(v4, 0.5) axis = geometry.normal(v1, v2, v4) mat_rot = mathutils.Matrix.Rotation(math.radians(90.0), 4, axis) # triangle edges v1_ = ((v1 - edge1_mid) * mat_rot) + edge1_mid v2_ = ((v2 - edge1_mid) * mat_rot) + edge1_mid v3_ = ((v3 - edge2_mid) * mat_rot) + edge2_mid v4_ = ((v4 - edge2_mid) * mat_rot) + edge2_mid r = geometry.intersect_line_line(v1_, v2_, v3_, v4_) if r: p1, _ = r cp = mw * p1 bpy.context.scene.cursor_location = cp layer = get_layer() generate_gp3d_stroke(layer, p1, v1, axis, mw, origin, nv) ''' # f = [i for i in dir(bpy.context) if 'gpencil' in i] active_gpencil_frame active_gpencil_layer editable_gpencil_layers editable_gpencil_strokes gpencil_data gpencil_data_owner visible_gpencil_layers ''' #bpy.context.active_gpencil_layer = layer #print(bpy.context.gpencil_data) scn = bpy.context.scene scn.grease_pencil = bpy.data.grease_pencil['tc_circle_000'] else: print('not on a circle')
def __init__(self, curveObj, objType=OBJTYPE_NONMODIFIER): self.bCurveObj = curveObj self.scale = curveObj.scale[:] curveCopyData = curveObj.data.copy() #Non-zero values of the following attributes impacts length curveCopyData.bevel_depth = 0 curveCopyData.extrude = 0 curveCopyData.offset = 0 curveCopyObj = curveObj.copy() curveCopyObj.data = curveCopyData bpy.context.scene.collection.objects.link(curveCopyObj) if (objType == OBJTYPE_MODIFIER): depsgraph = bpy.context.evaluated_depsgraph_get() cmd = curveCopyObj.evaluated_get(depsgraph).to_mesh() else: cmd = curveCopyObj.to_mesh() self.curvePts = [cmd.vertices[cmd.edges[0].vertices[0]].co.copy()] self.curvePts += [ cmd.vertices[e.vertices[1]].co.copy() for e in cmd.edges ] bpy.context.scene.collection.objects.unlink(curveCopyObj) bpy.data.objects.remove(curveCopyObj) self.curveLength = 0 #sum(s.calc_length() for s in curveObj.data.splines) self.mw = self.bCurveObj.matrix_world for i in range(0, len(self.curvePts)): self.curvePts[i] = self.mw @ self.curvePts[i] if (i > 0): segLen = (self.curvePts[i] - self.curvePts[i - 1]).length self.curveLength += segLen self.startCo = self.curvePts[0] self.endCo = self.curvePts[-1] self.curveNormal = geometry.normal(self.curvePts)
def get_face_extras(self, geom): face_medians = [] face_normals = [] for obj_index, faces in enumerate(geom.faces): verts = geom.verts[obj_index] medians = [] normals = [] concat_median = medians.append concat_normal = normals.append for face in faces: poly_verts = [verts[idx] for idx in face] concat_normal(normal(poly_verts)) concat_median(calc_median(poly_verts)) face_medians.append(medians) face_normals.append(normals) return face_medians, face_normals
def generate_3PT(pts, obj, nv, mode=0): origin = obj.location mw = obj.matrix_world V = Vector nv = max(3, nv) # construction v1, v2, v3, v4 = V(pts[0]), V(pts[1]), V(pts[1]), V(pts[2]) edge1_mid = v1.lerp(v2, 0.5) edge2_mid = v3.lerp(v4, 0.5) axis = geometry.normal(v1, v2, v4) mat_rot = mathutils.Matrix.Rotation(math.radians(90.0), 4, axis) # triangle edges v1_ = ((v1 - edge1_mid) * mat_rot) + edge1_mid v2_ = ((v2 - edge1_mid) * mat_rot) + edge1_mid v3_ = ((v3 - edge2_mid) * mat_rot) + edge2_mid v4_ = ((v4 - edge2_mid) * mat_rot) + edge2_mid r = geometry.intersect_line_line(v1_, v2_, v3_, v4_) if r: p1, _ = r cp = mw * p1 bpy.context.scene.cursor_location = cp if mode == 0: layer = get_layer() generate_gp3d_stroke(layer, p1, v1, axis, mw, origin, nv) scn = bpy.context.scene scn.grease_pencil = bpy.data.grease_pencil['tc_circle_000'] elif mode == 1: generate_bmesh_repr(p1, v1, axis, nv) else: print('not on a circle')
def polygons_geom(config, vecs, polygons, p_vertices, p_vertex_colors, p_indices, v_path, p_cols, idx_p_offset, points_colors): '''generates polygons geometry''' if (config.color_per_polygon and not config.polygon_use_vertex_color ) or config.shade_mode == 'facet': polygon_indices, original_idx = ensure_triangles( vecs, polygons, config.handle_concave_quads) if config.shade_mode == 'facet': normals = [normal(*[Vector(vecs[c]) for c in p]) for p in polygons] if config.polygon_use_vertex_color: p_v, v_c, idx, total_p_verts = splitted_facet_polygons_geom_v_cols( polygon_indices, original_idx, v_path, points_colors, idx_p_offset[0], normals, config.vector_light) else: p_v, v_c, idx, total_p_verts = splitted_facet_polygons_geom( polygon_indices, original_idx, v_path, p_cols, idx_p_offset[0], normals, config.vector_light) elif config.shade_mode == 'smooth': normals = get_vertex_normals(vecs, polygons) p_v, v_c, idx, total_p_verts = splitted_smooth_polygons_geom( polygon_indices, original_idx, v_path, p_cols, idx_p_offset[0], normals, config.vector_light) else: p_v, v_c, idx, total_p_verts = splitted_polygons_geom( polygon_indices, original_idx, v_path, p_cols, idx_p_offset[0]) p_vertices.extend(p_v) p_vertex_colors.extend(v_c) p_indices.extend(idx) else: polygon_indices, original_idx = ensure_triangles( vecs, polygons, config.handle_concave_quads) p_vertices.extend(v_path) if config.shade_mode == 'smooth': normals = get_vertex_normals(v_path, polygons) colors = [] if config.polygon_use_vertex_color: for normal_v, col in zip(normals, points_colors): factor = normal_v.dot(config.vector_light) * 0.5 + 0.5 colors.append([ col[0] * factor, col[1] * factor, col[2] * factor, col[3] ]) else: col = p_cols for normal_v in normals: factor = normal_v.dot(config.vector_light) * 0.5 + 0.5 colors.append([ col[0] * factor, col[1] * factor, col[2] * factor, col[3] ]) p_vertex_colors.extend(colors) else: p_vertex_colors.extend([p_cols for v in v_path]) p_indices.extend([[c + idx_p_offset[0] for c in p] for p in polygon_indices]) total_p_verts = len(vecs) idx_p_offset[0] += total_p_verts
def f_(me, list_0, dict_0, opp, list_fl, in_): if len(list_fl) == 0: # loop loop = True path = False frst = list_0[0][0] list_1 = f_2(frst, list_0) del list_1[-1] else: # path loop = False path = True frst = list_fl[0] last = list_fl[1] list_1 = f_1(frst, list_0, last) n = len(list_1) for ii in range(n): p_ = (me.vertices[list_1[ii]].co).copy() p1_ = (me.vertices[list_1[(ii - 1) % n]].co).copy() p2_ = (me.vertices[list_1[(ii + 1) % n]].co).copy() vec1_ = p_ - p1_ vec2_ = p_ - p2_ ang = vec1_.angle(vec2_, any) an = round(degrees(ang)) if ang == 0 or ang == 180: continue elif ang != 0 or ang != 180: vec_no = normal(p_, p1_, p2_) break list_2 = [] list_3 = [] for i in range(n): p = (me.vertices[list_1[i]].co).copy() p1 = (me.vertices[list_1[(i - 1) % n]].co).copy() p2 = (me.vertices[list_1[(i + 1) % n]].co).copy() me.vertices[list_1[i]].select = False if in_ == True: p3 = p - (vec_no * opp) else: p3 = p + (vec_no * opp) me.vertices.add(1) me.vertices[-1].co = p3 list_2.append(list_1[i]) list_3.append(me.vertices[-1].index) n1 = len(list_2) if path == True: for j in range(n1 - 1): me.faces.add(1) me.faces[-1].vertices_raw = [ list_2[j], list_2[(j + 1) % n1], list_3[(j + 1) % n1], list_3[j] ] elif loop == True: for j in range(n1): me.faces.add(1) me.faces[-1].vertices_raw = [ list_2[j], list_2[(j + 1) % n1], list_3[(j + 1) % n1], list_3[j] ] # -- -- -- -- list_4 = [] n2 = len(list_2) for k in range(n2): q = (me.vertices[list_2[k]].co).copy() q1 = (me.vertices[list_2[(k - 1) % n2]].co).copy() q2 = (me.vertices[list_2[(k + 1) % n2]].co).copy() vec1 = q - q1 vec2 = q - q2 q3 = q - (vec1.normalized() * 0.1) q4 = q - (vec2.normalized() * 0.1) ang = vec1.angle(vec2, any) if path: if k == 0: axis = vec2 elif k == n2 - 1: axis = -vec1 else: axis = q3 - q4 mtrx = Matrix.Rotation((pi * 0.5), 3, axis) q5 = (me.vertices[list_3[k]].co).copy() tmp = q5 - q tmp1 = mtrx * tmp tmp2 = tmp1 + q if k == 0: list_4.append(tmp2) elif k == n2 - 1: list_4.append(tmp2) else: vec3 = tmp2 - q adj = opp / tan(ang / 2) h = (adj ** 2 + opp ** 2) ** 0.5 q6 = q + (vec3.normalized() * h) list_4.append(q6) elif loop: axis = q3 - q4 ang = vec1.angle(vec2, any) mtrx = Matrix.Rotation((pi * 0.5), 3, axis) q5 = (me.vertices[list_3[k]].co).copy() tmp = q5 - q tmp1 = mtrx * tmp tmp2 = tmp1 + q vec3 = tmp2 - q # -- -- -- -- d_ = tan(ang / 2) if d_ == 0: pass elif d_ != 0: adj = opp / tan(ang / 2) h = (adj ** 2 + opp ** 2) ** 0.5 q6 = q + (vec3.normalized() * h) me.vertices[list_3[k]].co = q6 # -- -- -- -- if len(list_4) == 0: pass else: for kk in range(n2): me.vertices[list_3[kk]].co = list_4[kk] me.update(calc_edges = True)
def process(self): if self.outputs['Centers'].is_linked or self.outputs['Normals'].is_linked or \ self.outputs['Origins'].is_linked or self.outputs['Norm_abs'].is_linked: if 'Polygons' in self.inputs and 'Vertices' in self.inputs \ and self.inputs['Polygons'].is_linked and self.inputs['Vertices'].is_linked: pols_ = SvGetSocketAnyType(self, self.inputs['Polygons']) vers_tupls = SvGetSocketAnyType(self, self.inputs['Vertices']) vers_vects = Vector_generate(vers_tupls) # make mesh temp утилитарно - удалить в конце mat_collect = [] normals_out = [] origins = [] norm_abs_out = [] for verst, versv, pols in zip(vers_tupls, vers_vects, pols_): normals = [] centrs = [] norm_abs = [] p0_xdirs = [] for p in pols: v0 = versv[p[0]] v1 = versv[p[1]] v2 = versv[p[2]] # save direction of 1st point in polygon p0_xdirs.append(v0) # normals norm = geometry.normal(v0, v1, v2) normals.append(norm) # centrs x,y,z = zip(*[verst[poi] for poi in p]) x,y,z = sum(x)/len(x), sum(y)/len(y), sum(z)/len(z) current_center = Vector((x,y,z)) centrs.append(current_center) # normal absolute !!! # это совершенно нормально!!! ;-) norm_abs.append(current_center+norm) if self.Separate: norm_abs_out.append(norm_abs) origins.append(centrs) normals_out.append(normals) else: norm_abs_out.extend(norm_abs) origins.extend(centrs) normals_out.extend(normals) mat_collect_ = [] for cen, nor, p0 in zip(centrs, normals, p0_xdirs): zdir = nor xdir = (Vector(p0) - cen).normalized() ydir = zdir.cross(xdir) lM = [(xdir[0], ydir[0], zdir[0], cen[0]), (xdir[1], ydir[1], zdir[1], cen[1]), (xdir[2], ydir[2], zdir[2], cen[2]), (0.0, 0.0, 0.0, 1.0)] mat_collect_.append(lM) mat_collect.extend(mat_collect_) if not self.Separate: SvSetSocketAnyType(self, 'Centers', mat_collect) SvSetSocketAnyType(self, 'Norm_abs', Vector_degenerate([norm_abs_out])) SvSetSocketAnyType(self, 'Origins', Vector_degenerate([origins])) SvSetSocketAnyType(self, 'Normals', Vector_degenerate([normals_out])) else: SvSetSocketAnyType(self, 'Centers', mat_collect) SvSetSocketAnyType(self, 'Norm_abs', Vector_degenerate(norm_abs_out)) SvSetSocketAnyType(self, 'Origins', Vector_degenerate(origins)) SvSetSocketAnyType(self, 'Normals', Vector_degenerate(normals_out))
def calc_normal(self): """:rtype: Vector""" return geom.normal(*self.coords)
def process(self): if self.outputs['Centers'].is_linked or self.outputs['Normals'].is_linked or \ self.outputs['Origins'].is_linked or self.outputs['Norm_abs'].is_linked: if 'Polygons' in self.inputs and 'Vertices' in self.inputs \ and self.inputs['Polygons'].is_linked and self.inputs['Vertices'].is_linked: pols_ = SvGetSocketAnyType(self, self.inputs['Polygons']) vers_tupls = SvGetSocketAnyType(self, self.inputs['Vertices']) vers_vects = Vector_generate(vers_tupls) # make mesh temp утилитарно - удалить в конце mat_collect = [] normals_out = [] origins = [] norm_abs_out = [] for verst, versv, pols in zip(vers_tupls, vers_vects, pols_): # medians в векторах medians = [] normals = [] centrs = [] norm_abs = [] for p in pols: # medians # it calcs middle point of opposite edges, # than finds length vector between this two points v0 = versv[p[0]] v1 = versv[p[1]] v2 = versv[p[2]] lp=len(p) if lp >= 4: l = ((lp-2)//2) + 2 v3 = versv[p[l]] poi_2 = (v2+v3)/2 # normals norm = geometry.normal(v0, v1, v2, v3) normals.append(norm) else: poi_2 = v2 # normals norm = geometry.normal(v0, v1, v2) normals.append(norm) poi_1 = (v0+v1)/2 vm = poi_2 - poi_1 medians.append(vm) # centrs x,y,z = zip(*[verst[poi] for poi in p]) x,y,z = sum(x)/len(x), sum(y)/len(y), sum(z)/len(z) current_center = Vector((x,y,z)) centrs.append(current_center) # normal absolute !!! # это совершенно нормально!!! ;-) norm_abs.append(current_center+norm) norm_abs_out.append(norm_abs) origins.append(centrs) normals_out.extend(normals) mat_collect_ = [] for cen, med, nor in zip(centrs, medians, normals): loc = Matrix.Translation(cen) # need better solution for Z,Y vectors + may be X vector correction vecz = Vector((0, 1e-6, 1)) q_rot0 = vecz.rotation_difference(nor).to_matrix().to_4x4() q_rot2 = nor.rotation_difference(vecz).to_matrix().to_4x4() vecy = Vector((1e-6, 1, 0)) * q_rot2 q_rot1 = vecy.rotation_difference(med).to_matrix().to_4x4() # loc is matrix * rot vector * rot vector M = loc*q_rot1*q_rot0 lM = [ j[:] for j in M ] mat_collect_.append(lM) mat_collect.extend(mat_collect_) SvSetSocketAnyType(self, 'Centers', mat_collect) SvSetSocketAnyType(self, 'Norm_abs', Vector_degenerate(norm_abs_out)) SvSetSocketAnyType(self, 'Origins', Vector_degenerate(origins)) SvSetSocketAnyType(self, 'Normals', Vector_degenerate([normals_out]))
def process(self): verts_socket, poly_socket = self.inputs norm_socket, norm_abs_socket, origins_socket, centers_socket = self.outputs if not any([s.is_linked for s in self.outputs]): return if not (verts_socket.is_linked and poly_socket.is_linked): return pols_ = poly_socket.sv_get() vers_tupls = verts_socket.sv_get() vers_vects = Vector_generate(vers_tupls) # make mesh temp утилитарно - удалить в конце mat_collect = [] normals_out = [] origins = [] norm_abs_out = [] for verst, versv, pols in zip(vers_tupls, vers_vects, pols_): normals = [] centrs = [] norm_abs = [] p0_xdirs = [] for p in pols: v0 = versv[p[0]] v1 = versv[p[1]] v2 = versv[p[2]] # save direction of 1st point in polygon p0_xdirs.append(v0) # normals norm = geometry.normal(v0, v1, v2) normals.append(norm) # centrs x,y,z = zip(*[verst[poi] for poi in p]) x,y,z = sum(x)/len(x), sum(y)/len(y), sum(z)/len(z) current_center = Vector((x,y,z)) centrs.append(current_center) # normal absolute !!! # это совершенно нормально!!! ;-) norm_abs.append(current_center+norm) if self.Separate: norm_abs_out.append(norm_abs) origins.append(centrs) normals_out.append(normals) else: norm_abs_out.extend(norm_abs) origins.extend(centrs) normals_out.extend(normals) mat_collect_ = [] for cen, nor, p0 in zip(centrs, normals, p0_xdirs): zdir = nor xdir = (Vector(p0) - cen).normalized() ydir = zdir.cross(xdir) lM = [(xdir[0], ydir[0], zdir[0], cen[0]), (xdir[1], ydir[1], zdir[1], cen[1]), (xdir[2], ydir[2], zdir[2], cen[2]), (0.0, 0.0, 0.0, 1.0)] mat_collect_.append(Matrix(lM)) mat_collect.extend(mat_collect_) if not self.Separate: norm_abs_out = [norm_abs_out] origins = [origins] normals_out = [normals_out] centers_socket.sv_set(mat_collect) norm_abs_socket.sv_set(Vector_degenerate(norm_abs_out)) origins_socket.sv_set(Vector_degenerate(origins)) norm_socket.sv_set(Vector_degenerate(normals_out))
def convex_hull_3d(vecs, eps:'距離がこれ以下なら同一平面と見做す'=1e-6): """三次元又は二次元の凸包を求める""" if len(vecs) <= 1: return list(range(len(vecs))) verts = [_Vert(i, v) for i, v in enumerate(vecs)] # なるべく離れている二頂点を求める medium = reduce(lambda a, b: a + b, vecs) / len(vecs) v1 = max(verts, key=lambda v: (v.co - medium).length) v2 = max(verts, key=lambda v: (v.co - v1.co).length) line = v2.co - v1.co if line.length <= eps: # 全ての頂点が重なる return [0] verts.remove(v1) verts.remove(v2) if not verts: return [v1.index, v2.index] # 三角形を構成する為の頂点を求める v3 = max(verts, key=lambda v: line.cross(v.co - v1.co).length) if line.normalized().cross(v3.co - v1.co).length <= eps: # 全ての頂点が同一線上にある return [v1.index, v2.index] verts.remove(v3) if not verts: return [v1.index, v2.index, v3.index] # 四面体を構成する為の頂点を求める normal = geom.normal(v1.co, v2.co, v3.co) def key_func(v): return abs(geom.distance_point_to_plane(v.co, v1.co, normal)) v4 = max(verts, key=key_func) if key_func(v4) <= eps: # 全ての頂点が平面上にある quat = normal.rotation_difference(Vector((0, 0, 1))) vecs_2d = [(quat * v).to_2d() for v in vecs] return convex_hull_2d(vecs_2d, eps) verts.remove(v4) # 四面体作成 # ^ normal # v3 | # / |\ # v1 /____\v2 # \ / # \ / # v4 if geom.distance_point_to_plane(v4.co, v1.co, normal) < 0.0: faces = [_Face(v1, v2, v3), _Face(v1, v4, v2), _Face(v2, v4, v3), _Face(v3, v4, v1)] else: faces = [_Face(v1, v3, v2), _Face(v1, v2, v4), _Face(v2, v3, v4), _Face(v3, v1, v4)] # 残りの頂点を各面に分配 _divide_outer_verts(faces, verts, eps) # edge_faces作成 edge_faces = defaultdict(list) for face in faces: for ekey in face.edge_keys: edge_faces[ekey].append(face) while True: added = False for i in range(len(faces)): try: face = faces[i] except: break if not face.outer_verts: continue v1 = max(face.outer_verts, key=lambda v: face.distance(v.co)) if face.distance(v1.co) > eps: # 凸包になるようにv1から放射状に面を貼る added = True # 隠れて不要となる面を求める remove_faces = set() _find_remove_faces_re(remove_faces, v1.co, face, edge_faces, eps) # remove_facesを多面体から除去して穴を開ける for f in remove_faces: for ekey in f.edge_keys: edge_faces[ekey].remove(f) faces.remove(f) # 穴に面を貼る new_faces = [] ekey_count = defaultdict(int) for f in remove_faces: for ekey in f.edge_keys: ekey_count[ekey] += 1 for ekey, cnt in ekey_count.items(): if cnt != 1: continue linkface = edge_faces[ekey][0] v2, v3 = ekey if linkface.verts[linkface.verts.index(v2) - 1] != v3: v2, v3 = v3, v2 new_face = _Face(v1, v2, v3) for key in new_face.edge_keys: edge_faces[key].append(new_face) new_faces.append(new_face) faces.extend(new_faces) # 頂点の再分配 outer_verts = reduce(lambda a, b: a + b, (f.outer_verts for f in remove_faces)) if v1 in outer_verts: outer_verts.remove(v1) _divide_outer_verts(new_faces, outer_verts, eps) else: face.outer_verts = [] if not added: break return [[v.index for v in f.verts] for f in faces]
def process(self): verts_socket, poly_socket = self.inputs norm_socket, norm_abs_socket, origins_socket, centers_socket = self.outputs if not any([s.is_linked for s in self.outputs]): return if not (verts_socket.is_linked and poly_socket.is_linked): return pols_ = poly_socket.sv_get() vers_tupls = verts_socket.sv_get() vers_vects = Vector_generate(vers_tupls) # make mesh temp утилитарно - удалить в конце mat_collect = [] normals_out = [] origins = [] norm_abs_out = [] for verst, versv, pols in zip(vers_tupls, vers_vects, pols_): # medians в векторах medians = [] normals = [] centrs = [] norm_abs = [] for p in pols: # medians # it calcs middle point of opposite edges, # than finds length vector between this two points v0 = versv[p[0]] v1 = versv[p[1]] v2 = versv[p[2]] lp=len(p) if lp >= 4: l = ((lp-2)//2) + 2 v3 = versv[p[l]] poi_2 = (v2+v3)/2 # normals norm = geometry.normal(v0, v1, v2, v3) normals.append(norm) else: poi_2 = v2 # normals norm = geometry.normal(v0, v1, v2) normals.append(norm) poi_1 = (v0+v1)/2 vm = poi_2 - poi_1 medians.append(vm) # centrs x,y,z = zip(*[verst[poi] for poi in p]) x,y,z = sum(x)/len(x), sum(y)/len(y), sum(z)/len(z) current_center = Vector((x,y,z)) centrs.append(current_center) # normal absolute !!! # это совершенно нормально!!! ;-) norm_abs.append(current_center+norm) if self.Separate: norm_abs_out.append(norm_abs) origins.append(centrs) normals_out.append(normals) else: norm_abs_out.extend(norm_abs) origins.extend(centrs) normals_out.extend(normals) mat_collect_ = [] for cen, med, nor in zip(centrs, medians, normals): loc = Matrix.Translation(cen) # need better solution for Z,Y vectors + may be X vector correction vecz = Vector((0, 1e-6, 1)) q_rot0 = vecz.rotation_difference(nor).to_matrix().to_4x4() q_rot2 = nor.rotation_difference(vecz).to_matrix().to_4x4() if med[1]>med[0]: vecy = Vector((1e-6, 1, 0)) * q_rot2 else: vecy = Vector((1, 1e-6, 0)) * q_rot2 q_rot1 = vecy.rotation_difference(med).to_matrix().to_4x4() # loc is matrix * rot vector * rot vector M = loc*q_rot1*q_rot0 lM = [ j[:] for j in M ] mat_collect_.append(lM) mat_collect.extend(mat_collect_) if not self.Separate: norm_abs_out = [norm_abs_out] origins = [origins] normals_out = [normals_out] centers_socket.sv_set(mat_collect) norm_abs_socket.sv_set(Vector_degenerate(norm_abs_out)) origins_socket.sv_set(Vector_degenerate(origins)) norm_socket.sv_set(Vector_degenerate(normals_out))
def normal(self): return geometry.normal(*[x._position for x in self._vertices])
def offset_edges(verts_in, edges_in, shift_in): # take an input mesh (verts + edges ) and an offset property and generate the resulting geometry verts_out = [] faces_out = [] verts_z = verts_in[:] verts_in = [Vector(v).to_2d() for v in verts_in] diff_shift = len(verts_in) - len(shift_in) if diff_shift >= 0: shift_in.extend([shift_in[-1] for _ in range(diff_shift)]) else: shift_in = shift_in[:diff_shift] #Searching neighbours for each point neighbours = [[] for _ in verts_in] for edg in edges_in: neighbours[edg[0]].append(edg) neighbours[edg[1]].append(edg) #Sorting neighbours by hour hand for i, p_neighbs in enumerate(neighbours): if len(p_neighbs) != 1: angles = [0] first_item = list(set(p_neighbs[0]) - set([i]))[0] for another_neighb in p_neighbs[1:]: second_item = list(set(another_neighb) - set([i]))[0] vector1 = verts_in[first_item] - verts_in[i] vector2 = verts_in[second_item] - verts_in[i] angles.append(calc_angle(vector1, vector2)) sorted_ = list(zip(angles, p_neighbs)) sorted_.sort() neighbours[i] = [e[1] for e in sorted_] def get_end_points(item_point, shift): second_item = list(set(neighbours[item_point][0]) - set([item_point]))[0] vert_edg = verts_in[item_point] - verts_in[second_item] mat_r1 = Matrix.Rotation(radians(-45),2,'X') mat_r2 = Matrix.Rotation(radians(45),2,'X') shift_end_points = shift/sin(radians(45)) vert_new1 = (vert_edg * mat_r1).normalized() * shift_end_points + verts_in[item_point] vert_new2 = (vert_edg * mat_r2).normalized() * shift_end_points + verts_in[item_point] return [vert_new1,vert_new2] def get_middle_points(item_point, shift): points = [] for i in range(len(neighbours[item_point])): current_edges = (neighbours[item_point][i:] + neighbours[item_point][:i])[:2] second_item1 = list(set(current_edges[0]) - set([item_point]))[0] second_item2 = list(set(current_edges[1]) - set([item_point]))[0] vert_edg1 = verts_in[second_item1] - verts_in[item_point] vert_edg2 = verts_in[second_item2] - verts_in[item_point] angle = calc_angle(vert_edg1, vert_edg2) / 2 mat_r = Matrix.Rotation(angle, 2, 'X') points.append((vert_edg1 * mat_r).normalized() * shift/sin(angle) + verts_in[item_point]) return points #Seting points findex_new_points = [0] for i,sh in enumerate(shift_in): #avoid zero offset if not sh: sh = 0.001 if len(neighbours[i]) == 1: verts_out.extend(get_end_points(i,sh)) findex_new_points.append(findex_new_points[-1] + 2) else: p = get_middle_points(i,sh) verts_out.extend(p) findex_new_points.append(findex_new_points[-1] + len(p)) # Preparing Z coordinate z_co = [] for c,(i1,i2) in enumerate(zip(findex_new_points[:-1], findex_new_points[1:])): z_co.extend([verts_z[c][2] for _ in range(i2-i1)]) #Creating faces and mark outer edges and central points outer_edges = [] current_index = len(verts_out) - 1 vers_mask = [0 for _ in verts_out] nomber_outer_points = current_index position_old_points = [0 for _ in verts_out] for edg in edges_in: need_points = [] for i in edg: if len(neighbours[i]) <= 2: need_points.extend([findex_new_points[i], findex_new_points[i] + 1]) else: position = neighbours[i].index(edg) nomber_points = len(neighbours[i]) variants_positions = list(range(findex_new_points[i], findex_new_points[i] + nomber_points)) need_points.extend((variants_positions[position - 1:] + variants_positions[:position - 1])[:2]) vec_edg = verts_in[edg[0]] - verts_in[edg[1]] vec_1 = verts_out[need_points[0]] - verts_in[edg[0]] vec_2 = verts_out[need_points[1]] - verts_in[edg[0]] vec_3 = verts_out[need_points[2]] - verts_in[edg[1]] vec_4 = verts_out[need_points[3]] - verts_in[edg[1]] new_vecs = [vec_1, vec_2, vec_3, vec_4] angles = [vec_edg.angle_signed(vec) for vec in new_vecs] if position_old_points[edg[0]] == 0: verts_out.append(verts_in[edg[0]]) z_co.append(verts_z[edg[0]][2]) vers_mask.append(1) current_index += 1 position_old_points[edg[0]] = current_index if position_old_points[edg[1]] == 0: verts_out.append(verts_in[edg[1]]) z_co.append(verts_z[edg[1]][2]) vers_mask.append(1) current_index += 1 position_old_points[edg[1]] = current_index n_p = need_points if angles[0] < 0 and angles[2] < 0 or angles[0] >= 0 and angles[2] >= 0: new_edges = [[n_p[1], position_old_points[edg[0]], position_old_points[edg[1]], n_p[3]], [n_p[0], position_old_points[edg[0]], position_old_points[edg[1]], n_p[2]]] outer_edges.extend([[n_p[1],n_p[3]],[n_p[0],n_p[2]]]) else: new_edges = [[n_p[0], position_old_points[edg[0]], position_old_points[edg[1]], n_p[3]], [n_p[1], position_old_points[edg[0]], position_old_points[edg[1]], n_p[2]]] outer_edges.extend([[n_p[0],n_p[3]],[n_p[1],n_p[2]]]) if len(neighbours[edg[0]]) == 1: new_edges.append([need_points[1], position_old_points[edg[0]], need_points[0]]) outer_edges.append([need_points[1],need_points[0]]) if len(neighbours[edg[1]]) == 1: new_edges.append([need_points[2], position_old_points[edg[1]], need_points[3]]) outer_edges.append([need_points[2],need_points[3]]) for c,face in enumerate(new_edges): if normal(*[verts_out[i].to_3d() for i in face])[2] < 0: new_edges[c] = new_edges[c][::-1] faces_out.extend(new_edges) verts_out = [(v.x, v.y, z) for v, z in zip(verts_out, z_co)] return verts_out, faces_out, outer_edges, vers_mask
def OBB(vecs, r_indices=None, eps=1e-6): """Convex hull を用いたOBBを返す。 Z->Y->Xの順で長さが最少となる軸を求める。 :param vecs: list of Vector :type vecs: list | tuple :param r_indices: listを渡すとconvexhullの結果を格納する :type r_indices: None | list :param eps: 種々の計算の閾値 :return: (matrix, obb_size) matrix: type: Matrx OBBの回転と中心を表す。vecsが二次元ベクトルの場合は3x3, 三次元なら4x4。 obb_size: type: Vector OBBの各軸の長さ。vecsと同じ次元。 :rtype: (Matrix, Vector) """ if not vecs: return None, None # 2D ---------------------------------------------------------------------- if len(vecs[0]) == 2: mat = Matrix.Identity(3) bb_size = Vector((0, 0)) indices = convex_hull_2d(vecs, eps) if r_indices: r_indices[:] = indices if len(indices) == 1: mat.col[2][:2] = vecs[0] elif len(indices) == 2: v1 = vecs[indices[0]] v2 = vecs[indices[1]] xaxis = (v2 - v1).normalized() angle = math.atan2(xaxis[1], xaxis[0]) mat2 = Matrix.Rotation(angle, 2) mat.col[0][:2] = mat2.col[0] mat.col[1][:2] = mat2.col[1] mat.col[2][:2] = (v1 + v2) / 2 bb_size[0] = (v2 - v1).length else: yaxis = _closest_axis_on_plane(vecs, indices) angle = math.atan2(yaxis[1], yaxis[0]) - math.pi / 2 # X軸 mat2 = Matrix.Rotation(angle, 2) imat2 = Matrix.Rotation(-angle, 2) rotvecs = [imat2 * v for v in vecs] loc = Vector((0, 0)) for i in range(2): rotvecs.sort(key=lambda v: v[i]) bb_size[i] = rotvecs[-1][i] - rotvecs[0][i] loc[i] = (rotvecs[0][i] + rotvecs[-1][i]) / 2 mat.col[0][:2] = mat2.col[0] mat.col[1][:2] = mat2.col[1] mat.col[2][:2] = mat2 * loc return mat, bb_size # 3D ---------------------------------------------------------------------- mat = Matrix.Identity(4) bb_size = Vector((0, 0, 0)) indices = convex_hull(vecs, eps) if r_indices: r_indices[:] = indices if isinstance(indices[0], int): # 2d if len(indices) == 1: mat.col[3][:3] = vecs[0] return mat, bb_size elif len(indices) == 2: # 同一線上 v1 = vecs[indices[0]] v2 = vecs[indices[1]] xaxis = (v2 - v1).normalized() quat = Vector((1, 0, 0)).rotation_difference(xaxis) mat = quat.to_matrix().to_4x4() mat.col[3][:3] = (v1 + v2) / 2 bb_size[0] = (v2 - v1).length return mat, bb_size else: # 同一平面上 medium = reduce(lambda a, b: a + b, vecs) / len(vecs) v1 = max(vecs, key=lambda v: (v - medium).length) v2 = max(vecs, key=lambda v: (v - v1).length) line = v2 - v1 v3 = max(vecs, key=lambda v: line.cross(v - v1).length) zaxis = geom.normal(v1, v2, v3) if zaxis[2] < 0.0: zaxis.negate() quat = zaxis.rotation_difference(Vector((0, 0, 1))) rotvecs = [quat * v for v in vecs] indices_2d = indices else: # 3d indices_set = set(chain(*indices)) zaxis = None dist = 0.0 # 最も距離の近い面(平面)と頂点を求める for tri in indices: v1, v2, v3 = [vecs[i] for i in tri] normal = geom.normal(v1, v2, v3) d = 0.0 for v4 in (vecs[i] for i in indices_set if i not in tri): f = abs(geom.distance_point_to_plane(v4, v1, normal)) d = max(f, d) if zaxis is None or d < dist: zaxis = -normal dist = d quat = zaxis.rotation_difference(Vector((0, 0, 1))) rotvecs = [(quat * v).to_2d() for v in vecs] indices_2d = convex_hull_2d(rotvecs, eps) yaxis = _closest_axis_on_plane(rotvecs, indices_2d) yaxis = quat.inverted() * yaxis.to_3d() xaxis = yaxis.cross(zaxis) xaxis.normalize() # 不要? mat.col[0][:3] = xaxis mat.col[1][:3] = yaxis mat.col[2][:3] = zaxis # OBBの大きさと中心を求める imat = mat.inverted() rotvecs = [imat * v for v in vecs] loc = Vector() for i in range(3): rotvecs.sort(key=lambda v: v[i]) bb_size[i] = rotvecs[-1][i] - rotvecs[0][i] loc[i] = (rotvecs[0][i] + rotvecs[-1][i]) / 2 mat.col[3][:3] = mat * loc return mat, bb_size
def calc_normal(self): return geom.normal(*self.coords)
def offset_edges(verts_in, edges_in, shift_in): # take an input mesh (verts + edges ) and an offset property and generate the resulting geometry verts_out = [] faces_out = [] verts_z = verts_in[:] verts_in = [Vector(v).to_2d() for v in verts_in] diff_shift = len(verts_in) - len(shift_in) if diff_shift >= 0: shift_in.extend([shift_in[-1] for _ in range(diff_shift)]) else: shift_in = shift_in[:diff_shift] #Searching neighbours for each point neighbours = [[] for _ in verts_in] for edg in edges_in: neighbours[edg[0]].append(edg) neighbours[edg[1]].append(edg) #Sorting neighbours by hour hand for i, p_neighbs in enumerate(neighbours): if len(p_neighbs) != 1: angles = [0] first_item = list(set(p_neighbs[0]) - set([i]))[0] for another_neighb in p_neighbs[1:]: second_item = list(set(another_neighb) - set([i]))[0] vector1 = verts_in[first_item] - verts_in[i] vector2 = verts_in[second_item] - verts_in[i] angles.append(calc_angle(vector1, vector2)) sorted_ = list(zip(angles, p_neighbs)) sorted_.sort() neighbours[i] = [e[1] for e in sorted_] def get_end_points(item_point, shift): second_item = list(set(neighbours[item_point][0]) - set([item_point]))[0] vert_edg = verts_in[item_point] - verts_in[second_item] mat_r1 = Matrix.Rotation(radians(-45),2,'X') mat_r2 = Matrix.Rotation(radians(45),2,'X') shift_end_points = shift/sin(radians(45)) vert_new1 = (vert_edg @ mat_r1).normalized() * shift_end_points + verts_in[item_point] vert_new2 = (vert_edg @ mat_r2).normalized() * shift_end_points + verts_in[item_point] return [vert_new1,vert_new2] def get_middle_points(item_point, shift): points = [] for i in range(len(neighbours[item_point])): current_edges = (neighbours[item_point][i:] + neighbours[item_point][:i])[:2] second_item1 = list(set(current_edges[0]) - set([item_point]))[0] second_item2 = list(set(current_edges[1]) - set([item_point]))[0] vert_edg1 = verts_in[second_item1] - verts_in[item_point] vert_edg2 = verts_in[second_item2] - verts_in[item_point] angle = calc_angle(vert_edg1, vert_edg2) / 2 mat_r = Matrix.Rotation(angle, 2, 'X') points.append((vert_edg1 @ mat_r).normalized() * shift/sin(angle) + verts_in[item_point]) return points #Seting points findex_new_points = [0] for i,sh in enumerate(shift_in): #avoid zero offset if not sh: sh = 0.001 if len(neighbours[i]) == 1: verts_out.extend(get_end_points(i,sh)) findex_new_points.append(findex_new_points[-1] + 2) else: p = get_middle_points(i,sh) verts_out.extend(p) findex_new_points.append(findex_new_points[-1] + len(p)) # Preparing Z coordinate z_co = [] for c,(i1,i2) in enumerate(zip(findex_new_points[:-1], findex_new_points[1:])): z_co.extend([verts_z[c][2] for _ in range(i2-i1)]) #Creating faces and mark outer edges and central points outer_edges = [] current_index = len(verts_out) - 1 vers_mask = [0 for _ in verts_out] nomber_outer_points = current_index position_old_points = [0 for _ in verts_out] for edg in edges_in: need_points = [] for i in edg: if len(neighbours[i]) <= 2: need_points.extend([findex_new_points[i], findex_new_points[i] + 1]) else: position = neighbours[i].index(edg) nomber_points = len(neighbours[i]) variants_positions = list(range(findex_new_points[i], findex_new_points[i] + nomber_points)) need_points.extend((variants_positions[position - 1:] + variants_positions[:position - 1])[:2]) vec_edg = verts_in[edg[0]] - verts_in[edg[1]] vec_1 = verts_out[need_points[0]] - verts_in[edg[0]] vec_2 = verts_out[need_points[1]] - verts_in[edg[0]] vec_3 = verts_out[need_points[2]] - verts_in[edg[1]] vec_4 = verts_out[need_points[3]] - verts_in[edg[1]] new_vecs = [vec_1, vec_2, vec_3, vec_4] angles = [vec_edg.angle_signed(vec) for vec in new_vecs] if position_old_points[edg[0]] == 0: verts_out.append(verts_in[edg[0]]) z_co.append(verts_z[edg[0]][2]) vers_mask.append(1) current_index += 1 position_old_points[edg[0]] = current_index if position_old_points[edg[1]] == 0: verts_out.append(verts_in[edg[1]]) z_co.append(verts_z[edg[1]][2]) vers_mask.append(1) current_index += 1 position_old_points[edg[1]] = current_index n_p = need_points if angles[0] < 0 and angles[2] < 0 or angles[0] >= 0 and angles[2] >= 0: new_edges = [[n_p[1], position_old_points[edg[0]], position_old_points[edg[1]], n_p[3]], [n_p[0], position_old_points[edg[0]], position_old_points[edg[1]], n_p[2]]] outer_edges.extend([[n_p[1],n_p[3]],[n_p[0],n_p[2]]]) else: new_edges = [[n_p[0], position_old_points[edg[0]], position_old_points[edg[1]], n_p[3]], [n_p[1], position_old_points[edg[0]], position_old_points[edg[1]], n_p[2]]] outer_edges.extend([[n_p[0],n_p[3]],[n_p[1],n_p[2]]]) if len(neighbours[edg[0]]) == 1: new_edges.append([need_points[1], position_old_points[edg[0]], need_points[0]]) outer_edges.append([need_points[1],need_points[0]]) if len(neighbours[edg[1]]) == 1: new_edges.append([need_points[2], position_old_points[edg[1]], need_points[3]]) outer_edges.append([need_points[2],need_points[3]]) for c,face in enumerate(new_edges): if normal(*[verts_out[i].to_3d() for i in face])[2] < 0: new_edges[c] = new_edges[c][::-1] faces_out.extend(new_edges) verts_out = [(v.x, v.y, z) for v, z in zip(verts_out, z_co)] return verts_out, faces_out, outer_edges, vers_mask
def execute(self, context): if self.mode in ('selected', 'center', 'grid', 'active'): self.execute_builtin(context) return {'FINISHED'} actob = context.active_object mat = actob.matrix_world editmode = 'EDIT' in context.mode if editmode: bpy.ops.object.mode_set(mode='OBJECT') mesh = None if editmode and actob.type == 'MESH': mesh = actob.data loc = None if self.mode == 'circle': if mesh: vecs = (v.co for v in mesh.vertices if v.select and not v.hide) vecs = [mat * v for v in vecs] else: vecs = [Vector(ob.matrix_world.col[3][:3]) for ob in context.selected_objects] if len(vecs) == 3: center = vam.center_of_circumscribed_circle_tri(*vecs) if center: loc = center r = (vecs[0] - loc).length self.report({'INFO'}, 'r: {}'.format(r)) else: self.report({'WARNING'}, 'Select 3 vectors') elif self.mode == 'sphere': if mesh: vecs = (v.co for v in mesh.vertices if v.select and not v.hide) vecs = [mat * v for v in vecs] if len(vecs) < 4: self.report({'WARNING'}, 'Select more than 4 vertices') elif len(vecs) > 16: # 計算に時間がかかるのでパス。 txt = '{} vertices is selected. Too many'.format(len(vecs)) self.report({'WARNING'}, txt) else: locations = [] normals = [] for v1, v2, v3 in combinations(vecs, 3): center = vam.center_of_circumscribed_circle_tri( v1, v2, v3) if center: locations.append(center) normals.append(geom.normal(v1, v2, v3)) inters = [] for i, j in combinations(range(len(locations)), 2): cross_length = normals[i].cross(normals[j]).length angle = math.asin(min(cross_length, 1.0)) if angle < ANGLE_THRESHOLD: continue v1 = locations[i] v2 = v1 + normals[i] v3 = locations[j] v4 = v3 + normals[j] inter = geom.intersect_line_line(v1, v2, v3, v4) if inter: inters.append((inter[0] + inter[1]) / 2) if inters: loc = (reduce(lambda v1, v2: v1 + v2, inters) / len(inters)) r_sum = 0 for v in vecs: r_sum += (v - loc).length r = r_sum /len(vecs) self.report({'INFO'}, 'r: {}'.format(r)) elif self.mode == 'median': if mesh: dm = self.get_dm(context) vecs = [v.co for v in dm.vertices if v.select and not v.hide] if vecs: vecs = [mat * v for v in vecs] loc = (reduce(lambda v1, v2: v1 + v2, vecs) / len(vecs)) elif self.mode == 'boundbox': if mesh: dm = self.get_dm(context) vecs = (v.co for v in dm.vertices if v.select and not v.hide) vecs = [mat * v for v in vecs] if vecs: v = vecs[0] mins = [v[0], v[1], v[2]] maxs = [v[0], v[1], v[2]] for v in vecs: for i in range(3): if v[i] < mins[i]: mins[i] = v[i] if v[i] > maxs[i]: maxs[i] = v[i] loc = Vector([(mins[i] + maxs[i]) / 2 for i in range(3)]) if editmode: bpy.ops.object.mode_set(mode='EDIT') if loc: context.scene.cursor_location = loc return {'FINISHED'}