Source code for crystal_toolkit.helpers.povray_renderer

"""
Export wrapper for POV-Ray
For creating publication quality plots
"""
from jinja2 import Environment

HEAD = """
#version 3.7 ;
global_settings { assumed_gamma 1.8
                  ambient_light rgb<1, 1, 1>
}
background { rgb 0. } // Set the background to black

/*
Create an Atom object along with some textures.
The arguments are: Atom( position, radius, color, finish )
*/

#declare plastic_atom_finish = finish {
                                specular 0.2
                                roughness 0.001
                                ambient 0.075
                                diffuse 0.55
                                brilliance 1.5
                                conserve_energy
                              }

#macro Atom (P1, R1, C1, F1)
  #local T = texture {
                       pigment { C1 }
                       finish { F1 }
                     }
  sphere { P1, R1 texture {T} no_shadow }
#end

"""

CAMERA = """
/*
Define the camera and the view of the atoms
*/

camera {
   orthographic
   location <i,  j,  k>
   look_at  <ii, jj, kk>
   sky <0, 0, 1>
}

"""

LIGHTS = """
/*
Define light sources to illuminate the atoms. For visualizing mediam
media_interaction and media_attenuation are set to "off" so voxel
data is rendered to be transparent. Lights are automatically oriented
with respect to the camera position.
*/

// Overhead light source
light_source {
    <0, 0, 10>
    color rgb <1,1,1>*0.5
    parallel
    point_at <ii, jj, kk>*0.5
    media_interaction off
    media_attenuation off
}

// Rear (forward-facing) light source
light_source {
    < (i-ii), (j-jj), (k-kk)>*4
    color rgb <1,1,1> * 0.5
    parallel
    point_at <ii, jj, kk>
    media_interaction off
    media_attenuation off
}

// Left light source
light_source {
    <( (i-ii)*cos(60*pi/180) - (j-jj)*sin(60*pi/180) ), ( (i-ii)*sin(60*pi/180) + (j-jj)*cos(60*pi/180) ), k>
    color rgb <1,1,1>*0.5
    parallel
    point_at <ii, jj, kk>
    media_interaction off
    media_attenuation off
}

// Right light source
light_source {
    <( (i-ii)*cos(-60*pi/180) - (j-jj)*sin(-60*pi/180) ), ( (i-ii)*sin(-60*pi/180) + (j-jj)*cos(-60*pi/180) ), k>
    color rgb <1,1,1>*0.5
    parallel
    point_at <ii, jj, kk>
    media_interaction off
    media_attenuation off
}

"""

TEMP_SPHERE = """
// Draw atoms in the scene

{% for val in positions -%}
Atom(<{{val}}>, {{radius}}, {{color}}, plastic_atom_finish)
{% endfor %}
"""

TEMP_CYLINDER = """
// Draw bonds between atoms in the scene

#declare bond_texture = texture { pigment { {{color}} } finish { plastic_atom_finish } };

{% for ipos, fpos in posPairs -%}
cylinder { <{{ipos}}>, <{{fpos}}>, 0.1 texture { bond_texture } no_shadow }
{% endfor %}
"""

TEMP_LINE = """
// Draw the edges of the supercell in the scene

#declare bbox = texture { pigment { rgb <1,1,1> } }

{% for ipos, fpos in posPairs -%}
cylinder {<{{ipos}}>, <{{fpos}}>, 0.02 texture {bbox} no_shadow}
{% endfor %}

{% for val in cylCaps -%}
sphere {<{{val}}>, 0.02 texture {bbox} no_shadow}
{% endfor %}
"""


[docs]def pov_write_data(input_scene_comp, fstream): """ parse a primitive display object in crystaltoolkit and print it to POV-Ray input_scene_comp fstream """ vect = "{:.4f},{:.4f},{:.4f}" if input_scene_comp["type"] == "spheres": # Render atoms positions = input_scene_comp["positions"] positions = [vect.format(*pos) for pos in positions] color = input_scene_comp["color"].replace("#", "") color = tuple(int(color[i : i + 2], 16) / 255.0 for i in (0, 2, 4)) color = f"rgb<{vect.format(*color)}>" fstream.write( Environment() .from_string(TEMP_SPHERE) .render( positions=positions, radius=input_scene_comp["radius"], color=color, ) ) if input_scene_comp["type"] == "cylinders": # Render bonds between atoms posPairs = [ [vect.format(*ipos), vect.format(*fpos)] for ipos, fpos in input_scene_comp["positionPairs"] ] color = input_scene_comp["color"].replace("#", "") color = tuple(int(color[i : i + 2], 16) / 255.0 for i in (0, 2, 4)) color = f"rgb<{vect.format(*color)}>" fstream.write( Environment() .from_string(TEMP_CYLINDER) .render(posPairs=posPairs, color=color) ) if input_scene_comp["type"] == "lines": # Render the cell pos1, pos2 = ( input_scene_comp["positions"][0::2], input_scene_comp["positions"][1::2], ) cylCaps = {tuple(pos) for pos in input_scene_comp["positions"]} cylCaps = [vect.format(*pos) for pos in cylCaps] posPairs = [ [vect.format(*ipos), vect.format(*fpos)] for ipos, fpos in zip(pos1, pos2) ] fstream.write( Environment() .from_string(TEMP_LINE) .render(posPairs=posPairs, cylCaps=cylCaps) )
[docs]def filter_data(scene_data, fstream): """ Recursively traverse the scene_data dictionary to find objects to draw """ if "type" in scene_data: pov_write_data(scene_data, fstream) else: for itr in scene_data["contents"]: filter_data(itr, fstream)
[docs]def write_pov_file(smc, file_name): """ smc : (StructureMoleculeComponent) """ fstream = open(file_name, "w") fstream.write(HEAD) fstream.write(CAMERA) fstream.write(LIGHTS) filter_data(smc.initial_scene_data, fstream) fstream.close() fstream = open("render.ini", "w") render_settings = get_render_settings() fstream.write(render_settings) fstream.close()
[docs]def get_render_settings(file_name): """ Creates a POV-Ray render.ini file """ image_name = f"{file_name[:-4]}.png" settings = f""" Input_File_Name = {file_name} Output_File_Name = {image_name} Display = 1 # -- Option to switch on the density Declare=render_density=0 # 0 = off, 1 = on Quality = 9 Height = 1200 Width = 1600 # -- Uncomment below for higher quality rendering Antialias = On Antialias_Threshold = 0.01 Antialias_Depth = 4 Jitter_Amount = 1.0 # -- Set the camera position Declare=i=8 Declare=j=5 Declare=k=4 # -- Set the look_at position Declare=ii=0 Declare=jj=0 Declare=kk=0 """ return settings