def setup_render_freestyle(**options): """ Setup the render setting :param n_frames: Animation length of a single oscillation cycle in frames :type n_frames: Positive int :param start_frame: The starting frame number of the rendered animation (default=0) :type start_frame: int or None :param end_frame: The ending frame number of the rendered Animation (default=start_frame+n_frames-1) :type end_frame: int or None :param preview: Write to a temporary preview file at low resolution instead of the output. Use first frame only. If no preview, set to empty string '' :type preview: str :param config: Path to user configuration settings -- this function makes use of 'x_pixels', 'y_pixels', 'box_thickness' and 'outline_thickness' keys in [general] section and 'outline' and 'box' keys in [colours] section :type config: str """ opts = vsim2blender.Opts(options) start_frame = opts.get('start_frame', 0) n_frames = opts.get('n_frames', 30) if opts.get('static', False): end_frame = start_frame else: end_frame = opts.get('end_frame', start_frame + n_frames - 1) x_pixels = opts.get('x_pixels', 512) y_pixels = opts.get('y_pixels', 512) bpy.context.scene.render.resolution_x = x_pixels bpy.context.scene.render.resolution_y = y_pixels if opts.get('preview', False): bpy.context.scene.render.resolution_percentage = 40 else: bpy.context.scene.render.resolution_percentage = 100 # These flat renders don't use much memory, so render a single big # tiles for high speed bpy.context.scene.render.tile_x = x_pixels bpy.context.scene.render.tile_y = y_pixels bpy.context.scene.frame_start = start_frame bpy.context.scene.frame_end = end_frame bpy.context.scene.render.use_freestyle = True renderlayer = bpy.context.scene.render.layers['RenderLayer'] if opts.get('show_box', True): # Wireframe box and add to "Group" for exclusion from outlining # Freestyle doesn't work with wireframes bpy.data.materials['Bounding Box'].type = 'SURFACE' mesh_to_wireframe(bpy.data.objects['Bounding Box']) mark_edges(bpy.data.objects['Bounding Box']) # Bounding box line settings bpy.ops.scene.freestyle_lineset_add() boxlines = renderlayer.freestyle_settings.linesets.active boxlinestyle = boxlines.linestyle boxlinestyle.thickness = opts.get('box_thickness', 5) boxlinestyle.color = str2list( opts.config.get('colours', 'box', fallback='1. 1. 1.')) # Bounding box tracer ignores everything but Freestyle marked edges boxlines.select_silhouette = False boxlines.select_border = False boxlines.select_crease = False boxlines.select_edge_mark = True # Outline settings bpy.ops.scene.freestyle_lineset_add() atomlines = renderlayer.freestyle_settings.linesets.active atomlinestyle = atomlines.linestyle atomlinestyle.thickness = opts.get('outline_thickness', 3) atomlinestyle.color = str2list( opts.config.get('colours', 'outline', fallback='0. 0. 0.'))
def open_mode(**options): """ Open v_sim ascii file in Blender :param input_file: Path to file :type input_file: str :param camera_rot: Camera tilt adjustment in degrees :type camera_rot: float :param config: Settings from configuration files :type config: configparser.ConfigParser :param mass_weighting: Weight eigenvectors by mass. This has usually already been done when generating the .ascii file, in which case the default value of 0 is acceptable. A value of 1 corresponds to the formally correct mass scaling of m^-0.5 for non-weighted eigenvectors. A value of -1 will REMOVE existing mass-weighting. In-between values may be useful for re-scaling to see the range of movements in a system, but are not physically meaningful. :type mass_weighting: float :param end_frame: The ending frame number of the rendered Animation (default=start_frame+n_frames-1) :type end_frame: int or None :param miller: Miller indices of view :type miller: 3-tuple of floats :param mode_index: id of mode; 0 corresponds to first mode in ascii file :type mode_index: int :param n_frames: Animation length of a single oscillation cycle in frames (default=30) :type n_frames: Positive int :param normalise_vectors: Re-scale vectors so largest arrow is fixed length :type normalise_vectors: bool :param offset_box: Position of bbox in lattice vector coordinates. Default (0,0,0) (Left front bottom) :type offset_box: Vector or 3-tuple :param preview: Enable preview mode - single frame, smaller render :type preview: bool :param scale_arrow: Scale factor for arrows :type scale_arrow: float :param scale_atom: Scale of atoms, relative to covalent radius :type scale_atom: float :param scale_vib: Scale factor for oscillations (angstroms / normalised eigenvector). :type scale_vib: float :param show_box: If True, show bounding box :type show_box: bool :param start_frame: The starting frame number of the rendered animation (default=0) :type start_frame: int or None :param static: if True, ignore frame range and use single frame. Otherwise, add animation keyframes. :type static: bool :param supercell: supercell dimensions :type supercell: 3-tuple or 3-list of ints :param vectors: If True, show arrows :type vectors: bool :param zoom: Camera zoom adjustment :type zoom: float """ # Initialise Opts object, accessing options and user config opts = vsim2blender.Opts(options) # Work out actual frame range. # Priority goes 1. static/preview 2. end_frame 3. n_frames start_frame = opts.get('start_frame', 0) n_frames = opts.get('n_frames', 30) # Preview images default to static, others default to animated preview = opts.get('preview', False) if preview: static = opts.get('static', True) else: static = opts.get('static', False) if static: end_frame = start_frame else: end_frame = opts.get('end_frame', start_frame + n_frames - 1) input_file = opts.get('input_file', False) if input_file: (vsim_cell, positions, symbols, vibs) = import_vsim(input_file) lattice_vectors = cell_vsim_to_vectors(vsim_cell) else: raise Exception('No .ascii file provided') if opts.get('mass_weighting', 0): f = opts.get('mass_weighting', 1) masses = [ float(opts.config['masses'][symbol])**f for symbol in symbols ] else: masses = [1 for symbol in symbols] # Switch to a new empty scene bpy.ops.scene.new(type='EMPTY') # Draw bounding box if (opts.get('show_box', True)): bbox_offset = opts.get('offset_box', (0, 0, 0)) bbox_offset = Vector(bbox_offset) draw_bounding_box(lattice_vectors, offset=bbox_offset) # Draw atoms after checking config mode_index = opts.get('mode_index', 0) supercell = (opts.get('supercell', (2, 2, 2))) vectors = opts.get('vectors', False) normalise_vectors = opts.get('normalise_vectors', False) # Variable for tracking largest arrow max_vector = 0 arrow_objects = [] mass_weighting = opts.get('mass_weighting', 0.) assert type(mass_weighting) == float for cell_id_tuple in itertools.product(range(supercell[0]), range(supercell[1]), range(supercell[2])): cell_id = Vector(cell_id_tuple) for (atom_index, (position, symbol, mass)) in enumerate(zip(positions, symbols, masses)): atom = add_atom(position, lattice_vectors, symbol, cell_id=cell_id, scale_factor=opts.get('scale_atom', 1.), name='{0}_{1}_{2}{3}{4}'.format( atom_index, symbol, *cell_id_tuple), config=opts.config) if vectors or not static: displacement_vector = vibs[mode_index].vectors[atom_index] qpt = vibs[mode_index].qpt B = (2 * math.pi * Matrix(lattice_vectors).inverted().transposed()) qpt_cartesian = Vector(qpt) * B if not static: animate_atom_vibs(atom, qpt_cartesian, displacement_vector, start_frame=start_frame, end_frame=end_frame, n_frames=n_frames, magnitude=opts.get('scale_vib', 1.), mass=mass) if vectors: arrow_vector = vector_with_phase(atom, qpt_cartesian, displacement_vector) scale = arrow_vector.length loc = absolute_position(position, lattice_vectors=lattice_vectors, cell_id=cell_id) # Arrows are scaled by eigenvector magnitude arrow_objects.append( add_arrow(loc=loc, mass=mass, rot_euler=vector_to_euler(arrow_vector), scale=scale)) if vectors: col = str2list(opts.config.get('colours', 'arrow', fallback='0. 0. 0.')) bpy.data.materials['Arrow'].diffuse_color = col # Rescaling; either by clamping max or accounting for cell size if vectors and normalise_vectors: master_scale = opts.get('scale_arrow', 1.) # Compare first element of object scales; they should be isotropic! max_length = max((arrow.scale[0] for arrow in arrow_objects)) for arrow in arrow_objects: arrow.scale *= master_scale / max_length elif vectors: master_scale = opts.get('scale_arrow', 1.) * len(positions) for arrow in arrow_objects: arrow.scale *= master_scale # Position camera and colour world. Note that cameras as objects and # cameras as 'cameras' have different attributes, so need to look up # camera in bpy.data.cameras to set field of view. camera.setup_camera(lattice_vectors, field_of_view=0.2, opts=opts) bpy.context.scene.world = bpy.data.worlds['World'] bpy.data.worlds['World'].horizon_color = str2list( opts.config.get('colours', 'background', fallback='0.5 0.5 0.5'))