def GetBestViewAxis(context): #Gets the axis closest to cameras view vector. region = context.region rv3d = context.region_data view_vector = view3d_utils.region_2d_to_vector_3d(region, rv3d, (region.width/2.0, region.height/2.0)) xAxis = Vector((1, 0, 0)) yAxis = Vector((0, 1, 0)) zAxis = Vector((0, 0, 1)) a1 = xAxis.angle(view_vector) a1d = abs((a1 * 180 / 3.14) - 90) a2 = yAxis.angle(view_vector) a2d = abs((a2 * 180 / 3.14) - 90) a3 = zAxis.angle(view_vector) a3d = abs((a3 * 180 / 3.14) - 90) A = max(a1d, a2d,a3d) if A == a1d: dir = -1 if a1 * 180 / 3.14 > 90: dir = 1 return Vector((dir, 0, 0)), 'X', abs((a1 * 180 / 3.14) - 90) if A == a2d: dir = -1 if a2 * 180 / 3.14 > 90: dir = 1 return Vector((0, dir, 0)), 'Y', abs((a2 * 180 / 3.14) - 90) if A == a3d: dir = -1 if a3 * 180 / 3.14 > 90: dir = 1 return Vector((0, 0, dir)), 'Z', abs((a3 * 180 / 3.14) - 90)
def draw_arrow_head(ob, vecFrom, vecTo): if ob is None: return direction = Vector(vecTo) - Vector(vecFrom) print(direction) # direction.resize_2d() # print(direction) angle = direction.angle(Vector((0, 1, 0))) # form a 2d rotation matrix mat = Matrix().Rotation(angle, 2) # middle point middle = (Vector(vecTo) + Vector(vecFrom)) / 2.0 bgl.glEnable(bgl.GL_BLEND) bgl.glBegin(bgl.GL_LINE_STRIP) for v in arrow_head: xy = Vector(v) * 1.0 * mat xy.resize_3d() newPos = xy + middle bgl.glVertex3f(*newPos) bgl.glLineWidth(2) bgl.glColor4f(0.0, 0.0, 0.0, 0.5) bgl.glEnd()
def Circle(name = 'Circle', size = (1., 1.), subdivisions = 32, normal = None, fill_type = 'NOTHING', location = (0., 0., 0.), rotation = (0., 0., 0.), *args, **kw): if size == None: scale = (1., 1., 1.) elif isinstance(size, (float, int)): scale = (size, size, 1.) elif hasattr(size, "__iter__"): scale = (size[0], size[1], 1.) if normal: vector = Vector(normal) zaxis = Vector([0.,0.,1.]) angle = zaxis.angle(vector) axis = zaxis.cross(vector) rotation = mathutils.Matrix.Rotation(angle, 4, axis).to_euler() bpy.ops.mesh.primitive_circle_add( vertices=int(subdivisions), radius=1., location=Vector(location), rotation=Vector(rotation) ) #, \ #layers=[True]+[False]*19) obj = Mesh(Blender.active_object) obj.name = name if Blender.auto_shade_smooth(): obj.shade_smooth() return obj
def Cone(name = 'Cone', vector = (0.,0.,1.), radius1 = 1., radius2 = 0., subdivisions = 32, end_fill_type = 'NGON', location = (0., 0., 0.), *args, **kw): vector = Vector(vector) zaxis = Vector([0.,0.,1.]) angle = zaxis.angle(vector) axis = zaxis.cross(vector) rotation = mathutils.Matrix.Rotation(angle, 4, axis).to_euler() bpy.ops.mesh.primitive_cone_add( vertices=int(subdivisions), radius1=float(radius1), radius2=float(radius2), \ location=Vector(location), rotation=Vector(rotation), \ end_fill_type=end_fill_type, \ depth=float(vector.length) )#, layers=[True]+[False]*19) obj = Mesh(Blender.active_object) obj.name = name if Blender.auto_shade_smooth(): obj.shade_smooth() return obj
def test_orthogonal(self): angle_90d = math.pi / 2.0 for v in vector_data: v = Vector(v) if v.length_squared != 0.0: self.assertAlmostEqual(v.angle(v.orthogonal()), angle_90d)
def get_rotation_quaternion_from_normal(normal: mathutils.Vector): """ Construct the rotation quaternion to rotate the specified normal vector onto the (0.0, 0.0, 1.0) vector :param normal: The normal vector used to construct the rotation quaternion :returns: The rotation quaternion to align the normal with the z-axis """ vec_z = mathutils.Vector((0.0, 0.0, 1.0)) theta = normal.angle(vec_z) # angle axis = normal.cross(vec_z) # axis axis.normalize() sin_theta = math.sin(theta / 2.0) q_rot = mathutils.Quaternion((math.cos(theta / 2.0), axis.x * sin_theta, axis.y * sin_theta, axis.z * sin_theta)) q_norm = mathutils.Quaternion((0.0, normal.x, normal.y, normal.z)) rot = q_rot * q_norm * q_rot.conjugated() print(normal) print(axis) print(mathutils.Vector((rot.x, rot.y, rot.z))) return q_rot
def viewrotate_apply(self, context, event): # FIXME vod = self.vod x, y = event.x, event.y if context.user_preferences.inputs.view_rotate_method == 'TRACKBALL': newvec = calctrackballvec(context.region, event.x, event.y) dvec = newvec - vod.trackvec angle = (dvec.length / (2.0 * TRACKBALLSIZE)) * math.pi angle = angle_wrap_rad(angle) axis = vod.trackvec.cross(newvec) q1 = Quaternion(axis, angle) vod.viewquat = q1 * vod.oldquat self.viewrotate_apply_dyn_ofs(vod.viewquat) else: zvec_global = Vector([0, 0, 1]) sensitivity = 0.007 m = vod.viewquat.to_matrix() m_inv = m.inverted() if (zvec_global - m_inv.col[2]).length > 0.001: xaxis = zvec_global.closs(m_inv.col[0]) if xaxis.dot(m_inv.col[0]) < 0: xaxis.negate() fac = zvec_global.angle(m_inv.col[2]) / math.pi fac = abs(fac - 0.5) * 2 fac *= fac xaxis = xaxis.lerp(m_inv.col[0], fac) else: xaxis = m_inv[0].copy() quat_local_x = Quaternion(xaxis, sensitivity * - (y - vod.oldy)) quat_local_x = vod.viewquat * quat_local_x def axis_angle_to_quat_single(axis, angle): angle_half = angle * 0.5 angle_cos = math.cos(angle_half) angle_sin = math.sin(angle_half) axis_index = ['X', 'Y', 'Z'].index(axis) q = Quaternion([angle_cos, 0, 0, 0]) q[axis_index + 1] = angle_sin return q quat_global_z = axis_angle_to_quat_single( 'Z', sensitivity * vod.reverse * (x - vod.oldx)) vod.viewquat = quat_local_x * quat_global_z self.viewrotate_apply_dyn_ofs(vod.viewquat) vod.viewquat.normalize() context.region_data.view_rotation = vod.viewquat.inverted() if vod.axis_snap: self.viewrotate_apply_snap(vod) vod.oldx = x vod.oldy = y ED_view3d_camera_lock_sync(vod.v3d, context.region_data) context.region.tag_redraw() pass
def sort(self): # align to Y axis align_axis = Vector((0, 1)) rotated_faces = [] for face in self.faces: edge = face.getLongestEdge() edge_vector = Vector(edge[1]) - Vector(edge[0]) # change vector direction to positive if edge[0][0] > edge[1][0]: edge_vector = Vector(edge[0]) - Vector(edge[1]) angle = align_axis.angle(edge_vector) face.rotate(angle, 'center') center = face.getCenter() edge = face.getLongestEdge() # rotate the way that all other vertices is on the right side if edge[0][0] > center[0]: face.rotate(math.radians(180), 'center') # aligh to Y axis edge = face.getLongestEdge() point = Vector(edge[1]) target = Vector((self.x, self.y)) face.translate(point, target) rotated_faces.append(face) self.faces = rotated_faces
def getScreenLookAxis(context, location=None): scene = context.scene region = context.region rv3d = context.region_data coord = region.width / 2.0, region.height / 2.0 if (location is not None): coord = view3d_utils.location_3d_to_region_2d(region, rv3d, location) if (not coord): return Vector((0.0, 0.0, 0.0)), 0 # depth = Vector((0.0, 0.0, 0.0)); # origin = 0.0, 0.0; # down = 0.0, 1.0; # right = 1.0, 0.0; # o_vector = view3d_utils.region_2d_to_location_3d(region, rv3d, origin, depth_location=depth); # r_vector = view3d_utils.region_2d_to_location_3d(region, rv3d, right, depth_location=depth) - o_vector; # d_vector = view3d_utils.region_2d_to_location_3d(region, rv3d, down, depth_location=depth) - o_vector; # r_vector.normalize(); # d_vector.normalize(); # print('VIEW VECTOR ::: ', o_vector, r_vector, d_vector); base_view = Vector((0.0, 1.0, 0.0)) # base_view = Vector((0.0, 0.0, 1.0)); view_vector = view3d_utils.region_2d_to_vector_3d(region, rv3d, coord) view_vector.normalize() axis = base_view.cross(view_vector) angle = base_view.angle(view_vector) axis.normalize() return axis, math.degrees(angle)
def set_body_position_orientation(): """ Le point '18' est au centre de [8, 11] soit au centre du bassin. Il ne vient pas de COCO ! Le bone spine suit les rotation de 18 sur Z """ # Position 8 à droite 11 à gauche if gl.points[8] and gl.points[11]: x = (gl.spheres[8].worldPosition[0] + gl.spheres[11].worldPosition[0]) / 2 y = (gl.spheres[8].worldPosition[1] + gl.spheres[11].worldPosition[1]) / 2 z = (gl.spheres[8].worldPosition[2] + gl.spheres[11].worldPosition[2]) / 2 gl.spheres[18].worldPosition = [x, y, z] gl.spheres[18].worldScale = [ 1.5 * gl.scale, 1.5 * gl.scale, 1.5 * gl.scale ] # Rotation: direction = de 11 à 8 if gl.points[8] and gl.points[11]: try: a = gl.spheres[8].worldPosition b = gl.spheres[11].worldPosition direction = (b - a).normalized() axis_align = Vector((1.0, 0.0, 0.0)) angle = axis_align.angle(direction) axis = axis_align.cross(direction) quat = Quaternion(axis, angle) gl.spheres[18].localOrientation = quat.to_euler('XYZ') except: pass
def apply_objet_position_orientation(objet_point_1, objet_point_2, objet): """Valable pour un objet seulement. objet_point_1, objet_point_2 sont 2 objets Blender: ils définissent un vecteur. L'objet objet est orienté suivant ce vecteur, si objet_point_2 n'est pas None et positionné en objet_point_1. """ if objet_point_2: try: a = objet_point_1.worldPosition b = objet_point_2.worldPosition direction = (b - a).normalized() axis_align = Vector((1.0, 0.0, 0.0)) angle = axis_align.angle(direction) axis = axis_align.cross(direction) quat = Quaternion(axis, angle) objet.localOrientation = quat.to_euler('XYZ') sc = (b - a).length # Les coefficients correspondent à la taille des objects cube # qui représentent les os objet.localScale = [ sc * 5 * gl.scale, 0.2 * gl.scale, 0.2 * gl.scale ] except: pass # Apply position objet.worldPosition = objet_point_1.worldPosition
def is_bmvert_collinear(self, v): le = v.link_edges if len(le) == 2: vec1 = Vector(v.co - le[0].other_vert(v).co) vec2 = Vector(v.co - le[1].other_vert(v).co) if abs(vec1.angle(vec2)) >= 3.1415: return True return False
def draw_gems(self, context, ratio_w=1, ratio_h=1): from_scene_scale = unit.Scale(context).from_scene view_normal = Vector((0.0, 0.0, 1.0)) @ self.region_3d.view_matrix angle_thold = pi / 1.8 fontid = 0 blf.size(fontid, self.prefs.view_font_size_gem_size, 72) blf.color(fontid, 0.0, 0.0, 0.0, 1.0) shader = gpu.shader.from_builtin("2D_UNIFORM_COLOR") depsgraph = context.depsgraph for dup in depsgraph.object_instances: if dup.is_instance: ob = dup.instance_object.original else: ob = dup.object.original if "gem" not in ob or (self.use_select and not ob.select_get()): continue shader.bind() ob_stone = ob["gem"]["stone"] ob_cut = ob["gem"]["cut"] ob_size = tuple(round(x, 2) for x in from_scene_scale(ob.dimensions, batch=True)) for stone, cut, size, size_fmt, color in self.gems_raw: if ob_stone == stone and ob_cut == cut and ob_size == size: shader.uniform_float("color", color) break me = ob.to_mesh(depsgraph, True) me.transform(dup.matrix_world) verts = me.vertices for poly in me.polygons: if view_normal.angle(poly.normal) < angle_thold: cos = [ loc_3d_to_2d(self.region, self.region_3d, verts[v].co, ratio_w, ratio_h) for v in poly.vertices ] batch = batch_for_shader(shader, "TRI_FAN", {"pos": cos}) batch.draw(shader) bpy.data.meshes.remove(me) # Size # ----------------------------- ob_loc = dup.matrix_world.translation.to_tuple() loc_x, loc_y = loc_3d_to_2d(self.region, self.region_3d, ob_loc, ratio_w, ratio_h) dim_x, dim_y = blf.dimensions(fontid, size_fmt) blf.position(fontid, loc_x - dim_x / 2, loc_y - dim_y / 2, 0.0) blf.draw(fontid, size_fmt)
def func_constrain_axis_mmb(self, context, key, value, angle): if len(self.tab) > 1: angle = 0 if key == 'MIDDLEMOUSE': if value == 'PRESS': if self.handle_axes == None: args = (self, context, angle) self.handle_axes = bpy.types.SpaceSequenceEditor.draw_handler_add( draw_axes, args, 'PREVIEW', 'POST_PIXEL') self.choose_axis = True self.pos_clic = self.mouse_pos if value == 'RELEASE': self.choose_axis = False if self.pos_clic == self.mouse_pos: self.axis_x = self.axis_y = True if self.handle_axes: bpy.types.SpaceSequenceEditor.draw_handler_remove( self.handle_axes, 'PREVIEW') self.handle_axes = None if self.choose_axis: vec_axis_z = Vector((0, 0, 1)) vec_axis_x = Vector((1, 0, 0)) vec_axis_x.rotate(Quaternion(vec_axis_z, math.radians(angle))) vec_axis_x = vec_axis_x.to_2d() vec_axis_y = Vector((0, 1, 0)) vec_axis_y.rotate(Quaternion(vec_axis_z, math.radians(angle))) vec_axis_y = vec_axis_y.to_2d() ang_x = math.degrees( vec_axis_x.angle(self.mouse_pos - self.center_area)) ang_y = math.degrees( vec_axis_y.angle(self.mouse_pos - self.center_area)) if ang_x > 90: ang_x = 180 - ang_x if ang_y > 90: ang_y = 180 - ang_y if ang_x < ang_y: self.axis_x = True self.axis_y = False else: self.axis_x = False self.axis_y = True
def sun_animation_points(gravity_direction: Vector, north_direction: Vector, scene_bbox: Vector, radius: float, points_count: int) -> List[Vector]: """Sample sun lamp position points. Arguments: gravity_direction {Vector} -- world's gravity direction vector north_direction {Vector} -- world's north direction vector scene_center {Vector} -- current scene center radius {float} -- radius of the animation points_count {int} -- desired number of points Raises: ValueError: if gravity and north directions aren't orthogonal Returns: List[Vector] -- list of animation points """ # TODO add more realistic sun paths using geolocation and seasons ? # sanity check: axes must be orthogonal angle = round(gravity_direction.angle(north_direction), 2) if angle != round((pi / 2), 2): msg = "`gravity_direction` and `north_direction` aren't orthogonal (angle={})!".format( degrees(angle)) logger.error(msg) raise ValueError(msg) center = scene_bbox.floor_center # scene_center # get the half-vector h = -gravity_direction - north_direction h.normalize() # get the rotation axis axis = h.copy() axis.rotate(Quaternion(north_direction.cross(gravity_direction), pi / 2)) b = h.copy() b.rotate(Quaternion(axis, pi / 2)) pts = [] for i in range(points_count * 2): theta = 2 * pi * (i / (points_count * 2)) # circle x = center.x + radius * cos(theta) * b.x + radius * sin(theta) * h.x y = center.y + radius * cos(theta) * b.y + radius * sin(theta) * h.y z = center.z + radius * cos(theta) * b.z + radius * sin(theta) * h.z if z > scene_bbox.z_min: continue p = Vector((x, y, z)) - center pts.append(p) #bpy.ops.mesh.primitive_uv_sphere_add(radius=0.01, location=p) return pts
def get_direction_of_verts(a, b): direction = (a - b).normalized() axis_align = Vector((0.0, 0.0, 1.0)) angle = axis_align.angle(direction) axis = axis_align.cross(direction) q = Quaternion(axis, angle) return q.to_euler('XYZ')
def set_bonded(atom,b): """Sets the positions of all atoms bonded to atom. atom: Atom object rotate: Boolean defining whether or not to rotate tetrahedron around the z-axis b: bond length """ x = 0 y = 0 z = 0 b = b*sqrt(atom.radius) bonds = atom.bonds #Differences in position between central atom and bonded atom for item in bonds: if isinstance(item, Atom): x = atom.pos[0] - item.pos[0] y = atom.pos[1] - item.pos[1] z = atom.pos[2] - item.pos[2] break n = math.radians(109.5) n2 = pi*2/3 #Represents tetrahedron base centered at (0,0,0) v1 = Vector((b*sin(n),0,b*cos(n))) v2 = Vector((b*cos(n2),b*sin(n2),b*cos(n))) v3 = Vector((b*cos(2*n2),b*sin(2*n2),b*cos(n))) offset = (Vector((atom.pos))) top= Vector((0,0,-b)) diff = Vector((x,y,z)) axis = top.cross(diff) a = -(top.angle(diff, 0)) m = Matrix.Rotation(a,3,axis) #Rotates the tetrahedron base to align itself with the starting vector v1 = v1*m v2 = v2*m v3 = v3*m #Sets the positions of bonded atoms to locations in the tetrahedron, scales bond lengths and moves tetrahedron if len(bonds) >1: if isinstance(bonds[1], Atom): bonds[1].pos = v1*sqrt(bonds[1].radius)+offset if len(bonds) >2: if isinstance(bonds[2], Atom): bonds[2].pos = v2*sqrt(bonds[2].radius)+offset if len(bonds)>3: if isinstance(bonds[3],Atom): bonds[3].pos = v3*sqrt(bonds[3].radius)+offset
def calcFaceByNormal(self, points, normal, face): a = [ t - u for t,u in zip(points[1], points[0])] # vector a = point[1] - point[0] b = [ t - u for t,u in zip(points[2], points[0])] # vector b = point[2] - point[0] # normal = a x b = (a2b3-a3b2)i-(a1b3-a3b1)j+(a1b2-a2b1)k. myNormal = Vector([a[1]*b[2] - a[2]*b[1], a[2]*b[0] - a[0]*b[2], a[0]*b[1] - a[1]*b[0]]) angle = myNormal.angle(Vector(normal)) # if vectors are in opposite direction change points order if angle > math.pi/2 or angle < -math.pi/2: return (face[1], face[0], face[2]) return face
def getValues(): '''Return mesh data values (z, slope or az) for classification''' scn = bpy.context.scene obj = bpy.context.view_layer.objects.active #make a temp mesh with modifiers apply #mesh = obj.data #modifiers not apply mesh = obj.to_mesh(scn, apply_modifiers=True, settings='PREVIEW') mesh.transform(obj.matrix_world) # mode = scn.analysisMode if mode == 'HEIGHT': values = [vertex.co.z for vertex in mesh.vertices] elif mode == 'SLOPE': z = Vector((0, 0, 1)) m = obj.matrix_world values = [ math.degrees(z.angle(m * face.normal)) for face in mesh.polygons ] elif mode == 'ASPECT': y = Vector((0, 1, 0)) m = obj.matrix_world #values = [math.degrees(y.angle(m * face.normal)) for face in mesh.polygons] values = [] for face in mesh.polygons: normal = face.normal.copy() normal.z = 0 #project vector into XY plane try: a = math.degrees(y.angle(m * normal)) except ValueError: pass #zero length vector as no angle else: #returned angle is between 0° (north) to 180° (south) #we must correct it to get angle between 0 to 360° if normal.x < 0: a = 360 - a values.append(a) values.sort() #remove temp mesh bpy.data.meshes.remove(mesh) return values
def getValues(): '''Return mesh data values (z, slope or az) for classification''' scn = bpy.context.scene obj = scn.objects.active #make a temp mesh with modifiers apply #mesh = obj.data #modifiers not apply mesh = obj.to_mesh(scn, apply_modifiers=True, settings='PREVIEW') mesh.transform(obj.matrix_world) # mode = scn.analysisMode if mode == 'HEIGHT': values = [vertex.co.z for vertex in mesh.vertices] elif mode == 'SLOPE': z = Vector((0,0,1)) m = obj.matrix_world values = [math.degrees(z.angle(m * face.normal)) for face in mesh.polygons] elif mode == 'ASPECT': y = Vector((0,1,0)) m = obj.matrix_world #values = [math.degrees(y.angle(m * face.normal)) for face in mesh.polygons] values = [] for face in mesh.polygons: normal = face.normal.copy() normal.z = 0 #project vector into XY plane try: a = math.degrees(y.angle(m * normal)) except ValueError: pass#zero length vector as no angle else: #returned angle is between 0° (north) to 180° (south) #we must correct it to get angle between 0 to 360° if normal.x <0: a = 360 - a values.append(a) values.sort() #remove temp mesh bpy.data.meshes.remove(mesh) return values
def calcRotAngle(self, axis, nor_x, nor_y, nor_z): theta = 0 vector_z = Vector((0.0, 0.0, 1.0)) vector_n = None vector_cross = None if axis == 'X': vector_n = Vector((0.0, nor_y, nor_z)) theta = vector_z.angle(vector_n, 999) vector_cross = vector_n.cross(vector_z) if vector_cross.x < 0: theta = -(theta) elif axis == 'Y': vector_n = Vector((nor_x, 0.0, nor_z)) theta = vector_z.angle(vector_n, 999) vector_cross = vector_n.cross(vector_z) if vector_cross.y < 0: theta = -(theta) else: pass return theta
def vec_roll_to_mat3(vec, roll): target = Vector((0, 1, 0)) nor = vec.normalized() axis = target.cross(nor) if axis.dot(axis) > 0.000001: axis.normalize() theta = target.angle(nor) bMatrix = Matrix.Rotation(theta, 3, axis) else: updown = 1 if target.dot(nor) > 0 else -1 bMatrix = Matrix.Scale(updown, 3) rMatrix = Matrix.Rotation(roll, 3, nor) mat = rMatrix * bMatrix return mat
def get_angle_signed(v1: Vector, v2: Vector, n: Vector) -> float: # Implementing this idea: # https://math.stackexchange.com/questions/1027476/calculating-clockwise-anti-clockwise-angles-from-a-point matrix: Matrix = Matrix((v1, v2, n)) matrix.transpose() det = matrix.determinant() angle = v1.angle(v2) if det < 0: # clockwise return angle elif det > 0: # anticlockwise return -angle else: return 0
def vec_roll_to_mat3(vec, roll): target = Vector((0,1,0)) nor = vec.normalized() axis = target.cross(nor) if axis.dot(axis) > 0.000001: axis.normalize() theta = target.angle(nor) bMatrix = Matrix.Rotation(theta, 3, axis) else: updown = 1 if target.dot(nor) > 0 else -1 bMatrix = Matrix.Scale(updown, 3) rMatrix = Matrix.Rotation(roll, 3, nor) mat = rMatrix * bMatrix return mat
def vec_roll_to_quat(vec, roll): vec.normalize() target = Vector((0, 1, 0)) axis = target.cross(vec) if (axis.dot(axis) > 1.0e-9): axis.normalize theta = target.angle(vec) q = Quaternion(axis, theta) else: if (dot(axis, vec) > 0): q = Quaternion() else: q = Quaternion((0, 0, 0, 1)) q_roll = Quaternion(vec, roll) return q_roll * q
def vec_roll_to_mat3(vec, roll): target = Vector((0, 0.1, 0)) nor = vec.normalized() axis = target.cross(nor) if axis.dot(axis) > 0.0000000001: # this seems to be the problem for some bones, no idea how to fix axis.normalize() theta = target.angle(nor) bMatrix = Matrix.Rotation(theta, 3, axis) else: updown = 1 if target.dot(nor) > 0 else -1 bMatrix = Matrix.Scale(updown, 3) bMatrix[2][2] = 1.0 rMatrix = Matrix.Rotation(roll, 3, nor) mat = rMatrix @ bMatrix return mat
def getScreenLookAxis(context): scene = context.scene region = context.region rv3d = context.region_data coord = region.width/2.0, region.height/2.0; base_view = Vector((0.0, 1.0, 0.0)); # base_view = Vector((0.0, 0.0, -1.0)); view_vector = view3d_utils.region_2d_to_vector_3d(region, rv3d, coord); view_vector.normalize(); axis = base_view.cross(view_vector); angle = base_view.angle(view_vector); # print('VIEW VECTOR ::: ', view_vector); axis.normalize(); return axis, math.degrees(angle);
def generate_facet_data(verts, faces, face_color, vector_light): out_verts = [] out_vcols = [] concat_verts = out_verts.extend concat_vcols = out_vcols.extend for face in faces: vecs = [verts[j] for j in face] concat_verts(vecs) normal_no = Vector(normal(*vecs)) normal_no = (normal_no.angle(vector_light, 0)) / pi r = (normal_no * face_color[0]) - 0.1 g = (normal_no * face_color[1]) - 0.1 b = (normal_no * face_color[2]) - 0.1 vcol = (r + 0.2, g + 0.2, b + 0.2, 1.0) concat_vcols([vcol, vcol, vcol]) return out_verts, out_vcols
def too_unwarp(obj): dg = bpy.context.evaluated_depsgraph_get() lq = obj.evaluated_get(dg) cam = bpy.data.objects['Camera'] ng = bpy.context.evaluated_depsgraph_get() cq = cam.evaluated_get(ng) cam_direction = cq.matrix_world.to_quaternion() @ Vector((0.0, 0.0, -1.0)) mat = lq.matrix_world nos = [] for v in lq.data.vertices: nos.append(mat @ v.normal) mean = Vector(np.mean(nos, axis=0)) for v in nos: ndc = mean.angle(v) if ndc > math.radians(70): return False if cam_direction.angle(mean) < math.radians(110): return False return True
def compute_edge_angles_degrees(ob,ids): me = ob.data # Get a BMesh representation bm = bmesh.new() # create an empty BMesh bm.from_mesh(me) # fill it in from a Mesh all_angles = [] all_degrees = np.zeros_like(ids) for i, id in enumerate(ids): edges = [] prev_edge=0 #print("Searching all edges for vert: ", id) for edge in bm.edges: if (edge.verts[0].index == id or edge.verts[1].index == id): #print("edge found:", edge) edges.append(edge) if (isVisibleEdge(edge,ids)): all_degrees[i]+=1 else: continue for edge in edges: if (prev_edge == 0): prev_edge = edge continue #print("edge pair: ", prev_edge, edge) #edge.verts[0].index v00 = edge.verts[0] v01 = edge.verts[1] v10 = prev_edge.verts[0] v11 = prev_edge.verts[1] v0 = Vector(v00.co) - Vector(v01.co) v1 = Vector(v10.co) - Vector(v11.co) #print("Angle: ", math.degrees(v0.angle(v1))) all_angles.append(v0.angle(v1)) prev_edge = edge #print("All degrees: ",all_degrees) return all_angles, all_degrees
def from_other(cls, obj): """Constructs a Spherical object from a Quaternion, Euler, or Matrix rotation object from the mathutils library. """ if type(obj) is cls: return obj obj = to_quat(obj) # first, rotate the +Z unit vector by the object to replicate the effect of rot_quat without roll_quat z_axis = Vector((0, 0, 1)) z_axis.rotate(obj) # calculate the inverse of rot_quat, which is the rotation that moves the new position of the unit vector to # its original position on the +Z axis inv_rot_quat = Quaternion(z_axis.cross(Vector((0, 0, 1))), z_axis.angle(Vector((0, 0, 1)))) # extract roll_quat by left-multiplying by the inverse of rot_quat roll_quat = inv_rot_quat @ obj # calculate theta and phi from the new position of the unit vector, as well as roll directly from roll_quat theta = np.arctan2(z_axis.y, z_axis.x) phi = np.arccos(np.clip(z_axis.z, -1, 1)) roll = roll_quat.to_euler().z - theta return cls(theta, phi, roll)
def extrude(bm,faces,m_x,m_y,m_z): operat_faces=faces n = Vector((0,0,0)) for i in operate_faces: #print("face:",i.normal) n += i.normal r = bmesh.ops.extrude_face_region(bm,geom=operate_faces) verts = [e for e in r['geom'] if isinstance(e, bmesh.types.BMVert)] faces = [e for e in r['geom'] if isinstance(e, bmesh.types.BMFace)] clear_select(bm) z = mathutils.Vector((0,0,1)) axis = n.cross(z) angle = n.angle(z) R = mathutils.Matrix.Rotation(angle,4,axis) bmesh.ops.translate(bm, vec = Vector((m_x,m_y,m_z)),space=R,verts = verts ) bmesh.ops.delete(bm, geom=operate_faces, context=5) return faces
def modal_rotate_tool(self, context, eventd): cx,cy = self.action_center mx,my = eventd['mouse'] px,py = self.prev_pos #mode_pos if eventd['press'] in {'RET', 'NUMPAD_ENTER', 'LEFTMOUSE'}: self.tool_fn('commit', eventd) return 'main' if eventd['press'] in {'ESC', 'RIGHTMOUSE'}: self.tool_fn('undo', eventd) return 'main' if eventd['type'] == 'MOUSEMOVE': vp = Vector((px-cx,py-cy,0)) vm = Vector((mx-cx,my-cy,0)) ang = vp.angle(vm) * (-1 if vp.cross(vm).z<0 else 1) self.tool_rot += ang self.tool_fn(self.tool_rot, eventd) self.prev_pos = (mx,my) return '' return ''
def DEF_atom_pdb_main(use_mesh,Ball_azimuth,Ball_zenith, Ball_radius_factor,radiustype,Ball_distance_factor, use_stick,Stick_sectors,Stick_diameter,put_to_center, use_camera,use_lamp,path_datafile): # The list of all atoms as read from the PDB file. all_atoms = [] # The list of all sticks. all_sticks = [] # List of materials atom_material_list = [] # A list of ALL objects which are loaded (needed for selecting the loaded # structure. atom_object_list = [] # ------------------------------------------------------------------------ # INITIALIZE THE ELEMENT LIST ATOM_PDB_ELEMENTS[:] = [] for item in ATOM_PDB_ELEMENTS_DEFAULT: # All three radii into a list radii = [item[4],item[5],item[6]] # The handling of the ionic radii will be done later. So far, it is an # empty list. radii_ionic = [] li = CLASS_atom_pdb_Elements(item[0],item[1],item[2],item[3], radii,radii_ionic) ATOM_PDB_ELEMENTS.append(li) # ------------------------------------------------------------------------ # READING DATA OF ATOMS if DEF_atom_pdb_custom_datafile(path_datafile): print("Custom data file is loaded.") # Open the file ... ATOM_PDB_FILEPATH_p = io.open(ATOM_PDB_FILEPATH, "r") #Go to the line, in which "ATOM" or "HETATM" appears. for line in ATOM_PDB_FILEPATH_p: split_list = line.split(' ') if "ATOM" in split_list[0]: break if "HETATM" in split_list[0]: break j = 0 # This is in fact an endless 'while loop', ... while j > -1: # ... the loop is broken here (EOF) ... if line == "": break # If there is a "TER" we need to put empty entries into the lists # in order to not destroy the order of atom numbers and same numbers # used for sticks. "TER? What is that?" TER indicates the end of a # list of ATOM/HETATM records for a chain. if "TER" in line: short_name = "TER" name = "TER" radius = 0.0 color = [0,0,0] location = Vector((0,0,0)) # Append the TER into the list. Material remains empty so far. all_atoms.append(CLASS_atom_pdb_atom(short_name, name, location, radius, color,[])) # If 'ATOM or 'HETATM' appears in the line then do ... elif "ATOM" in line or "HETATM" in line: # What follows is due to deviations which appear from PDB to # PDB file. It is very special. PLEASE, DO NOT CHANGE! From here ... short_name = line[13:14] if short_name.isupper() == True: if line[14:15].islower() == True: short_name = short_name + line[14:15] else: short_name = line[12:13] if short_name.isupper() == True: if line[13:14].islower() == True: short_name = short_name + line[13:14] # ... to here. # Go through all elements and find the element of the current atom. FLAG_FOUND = False for element in ATOM_PDB_ELEMENTS: if str.upper(short_name) == str.upper(element.short_name): # Give the atom its proper names, color and radius: short_name = str.upper(element.short_name) name = element.name # int(radiustype) => type of radius: # pre-defined (0), atomic (1) or van der Waals (2) radius = float(element.radii[int(radiustype)]) color = element.color FLAG_FOUND = True break # Is it a vacancy or an 'unknown atom' ? if FLAG_FOUND == False: # Give this atom also a name. If it is an 'X' then it is a # vacancy. Otherwise ... if "X" in short_name: short_name = "VAC" name = "Vacancy" radius = float(ATOM_PDB_ELEMENTS[-3].radii[int(radiustype)]) color = ATOM_PDB_ELEMENTS[-3].color # ... take what is written in the PDB file. These are somewhat # unknown atoms. This should never happen, the element list is # almost complete. However, we do this due to security reasons. else: short_name = str.upper(short_name) name = str.upper(short_name) radius = float(ATOM_PDB_ELEMENTS[-2].radii[int(radiustype)]) color = ATOM_PDB_ELEMENTS[-2].color # x,y and z are at fixed positions in the PDB file. x = float(line[30:38].rsplit()[0]) y = float(line[38:46].rsplit()[0]) z = float(line[46:55].rsplit()[0]) location = Vector((x,y,z)) j += 1 # Append the atom to the list. Material remains empty so far. all_atoms.append(CLASS_atom_pdb_atom(short_name, name, location, radius, color,[])) line = ATOM_PDB_FILEPATH_p.readline() line = line[:-1] ATOM_PDB_FILEPATH_p.close() # From above it can be clearly seen that j is now the number of all atoms. Number_of_total_atoms = j # ------------------------------------------------------------------------ # MATERIAL PROPERTIES FOR ATOMS # The list that contains info about all types of atoms is created # here. It is used for building the material properties for # instance (see below). atom_all_types_list = [] for atom in all_atoms: FLAG_FOUND = False for atom_type in atom_all_types_list: # If the atom name is already in the list, FLAG on 'True'. if atom_type[0] == atom.name: FLAG_FOUND = True break # No name in the current list has been found? => New entry. if FLAG_FOUND == False: # Stored are: Atom label (e.g. 'Na'), the corresponding atom # name (e.g. 'Sodium') and its color. atom_all_types_list.append([atom.name, atom.element, atom.color]) # The list of materials is built. # Note that all atoms of one type (e.g. all hydrogens) get only ONE # material! This is good because then, by activating one atom in the # Blender scene and changing the color of this atom, one changes the color # of ALL atoms of the same type at the same time. # Create first a new list of materials for each type of atom # (e.g. hydrogen) for atom_type in atom_all_types_list: material = bpy.data.materials.new(atom_type[1]) material.name = atom_type[0] material.diffuse_color = atom_type[2] atom_material_list.append(material) # Now, we go through all atoms and give them a material. For all atoms ... for atom in all_atoms: # ... and all materials ... for material in atom_material_list: # ... select the correct material for the current atom via # comparison of names ... if atom.name in material.name: # ... and give the atom its material properties. # However, before we check, if it is a vacancy, because then it # gets some additional preparation. The vacancy is represented # by a transparent cube. if atom.name == "Vacancy": material.transparency_method = 'Z_TRANSPARENCY' material.alpha = 1.3 material.raytrace_transparency.fresnel = 1.6 material.raytrace_transparency.fresnel_factor = 1.6 material.use_transparency = True # The atom gets its properties. atom.material = material # ------------------------------------------------------------------------ # READING DATA OF STICKS # Open the PDB file again such that the file pointer is in the first # line ... . Stupid, I know ... ;-) ATOM_PDB_FILEPATH_p = io.open(ATOM_PDB_FILEPATH, "r") split_list = line.split(' ') # Go to the first entry if "CONECT" not in split_list[0]: for line in ATOM_PDB_FILEPATH_p: split_list = line.split(' ') if "CONECT" in split_list[0]: break Number_of_sticks = 0 sticks_double = 0 j = 0 # This is in fact an endless while loop, ... while j > -1: # ... which is broken here (EOF) ... if line == "": break # ... or here, when no 'CONECT' appears anymore. if "CONECT" not in line: break # The strings of the atom numbers do have a clear position in the file # (From 7 to 12, from 13 to 18 and so on.) and one needs to consider # this. One could also use the split function but then one gets into # trouble if there are many atoms: For instance, it may happen that one # has # CONECT 11111 22244444 # # In Fact it means that atom No. 11111 has a connection with atom # No. 222 but also with atom No. 44444. The split function would give # me only two numbers (11111 and 22244444), which is wrong. # Cut spaces from the right and 'CONECT' at the beginning line = line.rstrip() line = line[6:] # Amount of loops length = len(line) loops = int(length/5) # List of atoms atom_list = [] for i in range(loops): number = line[5*i:5*(i+1)].rsplit() if number != []: if number[0].isdigit() == True: atom_number = int(number[0]) atom_list.append(atom_number) # The first atom is connected with all the others in the list. atom1 = atom_list[0] # For all the other atoms in the list do: for each_atom in atom_list[1:]: # The second, third, ... partner atom atom2 = each_atom # Note that in a PDB file, sticks of one atom pair can appear a # couple of times. (Only god knows why ...) # So, does a stick between the considered atoms already exist? FLAG_BAR = False for k in range(Number_of_sticks): if ((all_sticks[k].atom1 == atom1 and all_sticks[k].atom2 == atom2) or (all_sticks[k].atom2 == atom1 and all_sticks[k].atom1 == atom2)): sticks_double += 1 # If yes, then FLAG on 'True'. FLAG_BAR = True break # If the stick is not yet registered (FLAG_BAR == False), then # register it! if FLAG_BAR == False: all_sticks.append(CLASS_atom_pdb_stick(atom1,atom2)) Number_of_sticks += 1 j += 1 line = ATOM_PDB_FILEPATH_p.readline() line = line.rstrip() ATOM_PDB_FILEPATH_p.close() # So far, all atoms and sticks have been registered. # ------------------------------------------------------------------------ # TRANSLATION OF THE STRUCTURE TO THE ORIGIN # It may happen that the structure in a PDB file already has an offset # If chosen, the structure is first put into the center of the scene # (the offset is substracted). if put_to_center == True: sum_vec = Vector((0.0,0.0,0.0)) # Sum of all atom coordinates sum_vec = sum([atom.location for atom in all_atoms], sum_vec) # Then the average is taken sum_vec = sum_vec / Number_of_total_atoms # After, for each atom the center of gravity is substracted for atom in all_atoms: atom.location -= sum_vec # ------------------------------------------------------------------------ # SCALING # Take all atoms and adjust their radii and scale the distances. for atom in all_atoms: atom.location *= Ball_distance_factor # ------------------------------------------------------------------------ # DETERMINATION OF SOME GEOMETRIC PROPERTIES # In the following, some geometric properties of the whole object are # determined: center, size, etc. sum_vec = Vector((0.0,0.0,0.0)) # First the center is determined. All coordinates are summed up ... sum_vec = sum([atom.location for atom in all_atoms], sum_vec) # ... and the average is taken. This gives the center of the object. object_center_vec = sum_vec / Number_of_total_atoms # Now, we determine the size.The farest atom from the object center is # taken as a measure. The size is used to place well the camera and light # into the scene. object_size_vec = [atom.location - object_center_vec for atom in all_atoms] object_size = 0.0 object_size = max(object_size_vec).length # ------------------------------------------------------------------------ # CAMERA AND LAMP camera_factor = 15.0 # If chosen a camera is put into the scene. if use_camera == True: # Assume that the object is put into the global origin. Then, the # camera is moved in x and z direction, not in y. The object has its # size at distance math.sqrt(object_size) from the origin. So, move the # camera by this distance times a factor of camera_factor in x and z. # Then add x, y and z of the origin of the object. object_camera_vec = Vector((math.sqrt(object_size) * camera_factor, 0.0, math.sqrt(object_size) * camera_factor)) camera_xyz_vec = object_center_vec + object_camera_vec # Create the camera current_layers=bpy.context.scene.layers bpy.ops.object.camera_add(view_align=False, enter_editmode=False, location=camera_xyz_vec, rotation=(0.0, 0.0, 0.0), layers=current_layers) # Some properties of the camera are changed. camera = bpy.context.scene.objects.active camera.name = "A_camera" camera.data.name = "A_camera" camera.data.lens = 45 camera.data.clip_end = 500.0 # Here the camera is rotated such it looks towards the center of # the object. The [0.0, 0.0, 1.0] vector along the z axis z_axis_vec = Vector((0.0, 0.0, 1.0)) # The angle between the last two vectors angle = object_camera_vec.angle(z_axis_vec, 0) # The cross-product of z_axis_vec and object_camera_vec axis_vec = z_axis_vec.cross(object_camera_vec) # Rotate 'axis_vec' by 'angle' and convert this to euler parameters. # 4 is the size of the matrix. euler = Matrix.Rotation(angle, 4, axis_vec).to_euler() camera.rotation_euler = euler # Rotate the camera around its axis by 90° such that we have a nice # camera position and view onto the object. bpy.ops.transform.rotate(value=(90.0*2*math.pi/360.0,), axis=object_camera_vec, constraint_axis=(False, False, False), constraint_orientation='GLOBAL', mirror=False, proportional='DISABLED', proportional_edit_falloff='SMOOTH', proportional_size=1, snap=False, snap_target='CLOSEST', snap_point=(0, 0, 0), snap_align=False, snap_normal=(0, 0, 0), release_confirm=False) # This does not work, I don't know why. # #for area in bpy.context.screen.areas: # if area.type == 'VIEW_3D': # area.spaces[0].region_3d.view_perspective = 'CAMERA' # Here a lamp is put into the scene, if chosen. if use_lamp == True: # This is the distance from the object measured in terms of % # of the camera distance. It is set onto 50% (1/2) distance. lamp_dl = math.sqrt(object_size) * 15 * 0.5 # This is a factor to which extend the lamp shall go to the right # (from the camera point of view). lamp_dy_right = lamp_dl * (3.0/4.0) # Create x, y and z for the lamp. object_lamp_vec = Vector((lamp_dl,lamp_dy_right,lamp_dl)) lamp_xyz_vec = object_center_vec + object_lamp_vec # Create the lamp current_layers=bpy.context.scene.layers bpy.ops.object.lamp_add (type = 'POINT', view_align=False, location=lamp_xyz_vec, rotation=(0.0, 0.0, 0.0), layers=current_layers) # Some properties of the lamp are changed. lamp = bpy.context.scene.objects.active lamp.data.name = "A_lamp" lamp.name = "A_lamp" lamp.data.distance = 500.0 lamp.data.energy = 3.0 lamp.data.shadow_method = 'RAY_SHADOW' bpy.context.scene.world.light_settings.use_ambient_occlusion = True bpy.context.scene.world.light_settings.ao_factor = 0.2 # ------------------------------------------------------------------------ # SOME OUTPUT ON THE CONSOLE print() print() print() print(ATOM_PDB_STRING) print() print("Total number of atoms : " + str(Number_of_total_atoms)) print("Total number of sticks : " + str(Number_of_sticks)) print("Center of object : ", object_center_vec) print("Size of object : ", object_size) print() # ------------------------------------------------------------------------ # SORTING THE ATOMS # Lists of atoms of one type are created. Example: # draw_all_atoms = [ data_hydrogen,data_carbon,data_nitrogen ] # data_hydrogen = [["Hydrogen", Material_Hydrogen, Vector((x,y,z)), 109], ...] draw_all_atoms = [] # Go through the list which contains all types of atoms. It is the list, # which has been created on the top during reading the PDB file. # Example: atom_all_types_list = ["hydrogen", "carbon", ...] for atom_type in atom_all_types_list: # Don't draw 'TER atoms'. if atom_type[0] == "TER": continue # This is the draw list, which contains all atoms of one type (e.g. # all hydrogens) ... draw_all_atoms_type = [] # Go through all atoms ... for atom in all_atoms: # ... select the atoms of the considered type via comparison ... if atom.name == atom_type[0]: # ... and append them to the list 'draw_all_atoms_type'. draw_all_atoms_type.append([atom.name, atom.material, atom.location, atom.radius]) # Now append the atom list to the list of all types of atoms draw_all_atoms.append(draw_all_atoms_type) # ------------------------------------------------------------------------ # DRAWING THE ATOMS # This is the number of all atoms which are put into the scene. bpy.ops.object.select_all(action='DESELECT') # For each list of atoms of ONE type (e.g. Hydrogen) for draw_all_atoms_type in draw_all_atoms: # Create first the vertices composed of the coordinates of all # atoms of one type atom_vertices = [] for atom in draw_all_atoms_type: # In fact, the object is created in the World's origin. # This is why 'object_center_vec' is substracted. At the end # the whole object is translated back to 'object_center_vec'. atom_vertices.append( atom[2] - object_center_vec ) # Build the mesh atom_mesh = bpy.data.meshes.new("Mesh_"+atom[0]) atom_mesh.from_pydata(atom_vertices, [], []) atom_mesh.update() new_atom_mesh = bpy.data.objects.new(atom[0], atom_mesh) bpy.context.scene.objects.link(new_atom_mesh) # Now, build a representative sphere (atom) current_layers=bpy.context.scene.layers if atom[0] == "Vacancy": bpy.ops.mesh.primitive_cube_add( view_align=False, enter_editmode=False, location=(0.0, 0.0, 0.0), rotation=(0.0, 0.0, 0.0), layers=current_layers) else: # NURBS balls if use_mesh == False: bpy.ops.surface.primitive_nurbs_surface_sphere_add( view_align=False, enter_editmode=False, location=(0,0,0), rotation=(0.0, 0.0, 0.0), layers=current_layers) # UV balls else: bpy.ops.mesh.primitive_uv_sphere_add( segments=Ball_azimuth, ring_count=Ball_zenith, size=1, view_align=False, enter_editmode=False, location=(0,0,0), rotation=(0, 0, 0), layers=current_layers) ball = bpy.context.scene.objects.active ball.scale = (atom[3]*Ball_radius_factor,) * 3 if atom[0] == "Vacancy": ball.name = "Cube_"+atom[0] else: ball.name = "Ball (NURBS)_"+atom[0] ball.active_material = atom[1] ball.parent = new_atom_mesh new_atom_mesh.dupli_type = 'VERTS' # The object is back translated to 'object_center_vec'. new_atom_mesh.location = object_center_vec atom_object_list.append(new_atom_mesh) print() # ------------------------------------------------------------------------ # DRAWING THE STICKS if use_stick == True and all_sticks != []: # Create a new material with the corresponding color. The # color is taken from the all_atom list, it is the last entry # in the data file (index -1). bpy.ops.object.material_slot_add() stick_material = bpy.data.materials.new(ATOM_PDB_ELEMENTS[-1].name) stick_material.diffuse_color = ATOM_PDB_ELEMENTS[-1].color vertices = [] faces = [] dl = 0.2 i = 0 # For all sticks, do ... for stick in all_sticks: # What follows is school mathematics! :-) v1 = all_atoms[stick.atom2-1].location v2 = all_atoms[stick.atom1-1].location dv = (v1 - v2) n = dv / dv.length # m = v1 - dv / 2.0 # UNUSED gamma = -n * v1 b = v1 + gamma * n n_b = b / b.length loops = int(dv.length / dl) for j in range(loops): g = v1 - n * dl / 2.0 - n * dl * j p1 = g + n_b * Stick_diameter p2 = g - n_b * Stick_diameter p3 = g - n_b.cross(n) * Stick_diameter p4 = g + n_b.cross(n) * Stick_diameter vertices.append(p1) vertices.append(p2) vertices.append(p3) vertices.append(p4) faces.append((i*4+0,i*4+2,i*4+1,i*4+3)) i += 1 mesh = bpy.data.meshes.new("Sticks") mesh.from_pydata(vertices, [], faces) mesh.update() new_mesh = bpy.data.objects.new("Sticks", mesh) bpy.context.scene.objects.link(new_mesh) current_layers = bpy.context.scene.layers stick_cylinder = DEF_atom_pdb_build_stick(Stick_diameter, dl, Stick_sectors) stick_cylinder.active_material = stick_material stick_cylinder.parent = new_mesh new_mesh.dupli_type = 'FACES' atom_object_list.append(new_mesh) # ------------------------------------------------------------------------ # SELECT ALL LOADED OBJECTS bpy.ops.object.select_all(action='DESELECT') obj = None for obj in atom_object_list: obj.select = True # activate the last selected object (perhaps another should be active?) if obj: bpy.context.scene.objects.active = obj print("\n\nAll atoms (%d) and sticks (%d) have been drawn - finished.\n\n" % (Number_of_total_atoms,Number_of_sticks)) return Number_of_total_atoms
def modal(self, context, event): context.area.tag_redraw() # Tooltip tooltip_text = "Shift: Scale Constraint; Ctrl+MouseWheel, Ctrl+Shift+MouseWheel, +-, Ctrl++, ctrl+-: Change Segments; Shift+LeftClick: Uniform Scale; C: CenterCursor; O: Orient on Surface; R: Rotate, S: Scale" context.area.header_text_set(tooltip_text) m_coords = event.mouse_region_x, event.mouse_region_y rv3d = context.region_data if event.type in {'MIDDLEMOUSE'}: # allow navigation return {'PASS_THROUGH'} elif event.type == 'O' and event.value == 'PRESS': if self.orient_on_surface: self.orient_on_surface = False else: self.orient_on_surface = True return {'RUNNING_MODAL'} elif event.type == 'C' and event.value == 'PRESS': if self.center_is_cursor: self.center_is_cursor = False else: self.center_is_cursor = True return {'RUNNING_MODAL'} elif event.type == 'M' and event.value == 'PRESS': if self.median_center: self.median_center = False else: self.median_center = True return {'RUNNING_MODAL'} elif event.type == 'R' and event.value == 'PRESS': if self.new_prim: if self.tool_mode == 'ROTATE': self.tool_mode = self.tool_mode_before_deform self.tool_mode_before_deform = None return {'RUNNING_MODAL'} else: self.deform_mouse_pos = m_coords self.tool_mode_before_deform = self.tool_mode self.tool_mode = 'ROTATE' else: return {'RUNNING_MODAL'} return {'RUNNING_MODAL'} elif event.type == 'S' and event.value == 'PRESS': if self.new_prim: if self.tool_mode == 'SCALE': self.tool_mode = self.tool_mode_before_deform self.tool_mode_before_deform = None return {'RUNNING_MODAL'} else: self.deform_mouse_pos = m_coords self.tool_mode_before_deform = self.tool_mode self.tool_mode = 'SCALE' else: return {'RUNNING_MODAL'} return {'RUNNING_MODAL'} elif event.type == 'Z' and event.value == 'PRESS' and event.ctrl: # delete object with Ctrl+Z if self.history_objects: del_obj = self.history_objects[-1][0] del self.history_objects[-1] # remove old primitive objs = bpy.data.objects objs.remove(objs[del_obj.name], True) self.new_prim = None self.hit_pos = None self.hit_dir = None self.prim_side_vec = None self.prim_front_vec = None self.tool_mode = 'IDLE' context.scene.update() context.area.tag_redraw() return {'RUNNING_MODAL'} elif event.type in {'WHEELUPMOUSE', 'WHEELDOWNMOUSE', 'NUMPAD_MINUS', 'MINUS', 'NUMPAD_PLUS', 'EQUAL'}: # just pass it if cloning if self.prim_type == 'Clone': if event.type in {'WHEELUPMOUSE', 'WHEELDOWNMOUSE'}: # allow navigation return {'PASS_THROUGH'} else: return {'RUNNING_MODAL'} # CHANGE SEGMENTS HERE if self.new_prim: if event.type in {'WHEELUPMOUSE', 'WHEELDOWNMOUSE'}: if event.ctrl: # Meka less/more segments if event.type == 'WHEELUPMOUSE': if self.prim_type == 'Sphere': if event.shift: self.sphere_segments[1] = self.sphere_segments[1] + 1 else: self.sphere_segments[0] = self.sphere_segments[0] + 1 elif self.prim_type == 'Capsule': if event.shift: self.capsule_segments[1] = self.capsule_segments[1] + 1 else: self.capsule_segments[0] = self.capsule_segments[0] + 1 else: self.circle_segments[0] += 1 else: if self.prim_type == 'Sphere': if event.shift: self.sphere_segments[1] = self.sphere_segments[1] - 1 self.sphere_segments[1] = max(self.sphere_segments[1], 3) else: self.sphere_segments[0] = self.sphere_segments[0] - 1 self.sphere_segments[0] = max(self.sphere_segments[0], 3) elif self.prim_type == 'Capsule': if event.shift: self.capsule_segments[1] = self.capsule_segments[1] - 1 self.capsule_segments[1] = max(self.capsule_segments[1], 1) else: self.capsule_segments[0] = self.capsule_segments[0] - 1 self.capsule_segments[0] = max(self.capsule_segments[0], 3) else: self.circle_segments[0] -= 1 self.circle_segments[0] = max(self.circle_segments[0], 3) else: # allow navigation return {'PASS_THROUGH'} elif event.type in {'NUMPAD_PLUS', 'EQUAL'} and event.value == 'PRESS': if self.prim_type == 'Sphere': if event.ctrl: self.sphere_segments[1] = self.sphere_segments[1] + 1 else: self.sphere_segments[0] = self.sphere_segments[0] + 1 elif self.prim_type == 'Capsule': if event.ctrl: self.capsule_segments[1] = self.capsule_segments[1] + 1 else: self.capsule_segments[0] = self.capsule_segments[0] + 1 else: self.circle_segments[0] += 1 elif event.type in {'NUMPAD_MINUS', 'MINUS'} and event.value == 'PRESS': if self.prim_type == 'Sphere': if event.ctrl: self.sphere_segments[1] = self.sphere_segments[1] - 1 self.sphere_segments[1] = max(self.sphere_segments[1], 3) else: self.sphere_segments[0] = self.sphere_segments[0] - 1 self.sphere_segments[0] = max(self.sphere_segments[0], 3) elif self.prim_type == 'Capsule': if event.ctrl: self.capsule_segments[1] = self.capsule_segments[1] - 1 self.capsule_segments[1] = max(self.capsule_segments[1], 1) else: self.capsule_segments[0] = self.capsule_segments[0] - 1 self.capsule_segments[0] = max(self.capsule_segments[0], 3) else: self.circle_segments[0] -= 1 self.circle_segments[0] = max(self.circle_segments[0], 3) if self.prim_type in {'Sphere','Capsule'}: del self.history_objects[-1] # If Edit Mode if self.edit_obj: bpy.ops.object.mode_set(mode='OBJECT', toggle=False) segments = self.sphere_segments if self.prim_type == 'Capsule': segments = self.capsule_segments # Replace Object Primitive if self.tool_mode == 'IDLE': self.new_prim = replace_prim(self.new_prim, segments, self.prim_type, context) else: self.new_prim = replace_prim(self.new_prim, segments, 'Circle', context) self.history_objects.append([self.new_prim, self.hit_pos]) # If Edit Mode if self.edit_obj: bpy.ops.object.select_all(action='DESELECT') self.new_prim.show_wire = True self.new_prim.show_all_edges = True self.edit_obj.select = True context.scene.objects.active = self.edit_obj bpy.ops.object.mode_set(mode='EDIT', toggle=False) else: del self.history_objects[-1] # If Edit Mode if self.edit_obj: bpy.ops.object.mode_set(mode='OBJECT', toggle=False) # Replace Object Primitive if self.tool_mode == 'IDLE': self.new_prim = replace_prim(self.new_prim, self.circle_segments, self.prim_type, context) else: if self.prim_type == 'Cube' or self.prim_type == 'Plane': self.new_prim = replace_prim(self.new_prim, self.circle_segments, 'Plane', context) else: self.new_prim = replace_prim(self.new_prim, self.circle_segments, 'Circle', context) self.history_objects.append([self.new_prim, self.hit_pos]) # If Edit Mode if self.edit_obj: bpy.ops.object.select_all(action='DESELECT') self.new_prim.show_wire = True self.new_prim.show_all_edges = True self.edit_obj.select = True context.scene.objects.active = self.edit_obj bpy.ops.object.mode_set(mode='EDIT', toggle=False) else: # allow navigation return {'PASS_THROUGH'} # FINISH THE TOOL! elif event.type in {'RIGHTMOUSE', 'ESC'}: # If Edit Mode. Join Primitives if self.edit_obj: bpy.ops.object.mode_set(mode='OBJECT', toggle=False) bpy.ops.object.select_all(action='DESELECT') for his_obj in self.history_objects: his_obj[0].select = True self.edit_obj.select = True context.scene.objects.active = self.edit_obj bpy.ops.object.join() bpy.ops.object.mode_set(mode='EDIT', toggle=False) # For non Edit Mode else: #bpy.ops.object.select_all(action='DESELECT') cursor_origin = context.scene.cursor_location.copy() for his_obj in self.history_objects: bpy.ops.object.select_all(action='DESELECT') his_obj[0].select = True # apply scale if self.prim_type != 'Clone': context.scene.objects.active = his_obj[0] bpy.ops.object.transform_apply(location=False, rotation=False, scale=True) # set object position to hit if his_obj[0].location != his_obj[1]: context.scene.cursor_location = his_obj[1].copy() bpy.ops.object.origin_set(type='ORIGIN_CURSOR') # apply/fix rotation if round(math.degrees(his_obj[0].rotation_euler.x)) in {-0.0, 0.0, 90.0, 180.0, -180.0, -90.0}: if round(math.degrees(his_obj[0].rotation_euler.y)) in {-0.0, 0.0, 90.0, 180.0, -180.0, -90.0}: if round(math.degrees(his_obj[0].rotation_euler.z)) in {-0.0, 0.0, 90.0, 180.0, -180.0, -90.0}: bpy.ops.object.transform_apply(location=False, rotation=True, scale=False) context.scene.cursor_location = cursor_origin bpy.ops.object.select_all(action='DESELECT') # clean clean(context, self) context.area.header_text_set() bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW') self.tool_mode == 'IDLE' return {'FINISHED'} # LEFTMOUSE CLICK if event.type == 'LEFTMOUSE': if self.tool_mode in {'ROTATE', 'SCALE'}: return {'RUNNING_MODAL'} if self.tool_mode == 'IDLE' and event.value == 'PRESS': do_create_prim = False # check if we found hit is_autoaxis = False # If Edit Mode if self.edit_obj: bpy.ops.object.mode_set(mode='OBJECT', toggle=False) # make raycast only one time! ray_result = ut_base.get_mouse_raycast(context, self.obj_matrices, m_coords) # If Edit Mode if self.edit_obj: bpy.ops.object.mode_set(mode='EDIT', toggle=False) if ray_result[0] and self.orient_on_surface and not self.center_is_cursor: self.hit_pos = ray_result[2].copy() self.hit_dir = ray_result[1].copy().normalized() do_create_prim = True else: # make auto pick according to 3d cursor if ray_result[0] and not self.orient_on_surface and not self.center_is_cursor: ray_result_auto = auto_pick(context, ray_result[2], event) else: ray_result_auto = auto_pick(context, None, event) if self.center_is_cursor: self.hit_pos = context.scene.cursor_location else: self.hit_pos = ray_result_auto[0].copy() self.hit_dir = ray_result_auto[1].copy().normalized() do_create_prim = True is_autoaxis = True # CREATE PRIMITIVE! if do_create_prim: # If Edit Mode if self.edit_obj: bpy.ops.object.mode_set(mode='OBJECT', toggle=False) # Do Create Primitive if self.prim_type == 'Sphere': new_prim = create_prim(context, event, self.hit_pos, self.hit_dir, self.sphere_segments, is_autoaxis, 'Circle', self) elif self.prim_type == 'Capsule': new_prim = create_prim(context, event, self.hit_pos, self.hit_dir, self.capsule_segments, is_autoaxis, 'Circle', self) elif self.prim_type in {'Cube','Plane'}: new_prim = create_prim(context, event, self.hit_pos, self.hit_dir, None, is_autoaxis, 'Plane', self) elif self.prim_type == 'Clone': new_prim = create_prim(context, event, self.hit_pos, self.hit_dir, None, is_autoaxis, 'Clone', self) else: new_prim = create_prim(context, event, self.hit_pos, self.hit_dir, self.circle_segments, is_autoaxis, 'Circle', self) self.new_prim = new_prim[0] self.history_objects.append([self.new_prim, self.hit_pos]) self.prim_side_vec = new_prim[1].copy() self.prim_front_vec = new_prim[2].copy() self.tool_mode = 'DRAW_1' # If Edit Mode if self.edit_obj: bpy.ops.object.select_all(action='DESELECT') self.new_prim.show_wire = True self.new_prim.show_all_edges = True self.edit_obj.select = True context.scene.objects.active = self.edit_obj bpy.ops.object.mode_set(mode='EDIT', toggle=False) # Change tool state to Draw_2. Create depth elif self.tool_mode == 'IDLE_2' and event.value == 'PRESS': # If Edit Mode if self.edit_obj: bpy.ops.object.mode_set(mode='OBJECT', toggle=False) # Replace Plane and Circle del self.history_objects[-1] if self.prim_type == 'Sphere': self.new_prim = replace_prim(self.new_prim, self.sphere_segments, self.prim_type, context) elif self.prim_type == 'Capsule': self.new_prim = replace_prim(self.new_prim, self.capsule_segments, self.prim_type, context) else: self.new_prim = replace_prim(self.new_prim, self.circle_segments, self.prim_type, context) self.history_objects.append([self.new_prim, self.hit_pos]) # If Edit Mode if self.edit_obj: bpy.ops.object.select_all(action='DESELECT') self.new_prim.show_wire = True self.new_prim.show_all_edges = True self.edit_obj.select = True context.scene.objects.active = self.edit_obj bpy.ops.object.mode_set(mode='EDIT', toggle=False) self.tool_mode = 'DRAW_2' if event.value == 'RELEASE': # change tool state. Wait for Draw_2 state if self.tool_mode == 'DRAW_1': # set to IDLE if this is Plane Primitive if self.prim_type in {'Plane', 'Circle', 'Clone'}: self.tool_mode = 'IDLE' return {'RUNNING_MODAL'} if self.new_prim.scale[0] == 0 or self.new_prim.scale[1] == 0: # remove primitive with 0,0,0 scale objs = bpy.data.objects del self.history_objects[-1] objs.remove(objs[self.new_prim.name], True) self.new_prim = None self.prim_side_vec = None self.prim_front_vec = None self.tool_mode = 'IDLE' else: self.tool_mode = 'IDLE_2' else: # go to standard state self.tool_mode = 'IDLE' # TOOL WORK # Draw Primitive X and Y if self.tool_mode == 'DRAW_1': plane_pos = ut_base.get_mouse_on_plane(context, self.hit_pos, self.hit_dir, m_coords) if plane_pos: if event.shift: if self.prim_type == 'Clone': self.new_prim.scale = self.objects_to_clone[0].scale.copy() else: # Make the same scale with Shift key new_dist = (plane_pos - self.hit_pos).length self.new_prim.scale[0] = new_dist self.new_prim.scale[1] = new_dist else: if self.prim_type == 'Clone': new_dist = (plane_pos - self.hit_pos).length self.new_prim.scale[0] = self.objects_to_clone[0].scale[0] * new_dist self.new_prim.scale[1] = self.objects_to_clone[0].scale[1] * new_dist self.new_prim.scale[2] = self.objects_to_clone[0].scale[2] * new_dist else: dist_x = mathu.geometry.distance_point_to_plane(plane_pos, self.hit_pos, self.prim_side_vec) dist_y = mathu.geometry.distance_point_to_plane(plane_pos, self.hit_pos, self.prim_front_vec) self.new_prim.scale[0] = abs(dist_x) self.new_prim.scale[1] = abs(dist_y) # Draw Primitive Z Depth elif self.tool_mode == 'DRAW_2': if event.shift: # Make the same scale with Shift key new_dist = self.new_prim.scale[0] if self.new_prim.scale[0] < self.new_prim.scale[1]: new_dist = self.new_prim.scale[1] if not self.median_center: self.new_prim.scale[2] = new_dist # set new location mult_vec = self.hit_dir.copy() mult_vec[0] *= (new_dist) mult_vec[1] *= (new_dist) mult_vec[2] *= (new_dist) self.new_prim.location = self.hit_pos + mult_vec else: self.new_prim.scale[2] = new_dist else: view_front_vec = rv3d.view_rotation * Vector((0.0, 0.0, -1.0)).normalized() plane_pos = ut_base.get_mouse_on_plane(context, self.hit_pos, view_front_vec, m_coords) new_dist = (plane_pos - self.hit_pos).length if not self.median_center: self.new_prim.scale[2] = abs(new_dist) / 2 # set new location mult_vec = self.hit_dir.copy() mult_vec[0] *= (new_dist / 2) mult_vec[1] *= (new_dist / 2) mult_vec[2] *= (new_dist / 2) self.new_prim.location = self.hit_pos + mult_vec else: self.new_prim.scale[2] = (abs(new_dist) / 2) * 2 # Rotate Primitive elif self.tool_mode == 'ROTATE': obj_pos_2d = view3d_utils.location_3d_to_region_2d(context.region, rv3d, self.new_prim.location) if obj_pos_2d: new_prim_mat = self.new_prim.matrix_world v1 = Vector(self.deform_mouse_pos) - obj_pos_2d v1 = Vector((v1[0], v1[1], 0)).normalized() v2 = Vector(m_coords) - obj_pos_2d v2 = Vector((v2[0], v2[1], 0)).normalized() rot_angle = v1.angle(v2) if rot_angle != 0: # check if negative angle if v1.cross(v2)[2] < 0: rot_angle = -rot_angle rot_axis = (new_prim_mat[0][2], new_prim_mat[1][2], new_prim_mat[2][2]) # if edit obj if self.edit_obj: bpy.ops.object.mode_set(mode='OBJECT', toggle=False) bpy.ops.object.select_all(action='DESELECT') act_temp = context.scene.objects.active #temp_sel = self.new_prim.select self.new_prim.select = True context.scene.objects.active = self.new_prim # do rotation bpy.ops.transform.rotate(value=rot_angle, axis=rot_axis) self.deform_mouse_pos = m_coords # if edit obj if self.edit_obj: self.new_prim.select = False context.scene.objects.active = act_temp bpy.ops.object.mode_set(mode='EDIT', toggle=False) # Rotate Primitive elif self.tool_mode == 'SCALE': obj_pos_2d = view3d_utils.location_3d_to_region_2d(context.region, rv3d, self.new_prim.location) if obj_pos_2d: v1 = Vector(self.deform_mouse_pos) - obj_pos_2d v2 = Vector(m_coords) - obj_pos_2d # do scale self.new_prim.scale *= (v2.length / v1.length) self.deform_mouse_pos = m_coords # fix position for non median primitives if not self.median_center and self.tool_mode_before_deform == 'IDLE' and self.prim_type != 'Clone': mult_vec = self.hit_dir.copy() mult_vec[0] *= (self.new_prim.scale[2]) mult_vec[1] *= (self.new_prim.scale[2]) mult_vec[2] *= (self.new_prim.scale[2]) self.new_prim.location = self.hit_pos + mult_vec return {'RUNNING_MODAL'}
def create_mesh(data_list, AFMdata, use_smooth, scale_size, scale_height, use_camera, use_lamp): # This is for the image name. path_list = AFMdata.datfile.strip('/').split('/') number_img = len(data_list) image_x_offset_gap = 10.0 * scale_size image_x_all = sum(AFMdata.x_size)*scale_size image_x_offset = -(image_x_all+image_x_offset_gap*(number_img-1)) / 2.0 # For each image do: for k, data in enumerate(data_list): size_x = AFMdata.x_pixel[k] size_y = AFMdata.y_pixel[k] image_scale = AFMdata.x_size[k] / float(AFMdata.x_pixel[k]) image_scale = image_scale * scale_size image_x_size = AFMdata.x_size[k] * scale_size image_x_offset += image_x_size / 2.0 image_name = path_list[-1] + "_" + AFMdata.channel[k][1] data_mesh = [] data_faces = [] #print("passed - create_mesh ---- 1") for i, line in enumerate(data): for j, pixel in enumerate(line): # The vertices data_mesh.append(Vector((float(i) * image_scale, float(j) * image_scale, float(pixel)*scale_height))) # The faces if i < size_y-1 and j < size_x-1: data_faces.append( [size_x*i+j , size_x*(i+1)+j, size_x*(i+1)+j+1, size_x*i+j+1 ]) #print("passed - create_mesh ---- 2") # Build the mesh surface_mesh = bpy.data.meshes.new("Mesh") surface_mesh.from_pydata(data_mesh, [], data_faces) surface_mesh.update() surface = bpy.data.objects.new(image_name, surface_mesh) bpy.context.scene.objects.link(surface) bpy.ops.object.select_all(action='DESELECT') surface.select = True bpy.ops.object.origin_set(type='ORIGIN_GEOMETRY') # sum((v.co for v in mesh.vertices), Vector()) / len(mesh.vertices) if use_smooth: for polygon in surface.data.polygons: polygon.use_smooth = True surface.location = Vector((0.0, image_x_offset, 0.0)) image_x_offset += image_x_size / 2.0 + image_x_offset_gap #print("passed - create_mesh ---- 3") object_center_vec = Vector((0.0,0.0,0.0)) object_size = (sum(AFMdata.x_size) * scale_size +image_x_offset_gap * (len(data_list)-1)) # ------------------------------------------------------------------------ # CAMERA AND LAMP camera_factor = 20.0 # If chosen a camera is put into the scene. if use_camera == True: # Assume that the object is put into the global origin. Then, the # camera is moved in x and z direction, not in y. The object has its # size at distance sqrt(object_size) from the origin. So, move the # camera by this distance times a factor of camera_factor in x and z. # Then add x, y and z of the origin of the object. object_camera_vec = Vector((sqrt(object_size) * camera_factor, 0.0, sqrt(object_size) * camera_factor)) camera_xyz_vec = object_center_vec + object_camera_vec # Create the camera current_layers=bpy.context.scene.layers camera_data = bpy.data.cameras.new("A_camera") camera_data.lens = 45 camera_data.clip_end = 50000.0 camera = bpy.data.objects.new("A_camera", camera_data) camera.location = camera_xyz_vec camera.layers = current_layers bpy.context.scene.objects.link(camera) # Here the camera is rotated such it looks towards the center of # the object. The [0.0, 0.0, 1.0] vector along the z axis z_axis_vec = Vector((0.0, 0.0, 1.0)) # The angle between the last two vectors angle = object_camera_vec.angle(z_axis_vec, 0) # The cross-product of z_axis_vec and object_camera_vec axis_vec = z_axis_vec.cross(object_camera_vec) # Rotate 'axis_vec' by 'angle' and convert this to euler parameters. # 4 is the size of the matrix. camera.rotation_euler = Matrix.Rotation(angle, 4, axis_vec).to_euler() # Rotate the camera around its axis by 90° such that we have a nice # camera position and view onto the object. bpy.ops.object.select_all(action='DESELECT') camera.select = True bpy.ops.transform.rotate(value=(90.0*2*pi/360.0), axis=object_camera_vec, constraint_axis=(False, False, False), constraint_orientation='GLOBAL', mirror=False, proportional='DISABLED', proportional_edit_falloff='SMOOTH', proportional_size=1, snap=False, snap_target='CLOSEST', snap_point=(0, 0, 0), snap_align=False, snap_normal=(0, 0, 0), release_confirm=False) # Here a lamp is put into the scene, if chosen. if use_lamp == True: # This is the distance from the object measured in terms of % # of the camera distance. It is set onto 50% (1/2) distance. lamp_dl = sqrt(object_size) * 15 * 0.5 # This is a factor to which extend the lamp shall go to the right # (from the camera point of view). lamp_dy_right = lamp_dl * (3.0/4.0) # Create x, y and z for the lamp. object_lamp_vec = Vector((lamp_dl,lamp_dy_right,lamp_dl)) lamp_xyz_vec = object_center_vec + object_lamp_vec # Create the lamp current_layers=bpy.context.scene.layers lamp_data = bpy.data.lamps.new(name="A_lamp", type="POINT") lamp_data.distance = 5000.0 lamp_data.energy = 3.0 lamp_data.shadow_method = 'RAY_SHADOW' lamp = bpy.data.objects.new("A_lamp", lamp_data) lamp.location = lamp_xyz_vec lamp.layers = current_layers bpy.context.scene.objects.link(lamp) bpy.context.scene.world.light_settings.use_ambient_occlusion = True bpy.context.scene.world.light_settings.ao_factor = 0.1
def bake(self, context, samples=False): ''' bake a driver to an action fcurve''' # REFACTO add flag to convert between kfs and samples def get_action_fcurve(driver): obj = driver.fcurve.id_data action = obj.animation_data.action # will have animation_data from driver if action is None: action = obj.animation_data.action = bpy.data.actions.new("%s (BFD)" % obj.name) fc = [fc for fc in action.fcurves if fc.data_path == driver.fcurve.data_path and fc.array_index == driver.fcurve.array_index] if len(fc): return fc[0] fc = action.fcurves.new(driver.data_path, driver.array_index) return fc scene = context.scene frame = self.f frame_end = self.f + self.bakeframes[self.pc] - 1 # bake to scene frame range self.driver.edit_driver_baking = True setattr(self.driver, "bake_pc", self.pc / self.chunks) driver = self.driver.fcurve obj = driver.id_data #action = speaker.animation_data.action # make an unbaked fcurve for the driver. # check whether there is already an fcurve coords = [] while frame <= frame_end: scene.frame_set(frame) co = (frame, self.driver.value) v = Vector(co) if len(coords) > 1: # enough to linear test v1 = Vector(coords[-1]) - v v2 = Vector(coords[-2]) - v if v1.length and v2.length and v1.angle(v2) < 0.001: coords[-1] = co else: coords.append(co) else: coords.append(co) # quick fix try array, then without #REFACTO frame += 1 ''' # frame by frame kfi if self.driver.is_vector: driver.id_data.keyframe_insert(driver.data_path, index=driver.array_index) else: driver.id_data.keyframe_insert(driver.data_path) frame += 1 # REFACTO ''' fc = get_action_fcurve(self.driver) l = len(coords) #x = [] # refactor got keyframe_points.foreach_set for i in range(l): fc.keyframe_points.insert(*coords[i]) if samples: fc.convert_to_samples(self.f, frame_end) return True
def import_pdb(Ball_type, Ball_azimuth, Ball_zenith, Ball_radius_factor, radiustype, Ball_distance_factor, use_sticks, use_sticks_color, use_sticks_smooth, use_sticks_bonds, Stick_unit, Stick_dist, Stick_sectors, Stick_diameter, put_to_center, use_camera, use_lamp, filepath_pdb): # List of materials atom_material_list = [] # A list of ALL objects which are loaded (needed for selecting the loaded # structure. atom_object_list = [] # ------------------------------------------------------------------------ # INITIALIZE THE ELEMENT LIST read_elements() # ------------------------------------------------------------------------ # READING DATA OF ATOMS (Number_of_total_atoms, all_atoms) = read_pdb_file(filepath_pdb, radiustype) # ------------------------------------------------------------------------ # MATERIAL PROPERTIES FOR ATOMS # The list that contains info about all types of atoms is created # here. It is used for building the material properties for # instance (see below). atom_all_types_list = [] for atom in all_atoms: FLAG_FOUND = False for atom_type in atom_all_types_list: # If the atom name is already in the list, FLAG on 'True'. if atom_type[0] == atom.name: FLAG_FOUND = True break # No name in the current list has been found? => New entry. if FLAG_FOUND == False: # Stored are: Atom label (e.g. 'Na'), the corresponding atom # name (e.g. 'Sodium') and its color. atom_all_types_list.append([atom.name, atom.element, atom.color]) # The list of materials is built. # Note that all atoms of one type (e.g. all hydrogens) get only ONE # material! This is good because then, by activating one atom in the # Blender scene and changing the color of this atom, one changes the color # of ALL atoms of the same type at the same time. # Create first a new list of materials for each type of atom # (e.g. hydrogen) for atom_type in atom_all_types_list: material = bpy.data.materials.new(atom_type[1]) material.name = atom_type[0] material.diffuse_color = atom_type[2] atom_material_list.append(material) # Now, we go through all atoms and give them a material. For all atoms ... for atom in all_atoms: # ... and all materials ... for material in atom_material_list: # ... select the correct material for the current atom via # comparison of names ... if atom.name in material.name: # ... and give the atom its material properties. # However, before we check, if it is a vacancy, because then it # gets some additional preparation. The vacancy is represented # by a transparent cube. if atom.name == "Vacancy": material.transparency_method = 'Z_TRANSPARENCY' material.alpha = 1.3 material.raytrace_transparency.fresnel = 1.6 material.raytrace_transparency.fresnel_factor = 1.6 material.use_transparency = True # The atom gets its properties. atom.material = material # ------------------------------------------------------------------------ # READING DATA OF STICKS all_sticks = read_pdb_file_sticks(filepath_pdb, use_sticks_bonds, all_atoms) # So far, all atoms, sticks and materials have been registered. # ------------------------------------------------------------------------ # TRANSLATION OF THE STRUCTURE TO THE ORIGIN # It may happen that the structure in a PDB file already has an offset # If chosen, the structure is first put into the center of the scene # (the offset is substracted). if put_to_center == True: sum_vec = Vector((0.0,0.0,0.0)) # Sum of all atom coordinates sum_vec = sum([atom.location for atom in all_atoms], sum_vec) # Then the average is taken sum_vec = sum_vec / Number_of_total_atoms # After, for each atom the center of gravity is substracted for atom in all_atoms: atom.location -= sum_vec # ------------------------------------------------------------------------ # SCALING # Take all atoms and adjust their radii and scale the distances. for atom in all_atoms: atom.location *= Ball_distance_factor # ------------------------------------------------------------------------ # DETERMINATION OF SOME GEOMETRIC PROPERTIES # In the following, some geometric properties of the whole object are # determined: center, size, etc. sum_vec = Vector((0.0,0.0,0.0)) # First the center is determined. All coordinates are summed up ... sum_vec = sum([atom.location for atom in all_atoms], sum_vec) # ... and the average is taken. This gives the center of the object. object_center_vec = sum_vec / Number_of_total_atoms # Now, we determine the size.The farthest atom from the object center is # taken as a measure. The size is used to place well the camera and light # into the scene. object_size_vec = [atom.location - object_center_vec for atom in all_atoms] object_size = 0.0 object_size = max(object_size_vec).length # ------------------------------------------------------------------------ # CAMERA AND LAMP camera_factor = 15.0 # If chosen a camera is put into the scene. if use_camera == True: # Assume that the object is put into the global origin. Then, the # camera is moved in x and z direction, not in y. The object has its # size at distance sqrt(object_size) from the origin. So, move the # camera by this distance times a factor of camera_factor in x and z. # Then add x, y and z of the origin of the object. object_camera_vec = Vector((sqrt(object_size) * camera_factor, 0.0, sqrt(object_size) * camera_factor)) camera_xyz_vec = object_center_vec + object_camera_vec # Create the camera current_layers=bpy.context.scene.layers camera_data = bpy.data.cameras.new("A_camera") camera_data.lens = 45 camera_data.clip_end = 500.0 camera = bpy.data.objects.new("A_camera", camera_data) camera.location = camera_xyz_vec camera.layers = current_layers bpy.context.scene.objects.link(camera) # Here the camera is rotated such it looks towards the center of # the object. The [0.0, 0.0, 1.0] vector along the z axis z_axis_vec = Vector((0.0, 0.0, 1.0)) # The angle between the last two vectors angle = object_camera_vec.angle(z_axis_vec, 0) # The cross-product of z_axis_vec and object_camera_vec axis_vec = z_axis_vec.cross(object_camera_vec) # Rotate 'axis_vec' by 'angle' and convert this to euler parameters. # 4 is the size of the matrix. camera.rotation_euler = Matrix.Rotation(angle, 4, axis_vec).to_euler() # Rotate the camera around its axis by 90° such that we have a nice # camera position and view onto the object. bpy.ops.object.select_all(action='DESELECT') camera.select = True bpy.ops.transform.rotate(value=(90.0*2*pi/360.0), axis=object_camera_vec, constraint_axis=(False, False, False), constraint_orientation='GLOBAL', mirror=False, proportional='DISABLED', proportional_edit_falloff='SMOOTH', proportional_size=1, snap=False, snap_target='CLOSEST', snap_point=(0, 0, 0), snap_align=False, snap_normal=(0, 0, 0), release_confirm=False) # Here a lamp is put into the scene, if chosen. if use_lamp == True: # This is the distance from the object measured in terms of % # of the camera distance. It is set onto 50% (1/2) distance. lamp_dl = sqrt(object_size) * 15 * 0.5 # This is a factor to which extend the lamp shall go to the right # (from the camera point of view). lamp_dy_right = lamp_dl * (3.0/4.0) # Create x, y and z for the lamp. object_lamp_vec = Vector((lamp_dl,lamp_dy_right,lamp_dl)) lamp_xyz_vec = object_center_vec + object_lamp_vec # Create the lamp current_layers=bpy.context.scene.layers lamp_data = bpy.data.lamps.new(name="A_lamp", type="POINT") lamp_data.distance = 500.0 lamp_data.energy = 3.0 lamp_data.shadow_method = 'RAY_SHADOW' lamp = bpy.data.objects.new("A_lamp", lamp_data) lamp.location = lamp_xyz_vec lamp.layers = current_layers bpy.context.scene.objects.link(lamp) bpy.context.scene.world.light_settings.use_ambient_occlusion = True bpy.context.scene.world.light_settings.ao_factor = 0.2 # ------------------------------------------------------------------------ # SORTING THE ATOMS # Lists of atoms of one type are created. Example: # draw_all_atoms = [ data_hydrogen,data_carbon,data_nitrogen ] # data_hydrogen = [["Hydrogen", Material_Hydrogen, Vector((x,y,z)), 109], ...] draw_all_atoms = [] # Go through the list which contains all types of atoms. It is the list, # which has been created on the top during reading the PDB file. # Example: atom_all_types_list = ["hydrogen", "carbon", ...] for atom_type in atom_all_types_list: # Don't draw 'TER atoms'. if atom_type[0] == "TER": continue # This is the draw list, which contains all atoms of one type (e.g. # all hydrogens) ... draw_all_atoms_type = [] # Go through all atoms ... for atom in all_atoms: # ... select the atoms of the considered type via comparison ... if atom.name == atom_type[0]: # ... and append them to the list 'draw_all_atoms_type'. draw_all_atoms_type.append([atom.name, atom.material, atom.location, atom.radius]) # Now append the atom list to the list of all types of atoms draw_all_atoms.append(draw_all_atoms_type) # ------------------------------------------------------------------------ # DRAWING THE ATOMS # This is the number of all atoms which are put into the scene. bpy.ops.object.select_all(action='DESELECT') # For each list of atoms of ONE type (e.g. Hydrogen) for draw_all_atoms_type in draw_all_atoms: # Create first the vertices composed of the coordinates of all # atoms of one type atom_vertices = [] for atom in draw_all_atoms_type: # In fact, the object is created in the World's origin. # This is why 'object_center_vec' is substracted. At the end # the whole object is translated back to 'object_center_vec'. atom_vertices.append( atom[2] - object_center_vec ) # Build the mesh atom_mesh = bpy.data.meshes.new("Mesh_"+atom[0]) atom_mesh.from_pydata(atom_vertices, [], []) atom_mesh.update() new_atom_mesh = bpy.data.objects.new(atom[0], atom_mesh) bpy.context.scene.objects.link(new_atom_mesh) # Now, build a representative sphere (atom) current_layers=bpy.context.scene.layers if atom[0] == "Vacancy": bpy.ops.mesh.primitive_cube_add( view_align=False, enter_editmode=False, location=(0.0, 0.0, 0.0), rotation=(0.0, 0.0, 0.0), layers=current_layers) else: # NURBS balls if Ball_type == "0": bpy.ops.surface.primitive_nurbs_surface_sphere_add( view_align=False, enter_editmode=False, location=(0,0,0), rotation=(0.0, 0.0, 0.0), layers=current_layers) # UV balls elif Ball_type == "1": bpy.ops.mesh.primitive_uv_sphere_add( segments=Ball_azimuth, ring_count=Ball_zenith, size=1, view_align=False, enter_editmode=False, location=(0,0,0), rotation=(0, 0, 0), layers=current_layers) # Meta balls elif Ball_type == "2": bpy.ops.object.metaball_add(type='BALL', view_align=False, enter_editmode=False, location=(0, 0, 0), rotation=(0, 0, 0), layers=current_layers) ball = bpy.context.scene.objects.active ball.scale = (atom[3]*Ball_radius_factor,) * 3 if atom[0] == "Vacancy": ball.name = "Cube_"+atom[0] else: ball.name = "Ball_"+atom[0] ball.active_material = atom[1] ball.parent = new_atom_mesh new_atom_mesh.dupli_type = 'VERTS' # The object is back translated to 'object_center_vec'. new_atom_mesh.location = object_center_vec atom_object_list.append(new_atom_mesh) # ------------------------------------------------------------------------ # DRAWING THE STICKS if use_sticks == True and all_sticks != []: dl = Stick_unit if use_sticks_color == False: bpy.ops.object.material_slot_add() stick_material = bpy.data.materials.new(ELEMENTS[-1].name) stick_material.diffuse_color = ELEMENTS[-1].color # Sort the sticks and put them into a new list such that ... sticks_all_lists = [] if use_sticks_color == True: for atom_type in atom_all_types_list: if atom_type[0] == "TER": continue sticks_list = [] for stick in all_sticks: for repeat in range(stick.number): atom1 = copy(all_atoms[stick.atom1-1].location) atom2 = copy(all_atoms[stick.atom2-1].location) dist = Stick_diameter * Stick_dist if stick.number == 2: if repeat == 0: atom1 += (stick.dist * dist) atom2 += (stick.dist * dist) if repeat == 1: atom1 -= (stick.dist * dist) atom2 -= (stick.dist * dist) if stick.number == 3: if repeat == 0: atom1 += (stick.dist * dist) atom2 += (stick.dist * dist) if repeat == 2: atom1 -= (stick.dist * dist) atom2 -= (stick.dist * dist) dv = atom1 - atom2 n = dv / dv.length if atom_type[0] == all_atoms[stick.atom1-1].name: location = atom1 name = "_" + all_atoms[stick.atom1-1].name material = all_atoms[stick.atom1-1].material sticks_list.append([name, location, dv, material]) if atom_type[0] == all_atoms[stick.atom2-1].name: location = atom1 - n * dl * int(ceil(dv.length / (2.0 * dl))) name = "_" + all_atoms[stick.atom2-1].name material = all_atoms[stick.atom2-1].material sticks_list.append([name, location, dv, material]) if sticks_list != []: sticks_all_lists.append(sticks_list) else: sticks_list = [] for stick in all_sticks: if stick.number > 3: stick.number = 1 for repeat in range(stick.number): atom1 = copy(all_atoms[stick.atom1-1].location) atom2 = copy(all_atoms[stick.atom2-1].location) dist = Stick_diameter * Stick_dist if stick.number == 2: if repeat == 0: atom1 += (stick.dist * dist) atom2 += (stick.dist * dist) if repeat == 1: atom1 -= (stick.dist * dist) atom2 -= (stick.dist * dist) if stick.number == 3: if repeat == 0: atom1 += (stick.dist * dist) atom2 += (stick.dist * dist) if repeat == 2: atom1 -= (stick.dist * dist) atom2 -= (stick.dist * dist) dv = atom1 - atom2 n = dv / dv.length location = atom1 material = stick_material sticks_list.append(["", location, dv, material]) sticks_all_lists.append(sticks_list) # ... the sticks in the list can be drawn: for stick_list in sticks_all_lists: vertices = [] faces = [] i = 0 # What follows is school mathematics! :-) for stick in stick_list: dv = stick[2] v1 = stick[1] n = dv / dv.length gamma = -n * v1 b = v1 + gamma * n n_b = b / b.length if use_sticks_color == True: loops = int(ceil(dv.length / (2.0 * dl))) else: loops = int(ceil(dv.length / dl)) for j in range(loops): g = v1 - n * dl / 2.0 - n * dl * j p1 = g + n_b * Stick_diameter p2 = g - n_b * Stick_diameter p3 = g - n_b.cross(n) * Stick_diameter p4 = g + n_b.cross(n) * Stick_diameter vertices.append(p1) vertices.append(p2) vertices.append(p3) vertices.append(p4) faces.append((i*4+0,i*4+2,i*4+1,i*4+3)) i += 1 mesh = bpy.data.meshes.new("Sticks"+stick[0]) mesh.from_pydata(vertices, [], faces) mesh.update() new_mesh = bpy.data.objects.new("Sticks"+stick[0], mesh) bpy.context.scene.objects.link(new_mesh) current_layers = bpy.context.scene.layers object_stick = build_stick(Stick_diameter, dl, Stick_sectors) stick_cylinder = object_stick[0] stick_cylinder.active_material = stick[3] stick_cups = object_stick[1] stick_cups.active_material = stick[3] if use_sticks_smooth == True: bpy.ops.object.select_all(action='DESELECT') stick_cylinder.select = True stick_cups.select = True bpy.ops.object.shade_smooth() stick_cylinder.parent = new_mesh stick_cups.parent = new_mesh new_mesh.dupli_type = 'FACES' atom_object_list.append(new_mesh) # ------------------------------------------------------------------------ # SELECT ALL LOADED OBJECTS bpy.ops.object.select_all(action='DESELECT') obj = None for obj in atom_object_list: obj.select = True # activate the last selected object (perhaps another should be active?) if obj: bpy.context.scene.objects.active = obj
def import_xyz(Ball_type, Ball_azimuth, Ball_zenith, Ball_radius_factor, radiustype, Ball_distance_factor, put_to_center, put_to_center_all, use_camera, use_lamp, filepath_xyz): # List of materials atom_material_list = [] # ------------------------------------------------------------------------ # INITIALIZE THE ELEMENT LIST read_elements() # ------------------------------------------------------------------------ # READING DATA OF ATOMS Number_of_total_atoms = read_xyz_file(filepath_xyz, radiustype) # We show the atoms of the first frame. first_frame = ALL_FRAMES[0] # ------------------------------------------------------------------------ # MATERIAL PROPERTIES FOR ATOMS # Create first a new list of materials for each type of atom # (e.g. hydrogen) for atoms_of_one_type in first_frame: # Take the first atom atom = atoms_of_one_type[0] material = bpy.data.materials.new(atom.name) material.name = atom.name material.diffuse_color = atom.color atom_material_list.append(material) # Now, we go through all atoms and give them a material. For all atoms ... for atoms_of_one_type in first_frame: for atom in atoms_of_one_type: # ... and all materials ... for material in atom_material_list: # ... select the correct material for the current atom via # comparison of names ... if atom.name in material.name: # ... and give the atom its material properties. # However, before we check if it is a vacancy # The vacancy is represented by a transparent cube. if atom.name == "Vacancy": material.transparency_method = 'Z_TRANSPARENCY' material.alpha = 1.3 material.raytrace_transparency.fresnel = 1.6 material.raytrace_transparency.fresnel_factor = 1.6 material.use_transparency = True # The atom gets its properties. atom.material = material # ------------------------------------------------------------------------ # TRANSLATION OF THE STRUCTURE TO THE ORIGIN # It may happen that the structure in a XYZ file already has an offset # If chosen, the structure is put into the center of the scene # (only the first frame). if put_to_center == True and put_to_center_all == False: sum_vec = Vector((0.0,0.0,0.0)) # Sum of all atom coordinates for atoms_of_one_type in first_frame: sum_vec = sum([atom.location for atom in atoms_of_one_type], sum_vec) # Then the average is taken sum_vec = sum_vec / Number_of_total_atoms # After, for each atom the center of gravity is substracted for atoms_of_one_type in first_frame: for atom in atoms_of_one_type: atom.location -= sum_vec # If chosen, the structure is put into the center of the scene # (all frames). if put_to_center_all == True: # For all frames for frame in ALL_FRAMES: sum_vec = Vector((0.0,0.0,0.0)) # Sum of all atom coordinates for (i, atoms_of_one_type) in enumerate(frame): # This is a guarantee that only the total number of atoms of the # first frame is used. Condition is, so far, that the number of # atoms in a xyz file is constant. However, sometimes the number # may increase (or decrease). If it decreases, the addon crashes. # If it increases, only the tot number of atoms of the first frame # is used. # By time, I will allow varying atom numbers ... but this takes # some time ... if i >= Number_of_total_atoms: break sum_vec = sum([atom.location for atom in atoms_of_one_type], sum_vec) # Then the average is taken sum_vec = sum_vec / Number_of_total_atoms # After, for each atom the center of gravity is substracted for atoms_of_one_type in frame: for atom in atoms_of_one_type: atom.location -= sum_vec # ------------------------------------------------------------------------ # SCALING # Take all atoms and adjust their radii and scale the distances. for atoms_of_one_type in first_frame: for atom in atoms_of_one_type: atom.location *= Ball_distance_factor # ------------------------------------------------------------------------ # DETERMINATION OF SOME GEOMETRIC PROPERTIES # In the following, some geometric properties of the whole object are # determined: center, size, etc. sum_vec = Vector((0.0,0.0,0.0)) # First the center is determined. All coordinates are summed up ... for atoms_of_one_type in first_frame: sum_vec = sum([atom.location for atom in atoms_of_one_type], sum_vec) # ... and the average is taken. This gives the center of the object. object_center_vec = sum_vec / Number_of_total_atoms # Now, we determine the size.The farthest atom from the object center is # taken as a measure. The size is used to place well the camera and light # into the scene. object_size_vec = [] for atoms_of_one_type in first_frame: object_size_vec += [atom.location - object_center_vec for atom in atoms_of_one_type] object_size = 0.0 object_size = max(object_size_vec).length # ------------------------------------------------------------------------ # CAMERA AND LAMP camera_factor = 20.0 # If chosen a camera is put into the scene. if use_camera == True: # Assume that the object is put into the global origin. Then, the # camera is moved in x and z direction, not in y. The object has its # size at distance sqrt(object_size) from the origin. So, move the # camera by this distance times a factor of camera_factor in x and z. # Then add x, y and z of the origin of the object. object_camera_vec = Vector((sqrt(object_size) * camera_factor, 0.0, sqrt(object_size) * camera_factor)) camera_xyz_vec = object_center_vec + object_camera_vec # Create the camera current_layers=bpy.context.scene.layers camera_data = bpy.data.cameras.new("A_camera") camera_data.lens = 45 camera_data.clip_end = 500.0 camera = bpy.data.objects.new("A_camera", camera_data) camera.location = camera_xyz_vec camera.layers = current_layers bpy.context.scene.objects.link(camera) # Here the camera is rotated such it looks towards the center of # the object. The [0.0, 0.0, 1.0] vector along the z axis z_axis_vec = Vector((0.0, 0.0, 1.0)) # The angle between the last two vectors angle = object_camera_vec.angle(z_axis_vec, 0) # The cross-product of z_axis_vec and object_camera_vec axis_vec = z_axis_vec.cross(object_camera_vec) # Rotate 'axis_vec' by 'angle' and convert this to euler parameters. # 4 is the size of the matrix. camera.rotation_euler = Matrix.Rotation(angle, 4, axis_vec).to_euler() # Rotate the camera around its axis by 90° such that we have a nice # camera position and view onto the object. bpy.ops.object.select_all(action='DESELECT') camera.select = True bpy.ops.transform.rotate(value=(90.0*2*pi/360.0), axis=object_camera_vec, constraint_axis=(False, False, False), constraint_orientation='GLOBAL', mirror=False, proportional='DISABLED', proportional_edit_falloff='SMOOTH', proportional_size=1, snap=False, snap_target='CLOSEST', snap_point=(0, 0, 0), snap_align=False, snap_normal=(0, 0, 0), release_confirm=False) # Here a lamp is put into the scene, if chosen. if use_lamp == True: # This is the distance from the object measured in terms of % # of the camera distance. It is set onto 50% (1/2) distance. lamp_dl = sqrt(object_size) * 15 * 0.5 # This is a factor to which extend the lamp shall go to the right # (from the camera point of view). lamp_dy_right = lamp_dl * (3.0/4.0) # Create x, y and z for the lamp. object_lamp_vec = Vector((lamp_dl,lamp_dy_right,lamp_dl)) lamp_xyz_vec = object_center_vec + object_lamp_vec # Create the lamp current_layers=bpy.context.scene.layers lamp_data = bpy.data.lamps.new(name="A_lamp", type="POINT") lamp_data.distance = 500.0 lamp_data.energy = 3.0 lamp_data.shadow_method = 'RAY_SHADOW' lamp = bpy.data.objects.new("A_lamp", lamp_data) lamp.location = lamp_xyz_vec lamp.layers = current_layers bpy.context.scene.objects.link(lamp) bpy.context.scene.world.light_settings.use_ambient_occlusion = True bpy.context.scene.world.light_settings.ao_factor = 0.2 # ------------------------------------------------------------------------ # DRAWING THE ATOMS bpy.ops.object.select_all(action='DESELECT') # For each list of atoms of ONE type (e.g. Hydrogen) for atoms_of_one_type in first_frame: # Create first the vertices composed of the coordinates of all # atoms of one type atom_vertices = [] for atom in atoms_of_one_type: # In fact, the object is created in the World's origin. # This is why 'object_center_vec' is substracted. At the end # the whole object is translated back to 'object_center_vec'. atom_vertices.append( atom.location - object_center_vec ) # Build the mesh atom_mesh = bpy.data.meshes.new("Mesh_"+atom.name) atom_mesh.from_pydata(atom_vertices, [], []) atom_mesh.update() new_atom_mesh = bpy.data.objects.new(atom.name, atom_mesh) bpy.context.scene.objects.link(new_atom_mesh) # Now, build a representative sphere (atom) current_layers=bpy.context.scene.layers if atom.name == "Vacancy": bpy.ops.mesh.primitive_cube_add( view_align=False, enter_editmode=False, location=(0.0, 0.0, 0.0), rotation=(0.0, 0.0, 0.0), layers=current_layers) else: # NURBS balls if Ball_type == "0": bpy.ops.surface.primitive_nurbs_surface_sphere_add( view_align=False, enter_editmode=False, location=(0,0,0), rotation=(0.0, 0.0, 0.0), layers=current_layers) # UV balls elif Ball_type == "1": bpy.ops.mesh.primitive_uv_sphere_add( segments=Ball_azimuth, ring_count=Ball_zenith, size=1, view_align=False, enter_editmode=False, location=(0,0,0), rotation=(0, 0, 0), layers=current_layers) # Meta balls elif Ball_type == "2": bpy.ops.object.metaball_add(type='BALL', view_align=False, enter_editmode=False, location=(0, 0, 0), rotation=(0, 0, 0), layers=current_layers) ball = bpy.context.scene.objects.active ball.scale = (atom.radius*Ball_radius_factor,) * 3 if atom.name == "Vacancy": ball.name = "Cube_"+atom.name else: ball.name = "Ball (NURBS)_"+atom.name ball.active_material = atom.material ball.parent = new_atom_mesh new_atom_mesh.dupli_type = 'VERTS' # The object is back translated to 'object_center_vec'. new_atom_mesh.location = object_center_vec STRUCTURE.append(new_atom_mesh) # ------------------------------------------------------------------------ # SELECT ALL LOADED OBJECTS bpy.ops.object.select_all(action='DESELECT') obj = None for obj in STRUCTURE: obj.select = True # activate the last selected object (perhaps another should be active?) if obj: bpy.context.scene.objects.active = obj
def camera_light_source(use_camera, use_light, object_center_vec, object_size): camera_factor = 15.0 # If chosen a camera is put into the scene. if use_camera == True: # Assume that the object is put into the global origin. Then, the # camera is moved in x and z direction, not in y. The object has its # size at distance sqrt(object_size) from the origin. So, move the # camera by this distance times a factor of camera_factor in x and z. # Then add x, y and z of the origin of the object. object_camera_vec = Vector((sqrt(object_size) * camera_factor, 0.0, sqrt(object_size) * camera_factor)) camera_xyz_vec = object_center_vec + object_camera_vec # Create the camera camera_data = bpy.data.cameras.new("A_camera") camera_data.lens = 45 camera_data.clip_end = 500.0 camera = bpy.data.objects.new("A_camera", camera_data) camera.location = camera_xyz_vec bpy.context.collection.objects.link(camera) # Here the camera is rotated such it looks towards the center of # the object. The [0.0, 0.0, 1.0] vector along the z axis z_axis_vec = Vector((0.0, 0.0, 1.0)) # The angle between the last two vectors angle = object_camera_vec.angle(z_axis_vec, 0) # The cross-product of z_axis_vec and object_camera_vec axis_vec = z_axis_vec.cross(object_camera_vec) # Rotate 'axis_vec' by 'angle' and convert this to euler parameters. # 4 is the size of the matrix. camera.rotation_euler = Matrix.Rotation(angle, 4, axis_vec).to_euler() # Rotate the camera around its axis by 90° such that we have a nice # camera position and view onto the object. bpy.ops.object.select_all(action='DESELECT') camera.select_set(True) # Rotate the camera around its axis 'object_camera_vec' by 90° such # that we have a nice camera view onto the object. matrix_rotation = Matrix.Rotation(90/360*2*pi, 4, object_camera_vec) rotate_object(matrix_rotation, camera) # Here a lamp is put into the scene, if chosen. if use_light == True: # This is the distance from the object measured in terms of % # of the camera distance. It is set onto 50% (1/2) distance. light_dl = sqrt(object_size) * 15 * 0.5 # This is a factor to which extend the lamp shall go to the right # (from the camera point of view). light_dy_right = light_dl * (3.0/4.0) # Create x, y and z for the lamp. object_light_vec = Vector((light_dl,light_dy_right,light_dl)) light_xyz_vec = object_center_vec + object_light_vec # Create the lamp light_data = bpy.data.lights.new(name="A_light", type="SUN") light_data.distance = 500.0 light_data.energy = 3.0 lamp = bpy.data.objects.new("A_light", light_data) lamp.location = light_xyz_vec bpy.context.collection.objects.link(lamp) # Some settings for the World: a bit ambient occlusion bpy.context.scene.world.light_settings.use_ambient_occlusion = True bpy.context.scene.world.light_settings.ao_factor = 0.2 # Some properties for cycles lamp.data.use_nodes = True lmp_P_BSDF = lamp.data.node_tree.nodes['Emission'] lmp_P_BSDF.inputs['Strength'].default_value = 5
def execute(self, context): threshold = 1e-6 region = context.region rv3d = context.region_data actob = context.active_object mat = actob.matrix_world imat = actob.matrix_world.inverted() bm = bmesh.from_edit_mesh(actob.data) edgelines = self.edgelines # 描画用 translines = self.translines # 描画用 edgelines[:] = [] # Clear translines[:] = [] # Clear # mm_relative = self.modal_mouse.relative.to_3d() # if mm_relative.length < threshold: # mm_relative = Vector((1, 0)) if self.modal_mouse.lock: lock_relative = self.modal_mouse.lock - self.modal_mouse.origin else: lock_relative = self.modal_mouse.relative if lock_relative.length < threshold: lock_relative = Vector((1, 0)) # 座標初期化 for src, dst in zip(self.bm.verts, bm.verts): dst.co = src.co new_coordinates = {} # index: Vector for i, eve in enumerate(bm.verts): if not eve.select or eve.hide: continue # 頂点に接続する頂点を求める connected_vertices = [] for eed in (e for e in eve.link_edges if not e.hide): connected_vertices.append(eed.other_vert(eve)) if not connected_vertices: continue # 辺の長さが0なら、更にその先にある頂点を追加する verts = [] for eve2 in connected_vertices: if (eve2.co - eve.co).length < threshold: for eed in (e for e in eve2.link_edges if not e.hide): verts.append(eed.other_vert(eve2)) connected_vertices.extend(verts) # 移動先の頂点を求める angles = [] v1 = eve.co v1W = mat * v1 v1R = vav.project(region, rv3d, v1W).to_2d() for eve2 in connected_vertices: v2W = mat * eve2.co v2R = vav.project(region, rv3d, v2W).to_2d() v = v2R - v1R if v.length >= threshold: angles.append(lock_relative.angle(v)) else: angles.append(math.pi * 2) # 優先度最低 eve_target = connected_vertices[angles.index(min(angles))] v2 = eve_target.co v2W = mat * v2 if self.use_local_coords: transvec = v2 - v1 else: transvec = v2W - v1W if transvec.length < threshold: continue if self.modal_mouse.lock: value = self.value else: if self.mode == 'DISTANCE': value = min(max(0, self.value), transvec.length) else: value = min(max(0.0, self.value), 1.0) if self.mode == 'DISTANCE': transvec.normalize() if self.use_local_coords: if self.flip: newco = v2 - transvec * value else: newco = v1 + transvec * value else: if self.flip: newco = v2W - transvec * value else: newco = v1W + transvec * value # edgelines for eve2 in connected_vertices: vW = mat * eve2.co if eve2 == eve_target: translines.append((v1W, vW)) else: edgelines.append((v1W, vW)) if self.use_local_coords: new_coordinates[i] = newco else: new_coordinates[i] = imat * newco # apply for i, vec in new_coordinates.items(): bm.verts[i].co = vec bm.normal_update() bmesh.update_edit_mesh(actob.data, True, True) return {'FINISHED'}
def create_prim(context, event, hit_world, normal, segments, is_autoaxis, prim_type, self): rv3d = context.region_data if prim_type == 'Plane': bpy.ops.mesh.primitive_plane_add(radius=1) elif prim_type == 'Cube': bpy.ops.mesh.primitive_cube_add(radius=1) elif prim_type == 'Circle': bpy.ops.mesh.primitive_circle_add(radius=1, vertices=segments[0], fill_type='NGON') elif prim_type == 'Sphere': bpy.ops.mesh.primitive_uv_sphere_add(size=1, segments=segments[0], ring_count=segments[1]) elif prim_type == 'Cylinder': bpy.ops.mesh.primitive_cylinder_add(radius=1, vertices=segments[0]) elif prim_type == 'Cone': bpy.ops.mesh.primitive_cone_add(radius1=1, vertices=segments[0]) elif prim_type == 'Capsule': add_mesh_capsule.add_capsule(1, 1, segments[1], segments[0], context) elif prim_type == 'Clone': orig_obj = self.objects_to_clone[-1] bpy.ops.object.select_all(action='DESELECT') orig_obj.select = True bpy.ops.object.duplicate(linked=True, mode='DUMMY') new_clone = context.selected_objects[0] context.scene.objects.active = new_clone new_prim = bpy.context.object # get rotation vectors z_neg_vec = Vector((0.0, 0.0, -1.0)) z_world_angle = z_neg_vec.angle(normal) if z_world_angle >= math.radians(180) or z_world_angle <= math.radians(0): view_cam_upvec = (rv3d.view_rotation * Vector((0.0, -1.0, 0.0))).normalized() view_upvec = view_cam_upvec.copy() view_upvec[2] = 0.0 #if view_upvec[0] == 0 and view_upvec[1] == 0: #view_upvec = view_cam_upvec if view_upvec[0] > view_upvec[1]: view_upvec[0] = 1.0 view_upvec[1] = 0.0 else: view_upvec[0] = 0.0 view_upvec[1] = 1.0 view_upvec = view_upvec.normalized() else: view_upvec = z_neg_vec prim_side_vec = None # side vector if is_autoaxis is True: if normal[0] == 1: prim_side_vec = Vector((0, 1, 0)) elif normal[0] == -1: prim_side_vec = Vector((0, -1, 0)) elif normal[1] == 1: prim_side_vec = Vector((-1, 0, 0)) elif normal[1] == -1: prim_side_vec = Vector((1, 0, 0)) elif normal[2] == 1: prim_side_vec = Vector((1, 0, 0)) elif normal[2] == -1: prim_side_vec = Vector((1, 0, 0)) else: prim_side_vec = normal.cross(view_upvec).normalized() prim_front_vec = normal.cross(prim_side_vec).normalized() # ROTATE CUBE final_mat = mathu.Matrix().to_3x3() final_mat[0][0], final_mat[1][0], final_mat[2][0] = prim_side_vec[0], prim_side_vec[1], prim_side_vec[2] final_mat[0][1], final_mat[1][1], final_mat[2][1] = prim_front_vec[0], prim_front_vec[1], prim_front_vec[2] final_mat[0][2], final_mat[1][2], final_mat[2][2] = normal[0], normal[1], normal[2] #final_mat = final_mat.normalized() #new_prim.matrix_world = final_mat.to_4x4() new_prim.rotation_euler = final_mat.to_euler() new_prim.location = hit_world.copy() return new_prim, prim_side_vec, prim_front_vec
def angle_between(self, vec1, vec2): vec1 = Vector(vec1) vec2 = Vector(vec2) return vec1.angle(vec2)
def pose_layer(self, armature, bone_name, plane, tex, key): ox,oy,width,height = tex['rect'] ## bpy.ops.object.select_all(action='DESELECT') bpy.context.scene.objects.active = bpy.data.objects[armature.name] #bpy.ops.object.select_pattern(pattern=str(armature.name), case_sensitive=False, extend=True) bpy.ops.object.mode_set(mode='POSE') #set index bone = bpy.context.object.pose.bones[bone_name] index = key['index'] if 'loc' in key: loc = key['loc'] ## loc = self.transformPoint(loc[0], loc[1], width, height) bone.location.x = loc[0] bone.location.y = -loc[1] bone.keyframe_insert(data_path="location", frame=index) self.report({'INFO'}, " bone loc: {0} index: {1}".format(bone_name, index)) scale = None ## http://www.senocular.com/flash/tutorials/transformmatrix/ if 'skew' in key : skew = key['skew'] scale = (1,1) if 'scale' in key: scale = key['scale'] #origin #ox, oy = self.transformPoint(ox, oy, width , height) #work out longest vector v = Vector((ox - width, 0.0,0.0)) if height > width: v = Vector((0.0, oy - height, 0.0)) m = mathutils.Matrix.Identity(3) m[0][0] = scale[0] m[1][1] = scale[1] m[0][1] = skew[0] m[1][0] = skew[1] r = m * v avg = v.angle(r) c = v.cross(r) ## #y transform ## y = Vector((1.0, 0.0, 0.0)) ## v = m * y ## angle = v.angle(y) ## c = v.cross(y) ## ## #x transform ## x = Vector((0.0, 1.0, 0.0)) ## v = m * x ## angle2 = v.angle(x) ## self.report({'INFO'}, " angle: {0} angle2: {1}".format(angle, angle2)) ## avg = (angle + angle2)/2.0 if c[2] < 0: avg *= -1 bone.rotation_euler.z = avg bone.keyframe_insert(data_path="rotation_euler", frame=index) if 'scale' in key: if scale is None: scale = key['scale'] bone.scale.x = scale[0] bone.scale.y = scale[1] bone.keyframe_insert(data_path="scale", frame=index) relative_parent = True if 'pivot' in key: pivot = key['pivot'] #pivot is actually the same as origin (just updated in case of switching symbols per layer) #invertY ox, oy = self.transformPoint(pivot[0], pivot[1], width , height) #tail position in image coordinates tx = width / 2.0 ty = height / 2.0 if not relative_parent: #set origin plane.location.x = - ox + tx plane.location.y = - oy else: bpy.data.armatures[armature.name].bones[bone_name].use_relative_parent = True bpy.data.armatures[armature.name].bones[bone_name].use_local_location = False plane.location.x = (-width /2 )+ox plane.location.y = (+height/2) - oy plane.keyframe_insert(data_path="location", frame=index)
def execute(self, context): curvepoints = [] curveobj = context.active_object bpy.ops.object.empty_add(type='PLAIN_AXES') parentobj = context.active_object parentobj.name = 'CurveForce' if len(curveobj.data.splines[0].bezier_points) > 1: curvepoints = [v.co for v in curveobj.data.splines[0].bezier_points] else: curvepoints = [v.co for v in curveobj.data.splines[0].points] curveobj.parent = parentobj context.active_object.select = False curveobj.select = True previousnormal = Vector([0.0,0.0,0.0]) lastStrength = context.window_manager.curveForce_strength lastDistance = context.window_manager.curveForce_maxDist for i in range(len(curvepoints)): cpoint = Vector([curvepoints[i][0],curvepoints[i][1],curvepoints[i][2]]) bpy.ops.object.empty_add(type='SINGLE_ARROW',location=(cpoint)) context.active_object.name = 'ForceObj' # turn into forcefield bpy.ops.object.forcefield_toggle() context.active_object.field.type = 'WIND' if context.window_manager.curveForce_trailout: if i > 0: lastStrength = lastStrength * 0.9 lastDistance = lastDistance * 0.9 context.active_object.field.strength = lastStrength context.active_object.field.use_max_distance = True context.active_object.field.distance_max = lastDistance context.active_object.field.falloff_power = context.window_manager.curveForce_falloffPower # get the curve's direction between points tempnorm = Vector([0,0,0]) if (i < len(curvepoints) - 1): cpoint2 = Vector([curvepoints[i + 1][0],curvepoints[i + 1][1],curvepoints[i + 1][2]]) tempnorm = cpoint - cpoint2 if i > 0: if abs(previousnormal.length) > 0.0: tempnorm = (tempnorm + previousnormal) / 2.0 previousnormal = tempnorm else: if curveobj.data.splines[0].use_cyclic_u or curveobj.data.splines[0].use_cyclic_u: cpoint2 = Vector([curvepoints[0][0],curvepoints[0][1],curvepoints[0][2]]) tempnorm = cpoint - cpoint2 if abs(previousnormal.length) > 0.0: tempnorm = (tempnorm + previousnormal) / 2.0 previousnormal = tempnorm else: cpoint2 = Vector([curvepoints[i - 1][0],curvepoints[i - 1][1],curvepoints[i - 1][2]]) tempnorm = cpoint2 - cpoint if abs(previousnormal.length) > 0.0: tempnorm = (tempnorm + previousnormal) / 2.0 previousnormal = tempnorm if abs(tempnorm.length) > 0.0: z = Vector((0,0,1)) angle = tempnorm.angle(z) axis = z.cross(tempnorm) mat = Matrix.Rotation(angle, 4, axis) mat_world = context.active_object.matrix_world * mat context.active_object.matrix_world = mat_world context.active_object.parent = parentobj return {'FINISHED'}
def modal(self, context, event): mx = event.mouse_x my = event.mouse_y off = False if event.type == "F": if event.shift and event.ctrl and not (event.alt) and event.value == "PRESS": off = True if off or event.type == "ESC" or not (self.rv3d.is_perspective): context.window_manager.event_timer_remove(self.movetimer) self.cursor_restore(context) self.regionui.tag_redraw() self.region.tag_redraw() del bpy.types.Scene.PreSelOff self.area.header_text_set() if self.addonprefs.UseLens: context.space_data.lens = self.oldlens bpy.types.SpaceView3D.draw_handler_remove(self._handle, "WINDOW") return {"FINISHED"} if event.type in {"LEFT_SHIFT", "RIGHTSHIFT"}: if event.value == "PRESS": self.runmulti = 1.5 else: self.runmulti = 1 if event.type == "WHEELUPMOUSE": self.addonprefs.Speed *= 1.4 if event.type == "WHEELDOWNMOUSE": self.addonprefs.Speed *= 0.8 if event.type == self.addonprefs.mouselook: if event.value == "PRESS" and self.addonprefs.ActPass: self.acton = True else: self.acton = False if self.addonprefs.ActPass == False: self.acton = True if event.type in {"MOUSEMOVE", "LEFTMOUSE", "WHEELUPMOUSE", "WHEELDOWNMOUSE"}: if event.type == "MOUSEMOVE": if mx == self.xcenter and my == self.ycenter: return {"RUNNING_MODAL"} self.cursor_reset(context) if self.acton and event.type == "MOUSEMOVE" and self.rv3d: if self.addonprefs.YMirror: ymult = -1 else: ymult = 1 smult = (self.addonprefs.MSens / 10) + 0.1 dx = mx - self.xcenter dy = my - self.ycenter cmat = self.rv3d.view_matrix.inverted() dxmat = Matrix.Rotation(math.radians(-dx * smult / 5), 3, "Z") cmat3 = cmat.copy().to_3x3() cmat3.rotate(dxmat) cmat4 = cmat3.to_4x4() cmat4.translation = cmat.translation self.rv3d.view_matrix = cmat4.inverted() self.rv3d.update() cmat = self.rv3d.view_matrix.inverted() dymat = Matrix.Rotation(math.radians(dy * ymult * smult / 5), 3, self.rv3d.view_matrix[0][:3]) cmat3 = cmat.copy().to_3x3() cmat3.rotate(dymat) cmat4 = cmat3.to_4x4() cmat4.translation = cmat.translation tempmat = cmat4.inverted() upvec = Vector(tempmat[1][:3]) downvec = -Vector(tempmat[1][:3]) if upvec.angle(Vector((0, 0, 1))) < math.radians(90): if downvec.angle(Vector((0, 0, -1))) < math.radians(90): self.rv3d.view_matrix = tempmat self.rv3d.update() if mx > self.regionui.x or my < self.regionui.y: return {"PASS_THROUGH"} else: return {"RUNNING_MODAL"} if event.type in {self.addonprefs.left1, self.addonprefs.left2, self.addonprefs.left3}: if event.value == "PRESS": self.leftnav = True else: self.leftnav = False elif event.type in {self.addonprefs.right1, self.addonprefs.right2, self.addonprefs.right3}: if event.value == "PRESS": self.rightnav = True else: self.rightnav = False elif event.type in {self.addonprefs.forward1, self.addonprefs.forward2, self.addonprefs.forward3}: if event.value == "PRESS": self.forwardnav = True else: self.forwardnav = False elif event.type in {self.addonprefs.back1, self.addonprefs.back2, self.addonprefs.back3}: if event.value == "PRESS": self.backnav = True else: self.backnav = False elif event.type in {self.addonprefs.up1, self.addonprefs.up2, self.addonprefs.up3}: if event.value == "PRESS": self.upnav = True else: self.upnav = False elif event.type in {self.addonprefs.down1, self.addonprefs.down2, self.addonprefs.down3}: if event.value == "PRESS": self.downnav = True else: self.downnav = False if event.type == "TIMER": moved = False if self.leftnav: moved = True self.moveleft() if self.rightnav: moved = True self.moveright() if self.forwardnav: moved = True self.moveforward() if self.backnav: moved = True self.moveback() if self.upnav: moved = True self.moveup() if self.downnav: moved = True self.movedown() if moved: self.rv3d.update() if self.scn.FPS_Walk: self.movetoground() if event.type == self.addonprefs.walkmode: if event.value == "PRESS": self.scn.FPS_Walk = not (self.scn.FPS_Walk) if self.scn.FPS_Walk: self.hchange = True self.regionui.tag_redraw() if event.type == self.addonprefs.teleport: if event.value == "PRESS": eyevec = -Vector(self.rv3d.view_matrix[2][:3]) eye = Vector(self.rv3d.view_matrix.inverted().col[3][:3]) start = eye eyevec.length = 10000 end = start + eyevec hit = self.scn.ray_cast(start, end) if hit[0]: delta = hit[3] - eye length = delta.length - self.addonprefs.TDistance * self.addonprefs.Scale if length > 0: delta.length = length else: return {"RUNNING_MODAL"} self.rv3d.view_location += delta self.rv3d.update() if self.scn.FPS_Walk: self.movetoground() return {"RUNNING_MODAL"}