def stroke_normal(it): """ Compute the 2D normal at the stroke vertex pointed by the iterator 'it'. It is noted that Normal2DF0D computes normals based on underlying FEdges instead, which is inappropriate for strokes when they have already been modified by stroke geometry modifiers. """ # first stroke segment it_next = it.incremented() if it.is_begin: e = it_next.object.point_2d - it.object.point_2d n = Vector((e[1], -e[0])) return n.normalized() # last stroke segment it_prev = it.decremented() if it_next.is_end: e = it.object.point_2d - it_prev.object.point_2d n = Vector((e[1], -e[0])) return n.normalized() # two subsequent stroke segments e1 = it_next.object.point_2d - it.object.point_2d e2 = it.object.point_2d - it_prev.object.point_2d n1 = Vector((e1[1], -e1[0])).normalized() n2 = Vector((e2[1], -e2[0])).normalized() n = (n1 + n2) return n.normalized()
def _do_rgb_to_dxyz_w_calc(prototype, data): r = data _set_xyz(prototype, r, r[prototype.index("R"):prototype.index("B")]) dir_vec = Vector((_get_xyz(prototype, r))) _set_width(prototype, r, 1 - dir_vec.magnitude) _set_xyz(prototype, r, dir_vec.normalized()) _set_rgb(prototype, r, [1, 1, 1])
def execute(self, context): volmesh = context.active_object for i in range(len(volmesh.custom_vectorfield)): tempVect = Vector(volmesh.custom_vectorfield[i].vvelocity) volmesh.custom_vectorfield[i].vvelocity = tempVect.normalized() context.window_manager.vf_showingvelocitylines = -1 return {'FINISHED'}
def get_dim_coords(context, myobj, DimGen, dim, mat, offset_pos = True): dimProps = dim if dim.uses_style: for alignedDimStyle in context.scene.StyleGenerator.alignedDimensions: if alignedDimStyle.name == dim.style: dimProps = alignedDimStyle # get points positions from indicies aMatrix = dim.dimObjectA.matrix_world bMatrix = dim.dimObjectB.matrix_world offset = dim.dimOffset geoOffset = dim.dimLeaderOffset # get points positions from indicies p1Local = None p2Local = None try: p1Local = get_mesh_vertex(dim.dimObjectA,dim.dimPointA,dimProps.evalMods) except IndexError: print('p1 excepted for ' + dim.name + ' on ' + myobj.name) try: p2Local = get_mesh_vertex(dim.dimObjectB,dim.dimPointB,dimProps.evalMods) except IndexError: print('p2 excepted for ' + dim.name + ' on ' + myobj.name) p1 = get_point(p1Local, dim.dimObjectA,aMatrix) p2 = get_point(p2Local, dim.dimObjectB,bMatrix) #check dominant Axis sortedPoints = sortPoints(p1, p2) p1 = sortedPoints[0] p2 = sortedPoints[1] distVector = Vector(p1)-Vector(p2) dist = distVector.length midpoint = interpolate3d(p1, p2, fabs(dist / 2)) normDistVector = distVector.normalized() # Compute offset vector from face normal and user input rotationMatrix = Matrix.Rotation(dim.dimRotation, 4, normDistVector) selectedNormal = Vector(select_normal(myobj, dim, normDistVector, midpoint, dimProps)) userOffsetVector = rotationMatrix@selectedNormal offsetDistance = userOffsetVector*offset geoOffsetDistance = offsetDistance.normalized()*geoOffset if offsetDistance < geoOffsetDistance: offsetDistance = geoOffsetDistance dimLineStart = Vector(p1)+offsetDistance dimLineEnd = Vector(p2)+offsetDistance if offset_pos: return [dimLineStart,dimLineEnd] else: return [p1,p2]
def _pre_calc(self): curve = self.curve t_min, t_max = curve.get_u_bounds() ts = np.linspace(t_min, t_max, num=self.resolution) points = curve.evaluate_array(ts) tangents, normals, binormals = curve.tangent_normal_binormal_array(ts) tangents /= np.linalg.norm(tangents, axis=1, keepdims=True) normal = normals[0] if np.linalg.norm(normal) > 1e-4: binormal = binormals[0] binormal /= np.linalg.norm(binormal) else: tangent = tangents[0] normal = Vector(tangent).orthogonal() normal = np.array(normal) binormal = np.cross(tangent, normal) binormal /= np.linalg.norm(binormal) out_normals = [normal] out_binormals = [binormal] for point, tangent in zip(points[1:], tangents[1:]): plane = PlaneEquation.from_normal_and_point(Vector(tangent), Vector(point)) normal = plane.projection_of_vector(Vector(point), Vector(point + normal)) normal = np.array(normal.normalized()) binormal = np.cross(tangent, normal) binormal /= np.linalg.norm(binormal) out_normals.append(normal) out_binormals.append(binormal) self.quats = self._make_quats(points, tangents, np.array(out_normals), np.array(out_binormals)) self.tknots = ts
def getVector(self, point): vect = Vector((0.0, 0.0, 0.0)) for n in range(0, len(self.guides)): guide = self.guides[n] weight = self.weights[n] vect += guide.getVector(point).normalized() * weight return vect.normalized()
def create_spiral_meshdata(center=(0, 0, 0), offset=1, points_per_round=20, rounds=3, extend=0, invert_direction=False): verts = [] for i in range(points_per_round * rounds + 1): p = i / points_per_round t = 2 * math.pi * p r = offset * p #+ p verts.append( Vector((r * math.sin(t) + center[0], r * math.cos(t) + center[1], center[2]))) if extend > 0: tangent = Vector((r * math.cos(t), -r * math.sin(t), 0.0)) verts.append( Vector(verts[len(verts) - 1]) + extend * tangent.normalized()) if invert_direction: for i, v in enumerate(verts): verts[i] = verts[i] - verts[len(verts) - 1] verts.reverse() edges = [] for i in range(len(verts) - 1): edges.append([i, i + 1]) mesh_data = bpy.data.meshes.new("spiral") mesh_data.from_pydata(verts, edges, []) return mesh_data, verts[0], verts[len(verts) - 1]
def create_power_labels(self): obj = bpy.context.scene.objects.get(self.PowerShowObject) self.create_total_labels() mat_world = obj.matrix_world cam = bpy.context.scene.objects['Camera'] cam_center = cam.location for poly in obj.data.polygons: text_name = 'sun_' + obj.name + '_text_' + str(poly.index).replace( '.', '_') bpy.ops.object.text_add(location=mat_world * Vector(poly.center)) myFontOb = bpy.context.object myFontOb.data.body = "0" myFontOb.name = text_name myFontOb.data.align = 'CENTER' myFontOb.scale = (0.3, 0.3, 0.3) obj.rotation_mode = 'QUATERNION' cam_norm = obj.rotation_quaternion * poly.normal fnorm = Vector((-1, 0, 0)) axis = fnorm.cross(cam_norm) dot = fnorm.normalized().dot(cam_norm.normalized()) dot = clamp(dot, -1.0, 1.0) if axis.length < 1.0e-8: axis = Vector(get_ortho(fnorm.x, fnorm.y, fnorm.z)) myFontOb.rotation_mode = 'AXIS_ANGLE' myFontOb.rotation_axis_angle = [ math.acos(dot) + math.pi, axis[0], axis[1], axis[2] ] myFontOb.convert_space(from_space='WORLD', to_space='LOCAL') myFontOb.rotation_mode = 'XYZ' myFontOb.rotation_euler = (radians(90), 0, myFontOb.rotation_euler[2] + radians(90)) bpy.context.scene.update() self.PowerObjectLabels_created = True
def JoinAxis(VX=0, VY=0, VZ=0): vec = Vector([VX, VY, VZ]) CLIP = ClipAxis(vec.length) NORM = vec.normalized()*CLIP return NORM
def proj_z(self, t, dz0, next=None, dz1=0): """ length of projection along crossing line / circle deformation unit vector for profil in z axis at line / line intersection so f(y) = position of point in yz plane """ return Vector((0, 1)), 1 """ NOTE (to myself): In theory this is how it has to be done so sections follow path, but in real world results are better when sections are z-up. So return a dumb 1 so f(y) = y """ if next is None: dz = dz0 / self.length else: dz = (dz1 + dz0) / (self.length + next.length) return Vector((0, 1)), sqrt(1 + dz * dz) # 1 / sqrt(1 + (dz0 / self.length) * (dz0 / self.length)) if next is None: return Vector((-dz0, self.length)).normalized(), 1 v0 = Vector((self.length, dz0)) v1 = Vector((next.length, dz1)) direction = Vector((-dz0, self.length)).normalized() + Vector((-dz1, next.length)).normalized() adj = v0 * v1 hyp = (v0.length * v1.length) c = min(1, max(-1, adj / hyp)) size = -cos(pi - 0.5 * acos(c)) return direction.normalized(), size
def add_torus(self, majSeg, minSeg, majRad, minRad): lv = [] circ = math.pi*2 majCirc = circ/majSeg minCirc = circ/minSeg index = 0 rings = [] for maj in range(majSeg): majTheta = majCirc*maj dx = math.cos(majTheta) * majRad dy = math.sin(majTheta) * majRad n = Vector((dx, dy, 0)) minorRing = [] for min in range(minSeg): minTheta = minCirc*min dn = math.cos(minTheta) * minRad dz = math.sin(minTheta) * minRad co = n + n.normalized() * dn + Vector((0, 0, dz)) co = co.to_tuple() lv.append(self.new_vertex((Vector((co))))) minorRing.append(index) index += 1 rings.append(minorRing) for ri in range(len(rings)-1): ring = rings[ri] nextRing = rings[ri+1] for i in range(len(ring)-1): self.new_face([lv[ring[i]], lv[nextRing[i]], lv[nextRing[i+1]], lv[ring[i+1]]]) self.new_face([lv[ring[0]], lv[ring[len(ring)-1]], lv[nextRing[len(nextRing)-1]], lv[nextRing[0]]]) ring = rings[len(rings)-1] nextRing = rings[0] for i in range(len(ring)-1): self.new_face([lv[ring[i]], lv[nextRing[i]], lv[nextRing[i+1]], lv[ring[i+1]]]) self.new_face([lv[ring[0]], lv[ring[len(ring)-1]], lv[nextRing[len(nextRing)-1]], lv[nextRing[0]]])
def rot_axis_quat(vector1, vector2): """ Find the rotation (quaternion) from vector 1 to vector 2""" vector1 = vector1.normalized() vector2 = vector2.normalized() cosTheta = vector1.dot(vector2) rotationAxis = Vector((0.0, 0.0, 0.0)) if (cosTheta < -1 + 0.001): v = Vector((0.0, 1.0, 0.0)) #Get the vector at the right angles to both rotationAxis = vector1.cross(v) rotationAxis = rotationAxis.normalized() q = Quaternion() q.w = 0.0 q.x = rotationAxis.x q.y = rotationAxis.y q.z = rotationAxis.z else: rotationAxis = vector1.cross(vector2) s = math.sqrt((1.0 + cosTheta) * 2.0) invs = 1 / s q = Quaternion() q.w = s * 0.5 q.x = rotationAxis.x * invs q.y = rotationAxis.y * invs q.z = rotationAxis.z * invs return q
def proj_z(self, t, dz0, next=None, dz1=0): """ length of projection along crossing line / circle deformation unit vector for profil in z axis at line / line intersection so f(y) = position of point in yz plane """ return Vector((0, 1)), 1 """ NOTE (to myself): In theory this is how it has to be done so sections follow path, but in real world results are better when sections are z-up. So return a dumb 1 so f(y) = y """ if next is None: dz = dz0 / self.length else: dz = (dz1 + dz0) / (self.length + next.length) return Vector((0, 1)), sqrt(1 + dz * dz) # 1 / sqrt(1 + (dz0 / self.length) * (dz0 / self.length)) if next is None: return Vector((-dz0, self.length)).normalized(), 1 v0 = Vector((self.length, dz0)) v1 = Vector((next.length, dz1)) direction = Vector((-dz0, self.length)).normalized() + Vector( (-dz1, next.length)).normalized() adj = v0 * v1 hyp = (v0.length * v1.length) c = min(1, max(-1, adj / hyp)) size = -cos(pi - 0.5 * acos(c)) return direction.normalized(), size
def get_custom_normals(bmesh_, use_edge_angle, split_angle): float_normals = [] for face in bmesh_.faces: if not face.smooth: for vertex in face.verts: float_normals.extend(face.normal.normalized()) else: for vertex in face.verts: v_normals = [[face.normal.normalized(), face.calc_area()]] for link_face in vertex.link_faces: if face.index == link_face.index: continue if link_face.smooth: if not use_edge_angle: v_normals.append([link_face.normal.normalized(), link_face.calc_area()]) elif use_edge_angle: face_angle = face.normal.normalized().dot(link_face.normal.normalized()) face_angle = min(1.0, max(face_angle, -1.0)) face_angle = math.acos(face_angle) if face_angle < split_angle: v_normals.append([link_face.normal.normalized(), link_face.calc_area()]) smooth_normal = Vector() area_sum = 0 for vertex_normal in v_normals: area_sum += vertex_normal[1] for vertex_normal in v_normals: if area_sum: smooth_normal += vertex_normal[0] * \ (vertex_normal[1] / area_sum) float_normals.extend(smooth_normal.normalized()) return float_normals
def average_normals(normalslist): avg = Vector() for n in normalslist: avg += n return avg.normalized()
def execute(self, context): # FIXME: Undo is inconsistent. # FIXME: Would be nicer if rotate could pick some object-local axis. from mathutils import Vector print_3d = context.scene.print_3d face_areas = print_3d.use_alignxy_face_area self.context = context mode_orig = context.mode skip_invalid = [] for obj in context.selected_objects: orig_loc = obj.location.copy() orig_scale = obj.scale.copy() # When in edit mode, do as the edit mode does. if mode_orig == 'EDIT_MESH': bm = bmesh.from_edit_mesh(obj.data) faces = [f for f in bm.faces if f.select] else: faces = [p for p in obj.data.polygons if p.select] if not faces: skip_invalid.append(obj.name) continue # Rotate object so average normal of selected faces points down. normal = Vector((0.0, 0.0, 0.0)) if face_areas: for face in faces: normal += (face.normal * face.calc_area()) else: for face in faces: normal += face.normal normal = normal.normalized() normal.rotate(obj.matrix_world) # local -> world. offset = normal.rotation_difference(Vector((0.0, 0.0, -1.0))) offset = offset.to_matrix().to_4x4() obj.matrix_world = offset @ obj.matrix_world obj.scale = orig_scale obj.location = orig_loc if len(skip_invalid) > 0: for name in skip_invalid: print( f"Align to XY: Skipping object {name}. No faces selected.") if len(skip_invalid) == 1: self.report( {'WARNING'}, f"Skipping object {skip_invalid[0]}. No faces selected.") else: self.report( {'WARNING'}, f"Skipping some objects. No faces selected. See terminal.") return {'FINISHED'}
def to_point(self, amplitude, coefficient, vertex, centers, direction): vertex = Vector(vertex) vectors = [] for center in centers: vector = Vector(center) - vertex vector = self.falloff(amplitude, coefficient, vector.length) * vector.normalized() vectors.append(vector) result = get_avg_vector(vectors) return result.length, result.normalized()
def execute(self, context): scene = context.scene for o in scene.objects: o.select_set(False) surf = bpy.data.objects['Mask_Surface'] surf.select_set(True) bpy.context.view_layer.objects.active = surf thickness = surf.modifiers['Thickness'].thickness for m in surf.modifiers: try: bpy.ops.object.modifier_apply(apply_as='DATA', modifier=m.name) except: pass surf.data.update() me = surf.data n_verts = len(me.vertices) n_half = int(n_verts / 2) for i in range(n_half): v0 = me.vertices[i] v1 = me.vertices[i + n_half] v2 = v1.co - v0.co v2 = Vector((v2.x, v2.y, 0)) v1.co = v2.normalized() * thickness + v0.co filter = bpy.data.objects['Filter'] filter.select_set(True) bpy.context.view_layer.objects.active = filter bpy.ops.object.mode_set(mode='OBJECT') bpy.ops.object.duplicate_move() ob = context.object ob.name = 'Mask' for m in ob.modifiers: m.show_viewport = True for m in ob.modifiers: try: bpy.ops.object.modifier_apply(apply_as='DATA', modifier=m.name) except: ob.modifiers.remove(m) bpy.ops.object.location_clear(clear_delta=False) bpy.ops.object.rotation_clear(clear_delta=False) bpy.ops.view3d.view_selected() for o in scene.objects: o.hide_viewport = True ob.hide_viewport = False ob.select_set(True) bpy.ops.object.mode_set(mode='EDIT') bpy.context.space_data.overlay.show_statvis = True bpy.context.scene.tool_settings.statvis.type = 'OVERHANG' bpy.ops.mesh.select_mode(use_extend=False, use_expand=False, type='FACE') bpy.ops.mesh.select_all(action='DESELECT') bpy.context.scene.tool_settings.use_snap = False return {'FINISHED'}
def getTNBfromVector(v): v = Vector(v) N = v.normalized() B = N.cross((0, 0, -1)) if (B.length == 0): B, T = Vector((1, 0, 0)), Vector((0, 1, 0)) else: B.normalize() T = N.cross(B).normalized() return T, N, B
def to_point(self, amplitude, coefficient, vertex, centers, direction): vertex = Vector(vertex) n = len(centers) if self.point_mode == 'AVG' or n <= 1: vectors = [] for center in centers: vector = Vector(center) - vertex vector = self.falloff(amplitude, coefficient, vector.length) * vector.normalized() vectors.append(vector) result = get_avg_vector(vectors) return result.length, result.normalized() else: kdt = KDTree(n) for i, center in enumerate(centers): kdt.insert(Vector(center), i) kdt.balance() nearest_co, nearest_idx, nearest_distance = kdt.find(vertex) vector = nearest_co - vertex coeff = self.falloff(amplitude, coefficient, nearest_distance) return coeff, vector.normalized()
def spread_array(bm, split_edges, split_faces, max_width, prop): """perform spreading for array faces""" if prop.count == 1: return normal = split_faces[0].normal.copy() median = calc_faces_median(split_faces) right = normal.cross(VEC_DOWN) split_edges = sort_edges(split_edges, right) split_faces = sort_faces(split_faces, right) # -- map each split edge to its neighbouring faces edge_neighbour_face_map = { edge: [split_faces[idx], split_faces[idx + 1]] for idx, edge in enumerate(split_edges) } def get_all_splitface_verts(f): corner_verts = list(f.verts) split_verts = [] for v in corner_verts: split_edge = [e for e in v.link_edges if e not in f.edges].pop() if edge_is_vertical(split_edge): split_verts.append(split_edge.other_vert(v)) return corner_verts + split_verts # XXX Fixme if you can # HACK(ranjian0) Setting spread to 1.0 causes multigroup jitters prop.spread = clamp(prop.spread, -1, 0.9999) # -- spread the array faces for f in split_faces: fm = f.calc_center_median() vts = get_all_splitface_verts(f) diff = Vector((fm - median).to_tuple(3)) spread_factor = (max_width - prop.width) * (diff.length / max_width) if prop.spread > 0: spread_factor /= prop.count - 1 bmesh.ops.translate(bm, verts=vts, vec=diff.normalized() * prop.spread * spread_factor) # -- move the split edges to the middle of their neighbour faces for edge in split_edges: neighbours = edge_neighbour_face_map[edge] nmedian = calc_faces_median(neighbours) diff = nmedian - calc_edge_median(edge) diff.z = 0 # XXX prevent vertical offset from influencing split edges bmesh.ops.translate(bm, verts=edge.verts, vec=diff)
def initialize_from_gp(self, context): mat = self.matrix.inverted() frame = get_gp_frame(context) seen_verts = set() if frame: for stroke in frame.strokes: le = len(stroke.points) for i in range(le - 2): p0 = mat @ stroke.points[i].co p1 = mat @ stroke.points[i + 1].co p2 = mat @ stroke.points[i + 2].co d = p0 - p1 d += p1 - p2 location, normal, index, dist = self.bvh.find_nearest(p1) face = self.bm.faces[index] vert = min(face.verts, key=lambda v: (v.co - p1).length_squared) self.field[vert.index] = d.normalized() self.weights[vert.index] = 0 seen_verts.add(vert) current_front = set() for vert in seen_verts: for edge in vert.link_edges: other = edge.other_vert(vert) if other not in seen_verts: current_front.add(vert) while current_front: new_front = set() for vert in current_front: d = Vector() tot = 0 for edge in vert.link_edges: other = edge.other_vert(vert) if other in seen_verts: if not tot: d = Vector(self.field[other.index]) else: d += best_matching_vector( symmetry_space(self.field[other.index], other.normal), d ) tot += 1 else: new_front.add(other) self.weights[other.index] = self.weights[vert.index] + 1 if tot: self.field[vert.index] = d.normalized().cross(vert.normal) seen_verts |= current_front new_front -= seen_verts current_front = new_front self.weights /= self.weights.max()
def generate_one(self, v1, v2, dv): dv = Vector(dv) size = dv.length dv = dv.normalized() orth = dv.orthogonal() arr1 = 0.1 * size * (orth - dv) arr2 = 0.1 * size * (-orth - dv) v3 = tuple(Vector(v2) + arr1) v4 = tuple(Vector(v2) + arr2) verts = [v1, v2, v3, v4] edges = [[0, 1], [1, 2], [1, 3]] return verts, edges
def orthogonalize(x: mathutils.Vector): w = x.normalized() if abs(w.x) >= abs(w.y): # W.x or W.z is the largest magnitude component, swap them invLength = 1.0 / w.xz.length u = mathutils.Vector((-w.z * invLength, 0.0, w.x * invLength)) else: # W.y or W.z is the largest magnitude component, swap them invLength = 1.0 / w.yz.length u = mathutils.Vector((0.0, w.z * invLength, -w.y * invLength)) v = w.cross(u) return u, v, w
def pass_line(vecs, is_closed_line): line_length = 0.0 line_data = [] vecs_len = len(vecs) for i, vec in enumerate(vecs): #if i == vecs_len - 1 and is_closed_line is False: #line_data.append((vec, line_length, 0.0, None)) #else: vec_area = None if i == vecs_len - 1: if is_closed_line: vec_area = vecs[0] - vec else: vec_area = Vector( (0.0, 0.0, 0.0) ) else: vec_area = vecs[i+1] - vec area_length = vec_area.length vec_dir = None if i == vecs_len - 1: vec_dir = (vec - vecs[i-1]).normalized() else: vec_dir = vec_area.normalized() line_data.append((vec.copy(), line_length, area_length, vec_dir)) line_length += area_length # last point line of closed curve if is_closed_line: vec_area = vecs[0] - vecs[-1] area_length = vec_area.length vec_dir = vec_area.normalized() line_data.append((vecs[0], line_length, 0.0, None)) return line_data
def pass_line(vecs, is_closed_line): line_length = 0.0 line_data = [] vecs_len = len(vecs) for i, vec in enumerate(vecs): #if i == vecs_len - 1 and is_closed_line is False: #line_data.append((vec, line_length, 0.0, None)) #else: vec_area = None if i == vecs_len - 1: if is_closed_line: vec_area = vecs[0] - vec else: vec_area = Vector((0.0, 0.0, 0.0)) else: vec_area = vecs[i + 1] - vec area_length = vec_area.length vec_dir = None if i == vecs_len - 1: vec_dir = (vec - vecs[i - 1]).normalized() else: vec_dir = vec_area.normalized() line_data.append((vec.copy(), line_length, area_length, vec_dir)) line_length += area_length # last point line of closed curve if is_closed_line: vec_area = vecs[0] - vecs[-1] area_length = vec_area.length vec_dir = vec_area.normalized() line_data.append((vecs[0], line_length, 0.0, None)) return line_data
def get_frame(p, as_matrix=False): p = Vector(p) N = p.normalized() B = N.cross((0, 0, -1)) if (B.length == 0): B, T = Vector((1, 0, 0)), Vector((0, 1, 0)) else: B.normalize() T = N.cross(B).normalized() if as_matrix: return Matrix([T, B, N]).to_4x4().transposed() else: return T, N, B
def execute(self, context): if bpy.context.object.type != 'MESH': self.report({'WARNING'}, 'Active object must be a mesh') return {'CANCELLED'} depsgraph = context.evaluated_depsgraph_get() mesh = bmesh.new() mesh.from_object(bpy.context.object, depsgraph, deform=True, cage=False, face_normals=True) mesh.transform(bpy.context.object.matrix_world) toolpath = internal.addObject('CURVE', 'Slices Toolpath') pitch_axis = Vector(self.pitch_axis) axis = pitch_axis.normalized() for i in range(0, self.slice_count): aux_mesh = mesh.copy() cut_geometry = bmesh.ops.bisect_plane( aux_mesh, geom=aux_mesh.edges[:] + aux_mesh.faces[:], dist=0, plane_co=pitch_axis * i + axis * self.offset, plane_no=axis, clear_outer=False, clear_inner=False)['geom_cut'] edge_pool = set( [e for e in cut_geometry if isinstance(e, bmesh.types.BMEdge)]) while len(edge_pool) > 0: current_edge = edge_pool.pop() first_vertex = current_vertex = current_edge.verts[0] vertices = [current_vertex.co] follow_edge_loop = len(edge_pool) > 0 while follow_edge_loop: current_vertex = current_edge.other_vert(current_vertex) vertices.append(current_vertex.co) if current_vertex == first_vertex: break follow_edge_loop = False for edge in current_vertex.link_edges: if edge in edge_pool: current_edge = edge edge_pool.remove(current_edge) follow_edge_loop = True break current_vertex = current_edge.other_vert(current_vertex) vertices.append(current_vertex.co) internal.addPolygonSpline(toolpath, False, vertices) aux_mesh.free() mesh.free() return {'FINISHED'}
def RT_render_scene(scene, width, height, depth, buf): """Main function for rendering the scene Parameters ---------- scene : bpy.types.Scene The scene that will be rendered It stores information about the camera, lights, objects, and material width : int Width of the rendered image height : int Height of the rendered image depth : int The recursion depth of raytracing i.e. the number that light bounces in the scene buf: numpy.ndarray the buffer that will be populated to store the calculated color for each pixel """ # get all the lights from the scene scene_lights = [o for o in scene.objects if o.type == "LIGHT"] # get the location and orientation of the active camera cam_location = scene.camera.location cam_orientation = scene.camera.rotation_euler # get camera focal length focal_length = scene.camera.data.lens / scene.camera.data.sensor_width aspect_ratio = height / width # iterate through all the pixels, cast a ray for each pixel for y in range(height): # get screen space coordinate for y screen_y = ((y - (height / 2)) / height) * aspect_ratio for x in range(width): # get screen space coordinate for x screen_x = (x - (width / 2)) / width # calculate the ray direction ray_dir = Vector((screen_x, screen_y, -focal_length)) ray_dir.rotate(cam_orientation) ray_dir = ray_dir.normalized() # populate the RGB component of the buffer with ray tracing result buf[y, x, 0:3] = RT_trace_ray(scene, cam_location, ray_dir, scene_lights, depth) # populate the alpha component of the buffer # to make the pixel not transparent buf[y, x, 3] = 1 yield y return buf
def to_plane(self, amplitude, coefficient, vertex, centers, direction): center = Vector(centers[0]) direction = Vector(direction) vertex = Vector(vertex) dirlength = direction.length if dirlength <= 0: raise ValueError("Direction vector must have nonzero length!") d = -direction.dot(center) # distance from vertex to plane rho = abs(vertex.dot(direction) + d) / dirlength from_center = center - vertex # vector is either direction or negative direction if from_center.dot(direction) >= 0: vector = direction.normalized() else: # for some reason mathutil's Vector does not have .negated() # thankfully we do not need direction itself anymore. direction.negate() vector = direction.normalized() return self.falloff(amplitude, coefficient, rho), vector
def _do_rgb_to_dxyz_w_calc(overload: "ParsedLightOverload") -> None: _replace_columns_via_values(overload, { "DX": overload["R"], "DY": overload["G"], "DZ": overload["B"] }) dir_vec = Vector((overload["DX"], overload["DY"], overload["DZ"])) overload["WIDTH"] = 1 - dir_vec.magnitude dvn = dir_vec.normalized() _replace_columns_via_values(overload, { "DX": dvn[0], "DY": dvn[1], "DZ": dvn[2] }) _replace_columns_via_values(overload, {"R": 1, "G": 1, "B": 1})
def execute(self, context): mesh = bmesh.new() aux_mesh = bpy.context.object.to_mesh(bpy.context.depsgraph, apply_modifiers=True, calc_undeformed=False) mesh.from_mesh(aux_mesh) mesh.transform(bpy.context.object.matrix_world) bpy.data.meshes.remove(aux_mesh) internal.addCurveObject( 'Toolpath').location = bpy.context.scene.cursor_location pitch_axis = Vector(self.pitch_axis) axis = pitch_axis.normalized() for i in range(0, self.slice_count): aux_mesh = mesh.copy() cut_geometry = bmesh.ops.bisect_plane( aux_mesh, geom=aux_mesh.edges[:] + aux_mesh.faces[:], dist=0, plane_co=pitch_axis * i + axis * self.offset, plane_no=axis, clear_outer=False, clear_inner=False)['geom_cut'] edge_pool = set( [e for e in cut_geometry if isinstance(e, bmesh.types.BMEdge)]) while len(edge_pool) > 0: current_edge = edge_pool.pop() first_vertex = current_vertex = current_edge.verts[0] vertices = [current_vertex.co] follow_edge_loop = len(edge_pool) > 0 while follow_edge_loop: current_vertex = current_edge.other_vert(current_vertex) vertices.append(current_vertex.co) if current_vertex == first_vertex: break follow_edge_loop = False for edge in current_vertex.link_edges: if edge in edge_pool: current_edge = edge edge_pool.remove(current_edge) follow_edge_loop = True break current_vertex = current_edge.other_vert(current_vertex) vertices.append(current_vertex.co) internal.addPolygonSpline(bpy.context.object, False, vertices) aux_mesh.free() return {'FINISHED'}
def to_line(self, amplitude, coefficient, vertex, centers, direction): center = Vector(centers[0]) direction = Vector(direction) dirlength = direction.length if dirlength <= 0: raise ValueError("Direction vector must have nonzero length!") vertex = Vector(vertex) to_center = center - vertex # cosine of angle between to_center and direction cos_phi = to_center.dot(direction) / (to_center.length * dirlength) # projection of to_center on direction to_center_projection = to_center.length * cos_phi * direction.normalized() # projection of vertex on direction projection = center - to_center_projection vector = projection - vertex return self.falloff(amplitude, coefficient, vector.length), vector.normalized()
def add_torus(self, majSeg, minSeg, majRad, minRad): lv = [] circ = math.pi * 2 majCirc = circ / majSeg minCirc = circ / minSeg index = 0 rings = [] for maj in range(majSeg): majTheta = majCirc * maj dx = math.cos(majTheta) * majRad dy = math.sin(majTheta) * majRad n = Vector((dx, dy, 0)) minorRing = [] for min in range(minSeg): minTheta = minCirc * min dn = math.cos(minTheta) * minRad dz = math.sin(minTheta) * minRad co = n + n.normalized() * dn + Vector((0, 0, dz)) co = co.to_tuple() lv.append(self.new_vertex((Vector((co))))) minorRing.append(index) index += 1 rings.append(minorRing) for ri in range(len(rings) - 1): ring = rings[ri] nextRing = rings[ri + 1] for i in range(len(ring) - 1): self.new_face([ lv[ring[i]], lv[nextRing[i]], lv[nextRing[i + 1]], lv[ring[i + 1]] ]) self.new_face([ lv[ring[0]], lv[ring[len(ring) - 1]], lv[nextRing[len(nextRing) - 1]], lv[nextRing[0]] ]) ring = rings[len(rings) - 1] nextRing = rings[0] for i in range(len(ring) - 1): self.new_face([ lv[ring[i]], lv[nextRing[i]], lv[nextRing[i + 1]], lv[ring[i + 1]] ]) self.new_face([ lv[ring[0]], lv[ring[len(ring) - 1]], lv[nextRing[len(nextRing) - 1]], lv[nextRing[0]] ])
def torus(majSeg, minSeg, majRad, minRad): lp = [] lf = [] circ = math.pi * 2 majCirc = circ / majSeg minCirc = circ / minSeg index = 0 rings = [] for maj in range(majSeg): majTheta = majCirc * maj dx = math.cos(majTheta) * majRad dy = math.sin(majTheta) * majRad n = Vector((dx, dy, 0)) minorRing = [] for min in range(minSeg): minTheta = minCirc * min dn = math.cos(minTheta) * minRad dz = math.sin(minTheta) * minRad co = n + n.normalized() * dn + Vector((0, 0, dz)) co = co.to_tuple() lp.append(co) minorRing.append(index) index += 1 rings.append(minorRing) for ri in range(len(rings) - 1): ring = rings[ri] nextRing = rings[ri + 1] for i in range(len(ring) - 1): lf.append((ring[i], nextRing[i], nextRing[i + 1], ring[i + 1])) lf.append((ring[0], ring[len(ring) - 1], nextRing[len(nextRing) - 1], nextRing[0])) ring = rings[len(rings) - 1] nextRing = rings[0] for i in range(len(ring) - 1): lf.append((ring[i], nextRing[i], nextRing[i + 1], ring[i + 1])) lf.append((ring[0], ring[len(ring) - 1], nextRing[len(nextRing) - 1], nextRing[0])) return lp, lf
def torus(majSeg, minSeg, majRad, minRad): lp = [] lf = [] circ = math.pi*2 majCirc = circ/majSeg minCirc = circ/minSeg index = 0 rings = [] for maj in range(majSeg): majTheta = majCirc*maj dx = math.cos(majTheta) * majRad dy = math.sin(majTheta) * majRad n = Vector((dx, dy, 0)) minorRing = [] for min in range(minSeg): minTheta = minCirc*min dn = math.cos(minTheta) * minRad dz = math.sin(minTheta) * minRad co = n + n.normalized() * dn + Vector((0, 0, dz)) co = co.to_tuple() lp.append(co) minorRing.append(index) index += 1 rings.append(minorRing) for ri in range(len(rings)-1): ring = rings[ri] nextRing = rings[ri+1] for i in range(len(ring)-1): lf.append((ring[i], nextRing[i], nextRing[i+1], ring[i+1])) lf.append((ring[0], ring[len(ring)-1], nextRing[len(nextRing)-1], nextRing[0])) ring = rings[len(rings)-1] nextRing = rings[0] for i in range(len(ring)-1): lf.append((ring[i], nextRing[i], nextRing[i+1], ring[i+1])) lf.append((ring[0], ring[len(ring)-1], nextRing[len(nextRing)-1], nextRing[0])) return lp, lf
def bevel(ob, follow, beveltype): me = ob.data # dict vvdict = vert_vert_dict(me, sel=0) vedict = vert_edge_dict(me, sel=0) vfdict = vert_face_dict(me, sel=0) efdict = edge_face_dict(me, sel=0) kedict = key_edge_dict_old(me, sel=0) bevelverts = [] bevelvertindex = len(me.vertices) bevelfaces = bfaces = [] bevelfaces_material = bfmats = [] # ekparallel: 既存の辺からbevelされた辺を参照。面を張る為にkeyをsortedしない ekparallel = [[] for e in me.edges] vvparallel = [set() for v in me.vertices] #共通の頂点を探したりする '''vevparallel = {v.index:{kedict[key]:None for key in vedict[v.index]} \ for v in me.vertices} ''' vevparallel = {} for v in me.vertices: if not v.hide: evdict = {} for key in vedict[v.index]: evdict[kedict[key]] = None vevparallel[v.index] = evdict # bevelで削除される予定頂点から生成される辺を参照 vkparallel = {v.index:[] for v in me.vertices} vertflags = [0 for v in me.vertices] #edgeflags = [e.index for e in me.edges] faceflags = [0 for f in me.faces] # flag作り for i in range(len(me.vertices)): # hiddenも処理 if me.vertices[i].hide: continue tmp = [0, 0] for key in vedict[i]: if len(efdict[key]) == 0: tmp[0] += 1 else: tmp[1] += 1 if sum(tmp) == 0: vertflags[i] |= VERT elif tmp[0] >= 1 and tmp[1] == 0: vertflags[i] |= EDGE elif tmp[0] == 0 and tmp[1] >= 1: vertflags[i] |= FACE else: vertflags[i] |= MIX # flag作り2 for e in me.edges: if e.key in efdict: if len(efdict[e.key]) > 2: vertflags[e.key[0]] |= MIX vertflags[e.key[1]] |= MIX # Face for face in me.faces: if face.hide: continue findex = face.index material = face.material_index vcor = {i:[] for i in face.vertices} # 新旧対応 correspond vflags = {i:0 for i in face.vertices} edge_keys = face.edge_keys # 頂点ループ? fverts = list(face.vertices) for vindex in fverts: vert = me.vertices[vindex] if not vert.select: continue if vertflags[vindex] & MIX: continue # 順番を考慮 #connected_edges = [me.edges[kedict[key]] for key in edge_keys # if key in vedict[vindex]] connected_edges = [key for key in edge_keys if vindex in key] if connected_edges == [edge_keys[0], edge_keys[-1]]: connected_edges.reverse() #if edge_keys.index(connected_edges[0]) == 0 and \ # edge_keys.index(connected_edges[1]) == len(edge_keys) - 1: # connected_edges.reverse() e1, e2 = [me.edges[kedict[key]] for key in connected_edges] key1 = e1.key key2 = e2.key v1i = the_other(key1, vindex) v2i = the_other(key2, vindex) va0 = vert.co va1 = me.vertices[v1i].co # edge1 va2 = me.vertices[v2i].co # edge2 vac = face.center vr01 = va1 - va0 vr02 = va2 - va0 vr0c = vac - va0 if beveltype == 'vert': vflags[vindex] = VERT elif e1.select and e2.select: tmp = [len(efdict[e1.key]), len(efdict[e2.key])] if tmp[0] == 1 and tmp[1] == 1: vflags[vindex] = VERT elif tmp[0] == 2 and tmp[1] == 1: ea, eb, vra, vrb = e2, e1, vr02, vr01 vflags[vindex] = EDGE elif tmp[0] == 1 and tmp[1] == 2: ea, eb, vra, vrb = e1, e2, vr01, vr02 vflags[vindex] = EDGE else: vflags[vindex] = FACE elif not e1.select and not e2.select: vflags[vindex] = VERT else: eb = e1 if e1.select else e2 if len(efdict[eb.key]) == 1: vflags[vindex] = VERT else: vflags[vindex] = EDGE if e1.select: ea, eb, vra, vrb = e2, e1, vr02, vr01 else: ea, eb, vra, vrb = e1, e2, vr01, vr02 if vflags[vindex] == FACE: # 両方のエッジが選択 # エッジの角度が180度以上になる場合は予期しない結果になる vr010c_cross = vr01.cross(vr0c).normalized() angle = vr01.angle(vr02) q = axis_angle_to_quat(vr010c_cross, angle / 2) v = q * vr01 v.normalize() if angle > SMALL_NUMBER: s = math.sin(angle / 2) v *= 1.0 / s else: v = Vector((0, 0, 0)) #co = va0 + v # absolute coordinate co = va0.copy() bevelvert = BVert(v, co, bevelvertindex, vindex, e1.index, findex)# e1, e2どちらを優先しても可 bevelvert.ebi = e2.index bevelvert.f = FACE bevelverts.append(bevelvert) vcor[vindex].append(bevelvert.vi) # == bevelvertindex #vflags[vindex] = FACE bevelvertindex += 1 elif vflags[vindex] == VERT: # 両方のエッジが非選択 # 頂点をエッジに沿って分割 for ea, eb, v in [(e1, e2, vr01), (e2, e1, vr02)]: vei = vevparallel[vindex][ea.index] if not vei: v = v.normalized() #co = va0 + v co = va0.copy() bevelvert = BVert(v, co, bevelvertindex, vindex, ea.index, findex) bevelvert.ebi = eb.index # bevelされた辺 bevelvert.f = VERT bevelverts.append(bevelvert) vevparallel[vindex][ea.index] = bevelvert.vi bevelvertindex += 1 else: bevelvert = bevelverts[vei - len(me.vertices)] bevelvert.eb2i = eb.index bevelvert.f2i = findex vcor[vindex].append(bevelvert.vi) #vflags[vindex] = VERT else: # 片側のエッジが選択 # 非選択エッジのみに頂点追加 ''' if e1.select: ea, eb, vra, vrb = e2, e1, vr02, vr01 else: ea, eb, vra, vrb = e1, e2, vr01, vr02 ''' vra = vra.normalized() vrb = vrb.normalized() angle = vra.angle(vrb) if angle > SMALL_NUMBER: v = vra / math.sin(angle) #co = va0 + v else: v = Vector([0, 0, 0]) co = va0.copy() vei = vevparallel[vindex][ea.index] if not vei: bevelvert = BVert(v, co, bevelvertindex, vindex, ea.index, findex) bevelvert.ebi = eb.index # bevelされた辺 bevelvert.f = EDGE bevelverts.append(bevelvert) vevparallel[vindex][ea.index] = bevelvert.vi bevelvertindex += 1 else: bevelvert = bevelverts[vei - len(me.vertices)] bevelvert.vec = (v + bevelvert.vec) / 2 # 平均化 #bevelvert.co = va0 + bevelvert.vec bevelvert.eb2i = eb.index bevelvert.f2i = findex vcor[vindex].append(bevelvert.vi) #vflags[vindex] = EDGE # vvparallel for k, verts in vcor.items(): for v in verts: vvparallel[k].add(v) if sum(vflags.values()) == 0: # 選択頂点無し continue # 面作成、辺・頂点削除指定 # 元頂点のflag vflags vfl = list(vflags.values()) vf = (vfl.count(VERT), vfl.count(EDGE), vfl.count(FACE)) if len(face.vertices) == 4: if vf[0] == 1: v0 = [k for k, v in vflags.items() if v == VERT][0] v0_i = fverts.index(v0) v1 = fverts[v0_i - 3] # next v2 = fverts[v0_i - 2] # diagonal v3 = fverts[v0_i - 1] # prev if vf == (1, 0, 0): # *** CAUTION *** # #bfaces.append([vcor[v0][0], vcor[v0][1]], v1, v3) #上記の順だと上手く張れない面が出る bfaces.append([v1, v3, vcor[v0][0], vcor[v0][1]]) bfaces.append([v1, v2, v3]) # material bfmats.extend([material, material]) # delete vertflags[v0] |= DELETE elif vf == (1, 2, 0): if vflags[v1] == EDGE: bfaces.append([vcor[v0][0], vcor[v0][1], vcor[v1][0], v3]) bfaces.append([vcor[v1][0], vcor[v2][0], v3]) # material bfmats.extend([material, material]) # ekparallel key = [vcor[v1][0], vcor[v2][0]] dkey = tuple(sorted([v1, v2])) ekparallel[kedict[dkey]].append(key) # delete vertflags[v1] |= DELETE vertflags[v2] |= DELETE else: bfaces.append([vcor[v0][0], vcor[v0][1], v1, vcor[v3][0]]) bfaces.append([v1, vcor[v2][0], vcor[v3][0]]) # material bfmats.extend([material, material]) # ekparallel key = [vcor[v2][0], vcor[v3][0]] dkey = tuple(sorted([v2, v3])) ekparallel[kedict[dkey]].append(key) # delete vertflags[v2] |= DELETE vertflags[v3] |= DELETE else: # vf == (1,2,1) bfaces.append([vcor[v0][0], vcor[v0][1], vcor[v1][0], vcor[v3][0]]) bfaces.append([vcor[v1][0], vcor[v2][0], vcor[v3][0]]) # material bfmats.extend([material, material]) # ekparallel for i, j in [[v1, v2], [v2, v3]]: key = [vcor[i][0], vcor[j][0]] dkey = tuple(sorted([i, j])) ekparallel[kedict[dkey]].append(key) # delete for i in [v1, v2, v3]: vertflags[i] |= DELETE # vkparallel vkparallel[v0].append([vcor[v0][1], vcor[v0][0]]) # delete vertflags[v0] |= DELETE elif vf[0] == 2: v0 = [k for k, v in vflags.items() if v == VERT][0] v0_i = fverts.index(v0) v1 = fverts[v0_i - 3] v2 = fverts[v0_i - 2] v3 = fverts[v0_i - 1] if EDGE not in vfl: # vf == (2, 0, 0) if vflags[v1] == VERT: bfaces.append([vcor[v0][0], vcor[v0][1], vcor[v1][0], vcor[v1][1]]) bfaces.append([vcor[v0][0], vcor[v1][1], v2, v3]) # material bfmats.extend([material, material]) # vkparallel vkparallel[v1].append([vcor[v1][1], vcor[v1][0]]) # delete vertflags[v1] |= DELETE elif vflags[v3] == VERT: bfaces.append([vcor[v3][0], vcor[v3][1], vcor[v0][0], vcor[v0][1]]) bfaces.append([vcor[v3][0], vcor[v0][1], v1, v2]) bfmats.extend([material, material]) # vkparallel vkparallel[v3].append([vcor[v3][1], vcor[v3][0]]) # delete vertflags[v3] |= DELETE elif vflags[v2] == VERT: # diagonal bfaces.append([vcor[v0][0], vcor[v0][1], v1, v3]) bfaces.append([vcor[v2][0], vcor[v2][1], v3, v1]) # material bfmats.extend([material, material]) # vkparallel vkparallel[v2].append([vcor[v2][1], vcor[v2][0]]) # delete vertflags[v2] |= DELETE # delete vertflags[v0] |= DELETE else: # vf == (2, 2, 0) if vflags[v1] == VERT: bfaces.append([vcor[v0][0], vcor[v0][1], vcor[v1][0], vcor[v1][1]]) bfaces.append([vcor[v0][1], vcor[v1][0], vcor[v2][0], vcor[v3][0]]) # material bfmats.extend([material, material]) # vkparallel vkparallel[v1].append([vcor[v1][1], vcor[v1][0]]) # ekparallel key = [vcor[v2][0], vcor[v3][0]] dkey = tuple(sorted([v2, v3])) ekparallel[kedict[dkey]].append(key) if vflags[v3] == VERT: bfaces.append([vcor[v3][0], vcor[v3][1], vcor[v0][0], vcor[v0][1]]) bfaces.append([vcor[v3][0], vcor[v0][1], vcor[v1][0], vcor[v2][0]]) # material bfmats.extend([material, material]) # vkparallel vkparallel[v3].append([vcor[v3][1], vcor[v3][0]]) # ekparallel key = [vcor[v1][0], vcor[v2][0]] dkey = tuple(sorted([v1, v2])) ekparallel[kedict[dkey]].append(key) # delete for i in fverts: vertflags[i] |= DELETE # vkparallel vkparallel[v0].append([vcor[v0][1], vcor[v0][0]]) elif vf == (3, 0, 0): v0 = [k for k, v in vflags.items() if v == 0][0] v0_i = fverts.index(v0) v1 = fverts[v0_i - 3] v2 = fverts[v0_i - 2] v3 = fverts[v0_i - 1] bfaces.append([v0, vcor[v1][0], vcor[v3][1]]) bfaces.append([vcor[v1][0], vcor[v1][1], vcor[v3][0], vcor[v3][1]]) bfaces.append([vcor[v1][1], vcor[v2][0], vcor[v2][1], vcor[v3][0]]) # material bfmats.extend([material, material, material]) # vkparallel for i in [v1, v2, v3]: vkparallel[i].append([vcor[i][1], vcor[i][0]]) # delete for i in [v1, v2, v3]: vertflags[i] |= DELETE elif vf == (4, 0, 0): v0 = [k for k, v in vflags.items() if v == VERT][0] v0_i = fverts.index(v0) v1 = fverts[v0_i - 3] v2 = fverts[v0_i - 2] v3 = fverts[v0_i - 1] ''' bfaces.append([vcor[v0][0], vcor[v0][1], vcor[v1][0], vcor[v3][1]]) bfaces.append([vcor[v1][0], vcor[v1][1], vcor[v3][0], vcor[v3][1]]) bfaces.append([vcor[v1][1], vcor[v2][0], vcor[v2][1], vcor[v3][0]]) ''' bfaces.append([vcor[v0][0], vcor[v0][1], vcor[v1][0], vcor[v1][1]]) bfaces.append([vcor[v1][1], vcor[v2][0], vcor[v3][1], vcor[v0][0]]) bfaces.append([vcor[v2][0], vcor[v2][1], vcor[v3][0], vcor[v3][1]]) # material bfmats.extend([material, material, material]) # vkparallel for i in [v0, v1, v2, v3]: vkparallel[i].append([vcor[i][1], vcor[i][0]]) # delete for i in fverts: vertflags[i] |= DELETE elif vf == (0, 2, 0): v0 = [k for k, v in vflags.items() if v == EDGE][0] v0_i = fverts.index(v0) v1 = fverts[v0_i - 3] v2 = fverts[v0_i - 2] v3 = fverts[v0_i - 1] if vflags[v1] != EDGE: v0 = fverts[fverts.index(v0) - 1] v1 = fverts[fverts.index(v1) - 1] v2 = fverts[fverts.index(v2) - 1] v3 = fverts[fverts.index(v3) - 1] #bfaces.append([vcor[v0][0], vcor[v1][0], v2, v3]) bfaces.append([v2, v3, vcor[v0][0], vcor[v1][0]]) # material bfmats.extend([material]) # ekparallel key = [vcor[v0][0], vcor[v1][0]] dkey = tuple(sorted([v0, v1])) ekparallel[kedict[dkey]].append(key) # delete vertflags[v0] |= DELETE vertflags[v1] |= DELETE elif vf == (0, 4, 0): bfaces.append([vcor[i][0] for i in fverts]) # material bfmats.extend([material]) # ekparallel keys = [(fverts[i - 1], fverts[i]) for i in range(4)] selkeys = [key for key in face.edge_keys if me.edges[kedict[key]].select] keys = [key for key in keys if tuple(sorted(key)) in selkeys] for k in keys: v0, v1 = k key = [vcor[v0][0], vcor[v1][0]] dkey = tuple(sorted([v0, v1])) ekparallel[kedict[dkey]].append(key) # delete for i in fverts: vertflags[i] |= DELETE elif vf == (0, 2, 1): v0 = [k for k, v in vflags.items() if v == FACE][0] v0_i = fverts.index(v0) v1 = fverts[v0_i - 3] v2 = fverts[v0_i - 2] v3 = fverts[v0_i - 1] bfaces.append([vcor[v0][0], vcor[v1][0], v2, vcor[v3][0]]) # material bfmats.extend([material]) # ekparallel for i, j in [[v0, v1], [v3, v0]]: key = [vcor[i][0], vcor[j][0]] dkey = tuple(sorted([i, j])) ekparallel[kedict[dkey]].append(key) # delete for i in [v0, v1, v3]: vertflags[i] |= DELETE elif vf == (0, 2, 2): v0 = [k for k, v in vflags.items() if v == EDGE][0] v0_i = fverts.index(v0) v1 = fverts[v0_i - 3] v2 = fverts[v0_i - 2] v3 = fverts[v0_i - 1] if vflags[v1] != FACE: v0 = fverts[fverts.index(v0) - 3] v1 = fverts[fverts.index(v1) - 3] v2 = fverts[fverts.index(v2) - 3] v3 = fverts[fverts.index(v3) - 3] bfaces.append([vcor[i][0] for i in fverts]) # material bfmats.extend([material]) # ekparallel for i, j in [[v0, v1], [v1, v2], [v2, v3]]: key = [vcor[i][0], vcor[j][0]] dkey = tuple(sorted([i, j])) ekparallel[kedict[dkey]].append(key) # delete for i in fverts: vertflags[i] |= DELETE elif vf == (0, 0, 4): v0, v1, v2, v3 = fverts bfaces.append([vcor[v0][0], vcor[v1][0], vcor[v2][0], vcor[v3][0]]) # material bfmats.extend([material]) # ekparallel for i, j in [[v0, v1], [v1, v2], [v2, v3], [v3, v0]]: key = [vcor[i][0], vcor[j][0]] dkey = tuple(sorted([i, j])) ekparallel[kedict[dkey]].append(key) # delete for i in fverts: vertflags[i] |= DELETE else: continue else: # Triangle if vf == (1, 0, 0): v0 = [k for k, v in vflags.items() if v == VERT][0] v0_i = fverts.index(v0) v1 = fverts[v0_i - 2] v2 = fverts[v0_i - 1] #bfaces.append([vcor[v0][0], vcor[v0][1], v1, v2]) bfaces.append([v1, v2, vcor[v0][0], vcor[v0][1]]) # material bfmats.extend([material]) # vkparallel vkparallel[v0].append([vcor[v0][1], vcor[v0][0]]) # delete vertflags[v0] |= DELETE elif vf == (1, 2, 0): v0 = [k for k, v in vflags.items() if v == VERT][0] v0_i = fverts.index(v0) v1 = fverts[v0_i - 2] v2 = fverts[v0_i - 1] bfaces.append([vcor[v0][0], vcor[v0][1], vcor[v1][0], vcor[v2][0]]) # material bfmats.extend([material]) # vkparallel vkparallel[v0].append([vcor[v0][1], vcor[v0][0]]) # ekparallel key = [vcor[v1][0], vcor[v2][0]] dkey = tuple(sorted([v1, v2])) ekparallel[kedict[dkey]].append(key) # delete vertflags[v0] |= DELETE elif vf == (2, 0, 0): v0 = [k for k, v in vflags.items() if v == 0][0] v0_i = fverts.index(v0) v1 = fverts[v0_i - 2] v2 = fverts[v0_i - 1] bfaces.append([v0, vcor[v1][0], vcor[v2][1]]) bfaces.append([vcor[v1][0], vcor[v1][1], vcor[v2][0], vcor[v2][1]]) # material bfmats.extend([material, material]) # vkparallel for i in [v1, v2]: vkparallel[i].append([vcor[i][1], vcor[i][0]]) # delete vertflags[v1] |= DELETE vertflags[v2] |= DELETE elif vf == (3, 0, 0): v0 = [k for k, v in vflags.items() if v == VERT][0] v0_i = fverts.index(v0) v1 = fverts[v0_i - 2] v2 = fverts[v0_i - 1] bfaces.append([vcor[v0][0], vcor[v0][1], vcor[v1][0], vcor[v2][1]]) bfaces.append([vcor[v1][0], vcor[v1][1], vcor[v2][0], vcor[v2][1]]) # material bfmats.extend([material, material]) # vkparallel for i in [v0, v1, v2]: vkparallel[i].append([vcor[i][1], vcor[i][0]]) # delete for i in fverts: vertflags[i] |= DELETE elif vf == (0, 2, 0): v0 = [k for k, v in vflags.items() if v == 0][0] v0_i = fverts.index(v0) v1 = fverts[v0_i - 2] v2 = fverts[v0_i - 1] bfaces.append([v0, vcor[v1][0], vcor[v2][0]]) # material bfmats.extend([material]) # ekparallel key = [vcor[v1][0], vcor[v2][0]] dkey = tuple(sorted([v1, v2])) ekparallel[kedict[dkey]].append(key) # delete vertflags[v1] |= DELETE vertflags[v2] |= DELETE elif vf == (0, 2, 1): v0 = [k for k, v in vflags.items() if v == FACE][0] v0_i = fverts.index(v0) v1 = fverts[v0_i - 2] v2 = fverts[v0_i - 1] bfaces.append([vcor[v0][0], vcor[v1][0], vcor[v2][0]]) # material bfmats.extend([material]) # ekparallel for i, j in [[v0, v1], [v2, v0]]: key = [vcor[i][0], vcor[j][0]] dkey = tuple(sorted([i, j])) ekparallel[kedict[dkey]].append(key) # delete for i in fverts: vertflags[i] |= DELETE elif vf == (0, 0, 3): v0 = [k for k, v in vflags.items() if v == FACE][0] v0_i = fverts.index(v0) v1 = fverts[v0_i - 2] v2 = fverts[v0_i - 1] bfaces.append([vcor[v0][0], vcor[v1][0], vcor[v2][0]]) # material bfmats.extend([material]) # ekparallel for i, j in [[v0, v1], [v1, v2], [v2, v0]]: key = [vcor[i][0], vcor[j][0]] dkey = tuple(sorted([i, j])) ekparallel[kedict[dkey]].append(key) # delete for i in fverts: vertflags[i] |= DELETE else: continue # delete faceflags[findex] |= DELETE # active material if ob.active_material in list(me.materials): actmat = list(me.materials).index(ob.active_material) else: actmat = None # bevel・消去された辺に面を張る for eindex, edges in enumerate(ekparallel): #if edges: if len(edges) == 2: # 判定不要? v0, v1 = edges[0] # 新規頂点 v2, v3 = edges[1] #bfaces.append([v3, v2, v1, v0]) # 法線を合わせる為に逆転 bfaces.append([v1, v0, v3, v2]) # 法線を合わせる為に逆転 # material key = me.edges[eindex].key fmats = [me.faces[i].material_index for i in efdict[key]] samemat = True fmat = fmats[0] for i in fmats: if i != fmat: samemat = False if samemat: bfmats.extend([fmat]) else: bfmats.extend([actmat]) # add new keys to vkparallel key1 = (v1, v2) key2 = (v3, v0) for v in me.edges[eindex].key: vv_verts = vvparallel[v] if key1[0] in vv_verts or key1[1] in vv_verts: vkparallel[v].append(key1) else: vkparallel[v].append(key2) # bevel・消去された頂点(flag:VERT)に面を張る # vkparallelのkeyは、面を張るために逆転済み for vi, keys in vkparallel.items(): if len(keys) < 2: continue verts = keypath(keys) if not verts: continue if len(verts) <= 4: bfaces.append(verts) # material #bfmats.extend([actmat]) else: faces = fill(verts) bfaces.extend(faces) # material #bfmats.extend([actmat for i in range(len(faces))]) # material fmats = [me.faces[i].material_index for i in vfdict[vi]] samemat = True fmat = fmats[0] for i in fmats: if i != fmat: samemat = False if len(verts) <= 4: fnum = 1 else: fnum = len(faces) if samemat: bfmats.extend([fmat for i in range(fnum)]) else: bfmats.extend([actmat for i in range(fnum)]) # edge beveledges = bedges = [] '''vevparallel = {v.index:{kedict[key]:None for key in vedict[v.index]} for v in me.vertices if len(vedict[v.index]) == 2} ''' vevparallel = {} for v in me.vertices: if v.hide: continue if v.index not in vedict: continue if len(vedict[v.index]) == 2: evdict = {} for key in vedict[v.index]: evdict[kedict[key]] = None vevparallel[v.index] = evdict for vertex in me.vertices: vindex = vertex.index if not vertex.select or vertex.hide: continue if not vertflags[vindex] & EDGE: continue keys = vedict[vindex] if len(keys) != 2: continue e1 = me.edges[kedict[keys[0]]] e2 = me.edges[kedict[keys[1]]] if e1.hide or e2.hide: continue everts = [] va0 = me.vertices[vindex].co for edge in [e1, e2]: va1 = me.vertices[the_other(edge.key, vindex)].co vr01 = va1 - va0 vr01.normalize() v = vr01 co = va0.copy() bevelvert = BVert(v, co, bevelvertindex, vindex, edge.index, None) bevelvert.f = VERT bevelverts.append(bevelvert) vevparallel[vindex][edge.index] = bevelvert.vi everts.append(bevelvert.vi) bevelvertindex += 1 bedges.append(sorted(everts)) # sortedの必要無し? # delete vertflags[vindex] |= DELETE for edge in me.edges: conti = 0 for i in edge.key: v = me.vertices[i] #if not v.select or v.hide or not vertflags[vindex] & EDGE: if (not v.select) or v.hide or (not vertflags[i] & EDGE): conti =+ 1 if conti == 2: continue v1, v2 = edge.key vb1 = vevparallel[v1][edge.index] if v1 in vevparallel else v1 vb2 = vevparallel[v2][edge.index] if v2 in vevparallel else v2 if vb1 and vb2: bedges.append(sorted([vb1, vb2])) # delverts delverts = [i for i, v in enumerate(vertflags) if v & DELETE] # delfaces #delfaces = [f for f in faceflags if f & DELETE] #return bevelverts, beveledges, bevelfaces, delverts, delfaces return bevelverts, beveledges, bevelfaces, delverts, bfmats
def execute(self, context): active_obj = context.scene.objects.active if active_obj is None or active_obj.data is None: self.report({'ERROR'}, "No active object") return {'CANCELLED'} active_mesh = active_obj.data select_and_change_mode(active_obj, 'EDIT') bm = bmesh.from_edit_mesh(active_mesh) selvertsIdx = get_bmhistory_vertsIdx(bm) if len(selvertsIdx) < 2: self.report({'ERROR'}, "Verts History error: Pick source, then target.") return {'CANCELLED'} verts_snap_map = {} verts_snap_nrm_map = {} if (active_mesh.uv_textures.get(kWPLPinmapUV) is not None and WPL_G.pinobj == active_obj.name): print("Found pinned state, using for reference") verts_se_map = {} (verts_snap_map,verts_snap_nrm_map,verts_se_map,_,_) = getBmPinmap(active_obj, bm, None) else: for vert in bm.verts: verts_snap_map[vert.index] = vert.co verts_snap_nrm_map[vert.index] = vert.normal transfScale = Vector(self.opt_scale) refp_origin_v = bm.verts[selvertsIdx[1]] refp_target_v = bm.verts[selvertsIdx[0]] refp_origin = refp_origin_v.co refp_target = refp_target_v.co transferred = [] transferMap = [(refp_origin_v,refp_target_v,Vector((0,0,0)))] transferStep = 0 errs1 = 0 errs2 = 0 while len(transferMap)>0: #print("transferMap", transferMap, "transferStep", transferStep) transferMapNext = [] for tpr in transferMap: vSrc = tpr[0] vTrg = tpr[1] vSrcOffs = tpr[2] vSrc_bCo = verts_snap_map[vSrc.index] vTrg_bCo = verts_snap_map[vTrg.index] # transferring position infl = 1.0 if self.opt_falloff > 0.0: infl = 1.0-float(transferStep+1)/float(self.opt_cloneLoops) infl = pow(infl,self.opt_falloff) vSrcOffsScaled = Vector((vSrcOffs[0]*transfScale[0],vSrcOffs[1]*transfScale[1],vSrcOffs[2]*transfScale[2])) vTrg.co = vTrg.co.lerp(refp_target+vSrcOffsScaled, infl) transferred.append(vSrc.index) transferred.append(vTrg.index) if transferStep < self.opt_cloneLoops: # moving to next srcNxt = [] for eSrc in vSrc.link_edges: vSrc2 = eSrc.other_vert(vSrc) if vSrc2.index in transferred or vSrc2.hide > 0: continue vSrc2_bCo = verts_snap_map[vSrc2.index] parentDir = (vSrc2_bCo-vSrc_bCo) parentDir = parentDir.normalized() srcNxt.append((vSrc2, parentDir)) trgNxt = [] trgNxtUsed = [] # Find nearest end point among target endes for each source edge for i,vprSrc in enumerate(srcNxt): nearestDst = 9999 nearestVrt = None for eTrg in vTrg.link_edges: vTrg2 = eTrg.other_vert(vTrg) if vTrg2.index in transferred or vTrg2.hide > 0: continue vTrg2_bCo = verts_snap_map[vTrg2.index] parentDir = (vTrg2_bCo-vTrg_bCo) parentDir = Vector((parentDir[0]*transfScale[0],parentDir[1]*transfScale[1],parentDir[2]*transfScale[2])) parentDir = parentDir.normalized() srcELen = (parentDir-vprSrc[1]).length if srcELen < nearestDst: # to avoid miscast on 1-unit distance if srcELen < 0.99 or len(trgNxt)-len(srcNxt)<2: nearestDst = srcELen nearestVrt = vTrg2 # TBD: other matching means if (nearestVrt is not None) and (nearestVrt not in trgNxtUsed): trgNxtUsed.append(nearestVrt) trgNxt.append((nearestVrt, nearestDst)) else: errs1 = errs1+1 if len(srcNxt) == len(trgNxt) and len(trgNxt)>0: #print("srcNxt", srcNxt) #print("trgNxt", trgNxt) for i,vprSrc in enumerate(srcNxt): vprTrg = trgNxt[i] vSrcOffs = vprSrc[0].co-refp_origin nextstep = (vprSrc[0],vprTrg[0],vSrcOffs) transferMapNext.append(nextstep) else: errs2 = errs2+1 # easily on mesh bounds -> just skipping #self.report({'ERROR'}, "Stopped at topology difference") #transferMapNext = [] transferMap = transferMapNext transferStep = transferStep+1 bm.normal_update() bmesh.update_edit_mesh(active_mesh, True) self.report({'INFO'}, "Snapped: "+ str(int(len(transferred)/2))+" in "+str(transferStep)+" steps; Skipped:"+str(errs1)+"/"+str(errs2)) return {'FINISHED'}
def calc_weighted_normal(bm, vert_index, edge_index): """Calculates weighted normal for given combination of vertex and edge index. WARNING: There is no safety chec if thoose two belongs together. :param bm: bmesh object :type bm: bmesh :param vert_index: index of the vertex to calculate normal for :type vert_index: int :param edge_index: index of the edge to use for calculation (vertex has to belong to this edge) :returns: Vector """ normal_hash = str(vert_index) + ":" + str(edge_index) if normal_hash in WeightNormalsCalculator.cache: return WeightNormalsCalculator.cache[normal_hash] edge = bm.edges[edge_index] vert = bm.verts[vert_index] selected_faces = [] # edge.seam = True # edge.select_set(True) for f in edge.link_faces: if not f.select: f.select = True selected_faces.append(f) # select linked faces of already selected edges # until every smooth face around current loop is selected more_selected = 1 while more_selected > 0: more_selected = 0 for edge1 in vert.link_edges: if edge1.smooth and edge1.select: for f in edge1.link_faces: if not f.select: f.select = True selected_faces.append(f) more_selected += 1 # calc areas max_area = 0 areas = {} for i, f in enumerate(selected_faces): area = f.calc_area() areas[i] = area if area > max_area: max_area = area # calc normal normal = Vector() for i, f in enumerate(selected_faces): perc = areas[i] / max_area f.normal_update() normal += perc * f.normal # also unselect all the faces f.select = False WeightNormalsCalculator.cache[normal_hash] = normal.normalized() return normal.normalized()
def determine_influence(self, octree, falloff_curve, ignore_backfacing=False, mesh_object=None): coordinate_map = octree.coordinate_map map_manager = MapManager() primary_brush = self.primary_brush vertex_filter = VertexFilter() vertex_filter.mesh_object = mesh_object # Determine the primary brush's influence. center = primary_brush.center radius = primary_brush.radius vertex_filter.indices = octree.get_indices_in_box(center, radius) vertex_filter.coordinate_map = coordinate_map distance_map = vertex_filter.discard_outside_of_sphere(center, radius) primary_brush.indices = vertex_filter.indices # Only proceed if at least one vertex is within the primary brush's # influence. if primary_brush.indices: # Create the falloff map for the vertex indices that are within the # primary brush's influence. map_manager.map_ = distance_map map_manager.clip_domain(primary_brush.indices, 'RETAIN') primary_brush.falloff_map =\ falloff_curve.get_falloff_map_from_distance_map( distance_map, radius ) # Calculate the primary brush's normal. primary_brush_falloff_map = primary_brush.falloff_map primary_brush_normal = primary_brush.normal normal = Vector((0, 0, 0)) normal_sampling_radius = 0.333 * radius model_matrix = bpy.context.active_object.matrix_world vertices = bpy.context.active_object.data.vertices for vertex_index, distance in distance_map.items(): # Only vertices within the normal sampling radius contribute to # the primary brush's normal. if distance <= normal_sampling_radius: # Disregard vertices that face away from the primary # brush's initial normal. vertex_normal = model_matrix * ( vertices[vertex_index].normal ).normalized() if vertex_normal.dot(primary_brush_normal) > 0: # Each vertex normal contributes in proportion to its # falloff value. normal += vertex_normal * ( primary_brush_falloff_map[vertex_index] ) normal.normalize() if normal.length_squared > 0: primary_brush.normal = normal.normalized() # Discard vertices facing away from the primary brush, if # necessary. if ignore_backfacing: vertex_filter.indices = primary_brush.indices vertex_filter.discard_backfacing(primary_brush.normal, 'WORLD') primary_brush.indices = vertex_filter.indices # Determine each derived brush's influence. self.update_derived() for brush in self.derived_brushes: # Determine which vertex indices are within the brush's influence. center = brush.center radius = brush.radius vertex_filter.indices = octree.get_indices_in_box(center, radius) vertex_filter.coordinate_map = octree.coordinate_map distance_map =\ vertex_filter.discard_outside_of_sphere(center, radius) if ignore_backfacing: vertex_filter.discard_backfacing(brush.normal, 'WORLD') brush.indices = vertex_filter.indices # Only proceed if at least one vertex is within the brush's # influence. if primary_brush.indices: # Create the falloff map for the vertex indices that are within # the brush's influence. map_manager.map_ = distance_map map_manager.clip_domain(brush.indices, 'RETAIN') brush.falloff_map =\ falloff_curve.get_falloff_map_from_distance_map( distance_map, radius )
def execute(self, context): mesh = context.object.data mesh.use_auto_smooth = True bpy.ops.mesh.customdata_custom_splitnormals_clear() bm = bmesh.new() bm.from_mesh(mesh) bm.verts.ensure_lookup_table() nor_weighted = [] for v in bm.verts: max_area = 0 areas = {} for i, f in enumerate(v.link_faces): if f.smooth: area = f.calc_area() areas[i] = area if area > max_area: max_area = area normal = Vector() for i, f in enumerate(v.link_faces): if f.smooth: perc = areas[i] / max_area normal += perc * f.normal nor_weighted.extend(normal.normalized()) bm.free() nor_list = [(0,)] * len(mesh.loops) for poly in mesh.polygons: l_s = poly.loop_start l_e = poly.loop_start + poly.loop_total - 1 curr_l = l_s prev_l = l_e while curr_l <= l_e: curr_loop = mesh.loops[curr_l] prev_loop = mesh.loops[prev_l] # if at least one edge of this corner doesn't use sharp edge # apply calculated weighted normal if not mesh.edges[curr_loop.edge_index].use_edge_sharp or not mesh.edges[prev_loop.edge_index].use_edge_sharp: curr_n = nor_weighted[curr_loop.vertex_index * 3:curr_loop.vertex_index * 3 + 3] nor_list[curr_l] = curr_n else: nor_list[curr_l] = curr_loop.normal prev_l = curr_l curr_l += 1 bpy.ops.mesh.customdata_custom_splitnormals_add() mesh.normals_split_custom_set(nor_list) mesh.free_normals_split() return {'FINISHED'}
def execute(self, context): influenceamount = abs(context.window_manager.vn_bendingratio) showselected = context.window_manager.vn_editselection selectByFace = context.window_manager.vn_editbyface maxdist = context.window_manager.normtrans_maxdist influencemult = 1.0 if ( context.window_manager.vn_bendingratio > 0.0 ) else -1.0 selectByFace = context.window_manager.vn_editbyface editselection = context.window_manager.vn_editselection if maxdist <= 0.0: maxdist = 8192.0 if influenceamount > 0.0: destobj = context.active_object.data destdata = [] newnormals = [] destobj.calc_normals_split() vertslist = [[(v.co).copy(), v.select] for v in destobj.vertices] loopnorms_raw = [(l.normal).copy() for l in destobj.loops] loopcount = 0 dfcount = 0 for f in destobj.polygons: fvn = [] fvco = [] fvsel = [] newnormals.append([]) for v in f.vertices: fvco.append(vertslist[v][0].copy()) fvn.append(loopnorms_raw[loopcount].copy()) if showselected: if selectByFace: fvsel.append(f.select) else: fvsel.append(vertslist[v][1]) else: fvsel.append(True) loopcount += 1 newnormals[dfcount].append([]) destdata.append([fvco,fvn,fvsel]) dfcount += 1 destobj.free_normals_split() del vertslist[:] del loopnorms_raw[:] selobjects = [obj.data for obj in context.selected_objects if obj.type == 'MESH'] sourceverts = [] foundobj = (len(selobjects) > 1) for objmesh in selobjects: if objmesh != destobj: fcount = 0 lastdist = maxdist curdistv = Vector((0.0,0.0,0.0)) tempv = Vector((0.0,0.0,0.0)) curdist = 0.0 if objmesh.use_auto_smooth: sourceverts = [] objmesh.calc_normals_split() vertslist = [[(v.co).copy(), v.select] for v in objmesh.vertices] loopnorms_raw = [(l.normal).copy() for l in objmesh.loops] loopcount = 0 for f in objmesh.polygons: fvn = [] fvco = [] fvsel = [] for v in f.vertices: fvco.append(vertslist[v][0].copy()) fvn.append(loopnorms_raw[loopcount].copy()) if showselected: if selectByFace: fvsel.append(f.select) else: fvsel.append(vertslist[v][1]) else: fvsel.append(True) loopcount += 1 sourceverts.append([fvco,fvn,fvsel]) objmesh.free_normals_split() del vertslist[:] del loopnorms_raw[:] if len(sourceverts) > 0: for f in destdata: for i in range(len(f[0])): lastdist = maxdist nearest = f[1][i].copy() if f[2][i]: for df in sourceverts: for j in range(len(df[0])): curdistv = f[0][i] - df[0][j] curdist = curdistv.magnitude if curdist < maxdist: if curdist < lastdist: nearest = df[1][j].copy() lastdist = curdist tempv = ( ((f[1][i] * (1.0 - influenceamount)) + (nearest * influenceamount)) * influencemult ).normalized() newnormals[fcount][i].append(tempv.copy()) else: newnormals[fcount][i].append(f[1][i].copy()) fcount += 1 del sourceverts[:] else: sourceverts = [[],[],[]] for v in objmesh.vertices: sourceverts[0].append((v.co).copy()) sourceverts[1].append((v.normal).copy()) if showselected: sourceverts[2].append(v.select) else: sourceverts[2].append(True) if len(sourceverts) > 0: for f in destdata: for i in range(len(f[0])): lastdist = maxdist nearest = f[1][i].copy() if f[2][i]: for j in range(len(sourceverts[0])): curdistv = f[0][i] - sourceverts[0][j] curdist = curdistv.magnitude if curdist < maxdist: if curdist < lastdist: nearest = sourceverts[1][j].copy() lastdist = curdist tempv = ( ((f[1][i] * (1.0 - influenceamount)) + (nearest * influenceamount)) * influencemult ).normalized() newnormals[fcount][i].append(tempv.copy()) else: newnormals[fcount][i].append(f[1][i].copy()) fcount += 1 del sourceverts[:] del destdata[:] del selobjects[:] if foundobj: procnormslist = [] for i in range(len(newnormals)): procnormslist.append([]) for j in range(len(newnormals[i])): tempv = Vector((0.0,0.0,0.0)) if len(newnormals[i][j]) > 0: for v in newnormals[i][j]: tempv = tempv + v procnormslist[i].append(tempv.normalized()) if (update_customnormals(destobj, procnormslist)): context.area.tag_redraw() context.scene.update() else: print('Need more than one object') del newnormals[:] else: print('No influence') return {'FINISHED'}
def noise(var=1): rand = Vector((r.gauss(0, 1), r.gauss(0, 1), r.gauss(0, 1))) vec = rand.normalized() * var return vec
def execute(self, context): mesh = context.active_object.data mesh.update() mesh.calc_normals_split() # store old normals normslist = [] loopnorms = [v.normal for v in mesh.loops] loopcount = 0 for f in mesh.polygons: fvns = [] for i in range(len(f.vertices)): fvns.append(loopnorms[loopcount].copy()) loopcount += 1 normslist.append(fvns) del loopnorms[:] # clear old normals emptynormslist = tuple(tuple((0,0,0)) for v in mesh.vertices) for e in mesh.edges: if e.use_edge_sharp: e.use_edge_sharp = False mesh.validate(clean_customdata=False) if mesh.use_auto_smooth: mesh.use_auto_smooth = False if mesh.show_edge_sharp: mesh.show_edge_sharp = False mesh.normals_split_custom_set_from_vertices(emptynormslist) mesh.free_normals_split() mesh.update() # gather old split normals rawnormslist = [[] for v in mesh.vertices] faceslist = [f for f in mesh.polygons] fcount = 0 for f in faceslist: newfn = [] vcount = 0 for v in f.vertices: rawnormslist[v].append(normslist[fcount][vcount]) vcount += 1 fcount += 1 # average split normals for new list procnormslist = [] for vl in rawnormslist: tempv = Vector((0.0,0.0,0.0)) for v in vl: tempv = tempv + v tempv = tempv.normalized() procnormslist.append(tempv) vertslist = [v for v in mesh.vertices] vcount = 0 for v in vertslist: v.normal = procnormslist[vcount] vcount += 1 del rawnormslist[:] del procnormslist[:] del faceslist[:] del vertslist[:] return {'FINISHED'}
def getNormalizedVector(sefl, vector): v = Vector(vector) return v.normalized()
def main(context, island_margin, projection_limit, user_area_weight, ): global USER_FILL_HOLES global USER_FILL_HOLES_QUALITY global USER_STRETCH_ASPECT global USER_ISLAND_MARGIN from math import cos import time global dict_matrix dict_matrix = {} # Constants: # Takes a list of faces that make up a UV island and rotate # until they optimally fit inside a square. global ROTMAT_2D_POS_90D global ROTMAT_2D_POS_45D global RotMatStepRotation main_consts() # Create the variables. USER_PROJECTION_LIMIT = projection_limit USER_ONLY_SELECTED_FACES = True USER_SHARE_SPACE = 1 # Only for hole filling. USER_STRETCH_ASPECT = 1 # Only for hole filling. USER_ISLAND_MARGIN = island_margin # Only for hole filling. USER_FILL_HOLES = 0 USER_FILL_HOLES_QUALITY = 50 # Only for hole filling. USER_VIEW_INIT = 0 # Only for hole filling. is_editmode = (context.active_object.mode == 'EDIT') if is_editmode: obList = [ob for ob in [context.active_object] if ob and ob.type == 'MESH'] else: obList = [ob for ob in context.selected_editable_objects if ob and ob.type == 'MESH'] USER_ONLY_SELECTED_FACES = False if not obList: raise Exception("error, no selected mesh objects") # Reuse variable if len(obList) == 1: ob = "Unwrap %i Selected Mesh" else: ob = "Unwrap %i Selected Meshes" # HACK, loop until mouse is lifted. ''' while Window.GetMouseButtons() != 0: time.sleep(10) ''' #~ XXX if not Draw.PupBlock(ob % len(obList), pup_block): #~ XXX return #~ XXX del ob # Convert from being button types USER_PROJECTION_LIMIT_CONVERTED = cos(USER_PROJECTION_LIMIT * DEG_TO_RAD) USER_PROJECTION_LIMIT_HALF_CONVERTED = cos((USER_PROJECTION_LIMIT/2) * DEG_TO_RAD) # Toggle Edit mode is_editmode = (context.active_object.mode == 'EDIT') if is_editmode: bpy.ops.object.mode_set(mode='OBJECT') # Assume face select mode! an annoying hack to toggle face select mode because Mesh doesn't like faceSelectMode. if USER_SHARE_SPACE: # Sort by data name so we get consistent results obList.sort(key = lambda ob: ob.data.name) collected_islandList= [] #XXX Window.WaitCursor(1) time1 = time.time() # Tag as False so we don't operate on the same mesh twice. #XXX bpy.data.meshes.tag = False for me in bpy.data.meshes: me.tag = False for ob in obList: me = ob.data if me.tag or me.library: continue # Tag as used me.tag = True if not me.uv_textures: # Mesh has no UV Coords, don't bother. me.uv_textures.new() uv_layer = me.uv_layers.active.data me_verts = list(me.vertices) if USER_ONLY_SELECTED_FACES: meshFaces = [thickface(f, uv_layer, me_verts) for i, f in enumerate(me.polygons) if f.select] else: meshFaces = [thickface(f, uv_layer, me_verts) for i, f in enumerate(me.polygons)] if not meshFaces: continue #XXX Window.DrawProgressBar(0.1, 'SmartProj UV Unwrapper, mapping "%s", %i faces.' % (me.name, len(meshFaces))) # ======= # Generate a projection list from face normals, this is meant to be smart :) # make a list of face props that are in sync with meshFaces # Make a Face List that is sorted by area. # meshFaces = [] # meshFaces.sort( lambda a, b: cmp(b.area , a.area) ) # Biggest first. meshFaces.sort(key=lambda a: -a.area) # remove all zero area faces while meshFaces and meshFaces[-1].area <= SMALL_NUM: # Set their UV's to 0,0 for uv in meshFaces[-1].uv: uv.zero() meshFaces.pop() # Smallest first is slightly more efficient, but if the user cancels early then its better we work on the larger data. # Generate Projection Vecs # 0d is 1.0 # 180 IS -0.59846 # Initialize projectVecs if USER_VIEW_INIT: # Generate Projection projectVecs = [Vector(Window.GetViewVector()) * ob.matrix_world.inverted().to_3x3()] # We add to this along the way else: projectVecs = [] newProjectVec = meshFaces[0].no newProjectMeshFaces = [] # Popping stuffs it up. # Pretend that the most unique angle is ages away to start the loop off mostUniqueAngle = -1.0 # This is popped tempMeshFaces = meshFaces[:] # This while only gathers projection vecs, faces are assigned later on. while 1: # If theres none there then start with the largest face # add all the faces that are close. for fIdx in range(len(tempMeshFaces)-1, -1, -1): # Use half the angle limit so we don't overweight faces towards this # normal and hog all the faces. if newProjectVec.dot(tempMeshFaces[fIdx].no) > USER_PROJECTION_LIMIT_HALF_CONVERTED: newProjectMeshFaces.append(tempMeshFaces.pop(fIdx)) # Add the average of all these faces normals as a projectionVec averageVec = Vector((0.0, 0.0, 0.0)) if user_area_weight == 0.0: for fprop in newProjectMeshFaces: averageVec += fprop.no elif user_area_weight == 1.0: for fprop in newProjectMeshFaces: averageVec += fprop.no * fprop.area else: for fprop in newProjectMeshFaces: averageVec += fprop.no * ((fprop.area * user_area_weight) + (1.0 - user_area_weight)) if averageVec.x != 0 or averageVec.y != 0 or averageVec.z != 0: # Avoid NAN projectVecs.append(averageVec.normalized()) # Get the next vec! # Pick the face thats most different to all existing angles :) mostUniqueAngle = 1.0 # 1.0 is 0d. no difference. mostUniqueIndex = 0 # dummy for fIdx in range(len(tempMeshFaces)-1, -1, -1): angleDifference = -1.0 # 180d difference. # Get the closest vec angle we are to. for p in projectVecs: temp_angle_diff= p.dot(tempMeshFaces[fIdx].no) if angleDifference < temp_angle_diff: angleDifference= temp_angle_diff if angleDifference < mostUniqueAngle: # We have a new most different angle mostUniqueIndex = fIdx mostUniqueAngle = angleDifference if mostUniqueAngle < USER_PROJECTION_LIMIT_CONVERTED: #print 'adding', mostUniqueAngle, USER_PROJECTION_LIMIT, len(newProjectMeshFaces) # Now weight the vector to all its faces, will give a more direct projection # if the face its self was not representative of the normal from surrounding faces. newProjectVec = tempMeshFaces[mostUniqueIndex].no newProjectMeshFaces = [tempMeshFaces.pop(mostUniqueIndex)] else: if len(projectVecs) >= 1: # Must have at least 2 projections break # If there are only zero area faces then its possible # there are no projectionVecs if not len(projectVecs): Draw.PupMenu('error, no projection vecs where generated, 0 area faces can cause this.') return faceProjectionGroupList =[[] for i in range(len(projectVecs)) ] # MAP and Arrange # We know there are 3 or 4 faces here for fIdx in range(len(meshFaces)-1, -1, -1): fvec = meshFaces[fIdx].no i = len(projectVecs) # Initialize first bestAng = fvec.dot(projectVecs[0]) bestAngIdx = 0 # Cycle through the remaining, first already done while i-1: i-=1 newAng = fvec.dot(projectVecs[i]) if newAng > bestAng: # Reverse logic for dotvecs bestAng = newAng bestAngIdx = i # Store the area for later use. faceProjectionGroupList[bestAngIdx].append(meshFaces[fIdx]) # Cull faceProjectionGroupList, # Now faceProjectionGroupList is full of faces that face match the project Vecs list for i in range(len(projectVecs)): # Account for projectVecs having no faces. if not faceProjectionGroupList[i]: continue # Make a projection matrix from a unit length vector. MatQuat = VectoQuat(projectVecs[i]) # Get the faces UV's from the projected vertex. for f in faceProjectionGroupList[i]: f_uv = f.uv for j, v in enumerate(f.v): # XXX - note, between mathutils in 2.4 and 2.5 the order changed. f_uv[j][:] = (MatQuat * v.co).xy if USER_SHARE_SPACE: # Should we collect and pack later? islandList = getUvIslands(faceProjectionGroupList, me) collected_islandList.extend(islandList) else: # Should we pack the islands for this 1 object? islandList = getUvIslands(faceProjectionGroupList, me) packIslands(islandList) # update the mesh here if we need to. # We want to pack all in 1 go, so pack now if USER_SHARE_SPACE: #XXX Window.DrawProgressBar(0.9, "Box Packing for all objects...") packIslands(collected_islandList) print("Smart Projection time: %.2f" % (time.time() - time1)) # Window.DrawProgressBar(0.9, "Smart Projections done, time: %.2f sec" % (time.time() - time1)) if is_editmode: bpy.ops.object.mode_set(mode='EDIT') dict_matrix.clear()
def generate_newnormals(self, context): genmode = context.window_manager.vn_genmode me = context.active_object.data bm = bmesh.new() if context.mode == 'EDIT_MESH': bm = bmesh.from_edit_mesh(me) else: bm.from_mesh(me) me.update() faces_list = [f for f in bm.faces] verts_list = [v for v in bm.verts] # DEFAULT: Blender default if (genmode == 'DEFAULT'): wasobjmode = (context.mode == 'OBJECT') if wasobjmode: bpy.ops.object.mode_set(mode='EDIT') bm = bmesh.from_edit_mesh(me) me.update() faces_list = [f for f in bm.faces] verts_list = [v for v in bm.verts] bpy.ops.mesh.normals_make_consistent() if context.window_manager.edit_splitnormals: normals_data.cust_normals_ppoly.clear() for i in range(len(faces_list)): faceverts = [v for v in faces_list[i].verts] normals_data.cust_normals_ppoly.append([]) for j in range(len(faceverts)): normals_data.cust_normals_ppoly[len(normals_data.cust_normals_ppoly) - 1].append(faceverts[j].normal.copy()) else: normals_data.cust_normals_pvertex.clear() for i in range(len(verts_list)): normals_data.cust_normals_pvertex.append(verts_list[i].normal.copy()) if wasobjmode: bpy.ops.object.mode_set(mode='OBJECT') # UPVECT: custom direction elif (genmode == 'UPVECT'): if context.window_manager.edit_splitnormals: if context.window_manager.vn_genselectiononly: for i in range(len(normals_data.cust_normals_ppoly)): for j in range(len(normals_data.cust_normals_ppoly[i])): if faces_list[i].verts[j].select: normals_data.cust_normals_ppoly[i][j] = Vector(context.window_manager.vn_dirvector) else: for i in range(len(normals_data.cust_normals_ppoly)): for j in range(len(normals_data.cust_normals_ppoly[i])): normals_data.cust_normals_ppoly[i][j] = Vector(context.window_manager.vn_dirvector) else: if context.window_manager.vn_genselectiononly: for i in range(len(verts_list)): if verts_list[i].select: normals_data.cust_normals_pvertex[i] = Vector(context.window_manager.vn_dirvector) else: for i in range(len(verts_list)): normals_data.cust_normals_pvertex[i] = Vector(context.window_manager.vn_dirvector) # BENT: Bent from point (3D cursor) elif (genmode == 'BENT'): cursorloc = context.scene.cursor_location if context.window_manager.edit_splitnormals: if context.window_manager.vn_genselectiononly: for i in range(len(normals_data.cust_normals_ppoly)): for j in range(len(normals_data.cust_normals_ppoly[i])): if not (faces_list[i].hide) and faces_list[i].select: tempv = Vector(faces_list[i].verts[j].co) - cursorloc tempv = tempv.normalized() normals_data.cust_normals_ppoly[i][j] = tempv.copy() else: for i in range(len(faces_list)): for j in range(len(faces_list[i].verts)): tempv = Vector(vd.vpos) - cursorloc tempv = tempv.normalized() normals_data.cust_normals_ppoly[i][j] = tempv.copy() else: if context.window_manager.vn_genselectiononly: for i in range(len(verts_list)): if verts_list[i].select: tempv = Vector(verts_list[i].co) - cursorloc tempv = tempv.normalized() tempv = (normals_data.cust_normals_pvertex[i] * (1.0 - context.window_manager.vn_genbendingratio)) + (tempv * (context.window_manager.vn_genbendingratio)) normals_data.cust_normals_pvertex[i] = tempv else: for i in range(len(verts_list)): tempv = Vector(verts_list[i].co) - cursorloc tempv = tempv.normalized() tempv = (normals_data.cust_normals_pvertex[i] * (1.0 - context.window_manager.vn_genbendingratio)) + (tempv * (context.window_manager.vn_genbendingratio)) normals_data.cust_normals_pvertex[i] = tempv # G_FOLIAGE: combination of bent and up-vector for ground foliage elif (genmode == 'G_FOLIAGE'): ignorehidden = context.window_manager.vn_genignorehidden cursorloc = Vector(context.window_manager.vn_centeroffset) if context.window_manager.edit_splitnormals: for i in range(len(faces_list)): ignoreface = False if ignorehidden: if faces_list[i].hide: ignoreface = True for j in range(len(faces_list[i].verts)): if faces_list[i].verts[j].select: if not ignoreface: normals_data.cust_normals_ppoly[i][j] = Vector((0.0, 0.0, 1.0)) else: if not ignoreface: tempv = faces_list[i].verts[j].co - cursorloc normals_data.cust_normals_ppoly[i][j] = tempv.normalized() else: for i in range(len(verts_list)): if ignorehidden: if not verts_list[i].hide: if verts_list[i].select: normals_data.cust_normals_pvertex[i] = Vector((0.0, 0.0, 1.0)) else: tempv = verts_list[i].co - cursorloc normals_data.cust_normals_pvertex[i] = tempv.normalized() else: if verts_list[i].select: normals_data.cust_normals_pvertex[i] = Vector((0.0, 0.0, 1.0)) else: tempv = verts_list[i].co - cursorloc normals_data.cust_normals_pvertex[i] = tempv.normalized() # CUSTOM: generate for selected faces independently from mesh (or for the whole mesh) # - based on existing face nomals, so the mesh requires faces # - seems to be weighted by mesh topology when used in poly mode # - number of intersecting edges on connected face influences the direction elif (genmode == 'CUSTOM'): if context.window_manager.edit_splitnormals: for i in range(len(faces_list)): f = faces_list[i] if context.window_manager.vn_genselectiononly: if f.select: for j in range(len(f.verts)): fncount = 0 tempfvect = Vector((0.0, 0.0, 0.0)) if f.verts[j].select: for vf in f.verts[j].link_faces: if vf.select: fncount += 1 tempfvect = tempfvect + vf.normal if fncount > 0: normals_data.cust_normals_ppoly[i][j] = (tempfvect / float(fncount)).normalized() else: for j in range(len(f.verts)): fncount = len(f.verts[j].link_faces) tempfvect = Vector((0.0, 0.0, 0.0)) for vf in f.verts[j].link_faces: tempfvect = tempfvect + vf.normal normals_data.cust_normals_ppoly[i][j] = (tempfvect / float(fncount)).normalized() else: for i in range(len(verts_list)): v = verts_list[i] if context.window_manager.vn_genselectiononly: if v.select: fncount = 0 tempfvect = Vector((0.0, 0.0, 0.0)) for j in range(len(v.link_faces)): if v.link_faces[j].select: fncount += 1 tempfvect = tempfvect + v.link_faces[j].normal if fncount > 0: normals_data.cust_normals_pvertex[i] = (tempfvect / float(fncount)).normalized() else: fncount = len(v.link_faces) tempfvect = Vector((0.0, 0.0, 0.0)) for j in range(len(v.link_faces)): tempfvect = tempfvect + v.link_faces[j].normal normals_data.cust_normals_pvertex[i] = (tempfvect / float(fncount)).normalized() save_normalsdata(context) if (hasattr(context.active_object.data, "define_normals_split_custom") or not context.window_manager.edit_splitnormals) and context.window_manager.vn_settomeshongen: set_meshnormals(context)