Statistiques
| Révision :

root / ase / io / pov.py @ 15

Historique | Voir | Annoter | Télécharger (8,99 ko)

1
"""
2
Module for povray file format support.
3

4
See http://www.povray.org/ for details on the format.
5
"""
6
import os
7

    
8
import numpy as np
9

    
10
from ase.io.eps import EPS
11
from ase.data import chemical_symbols
12

    
13

    
14
def pa(array):
15
    """Povray array syntax"""
16
    return '<% 6.2f, % 6.2f, % 6.2f>' % tuple(array)
17

    
18

    
19
def pc(array):
20
    """Povray color syntax"""
21
    if type(array) == str:
22
        return 'color ' + array
23
    if type(array) == float:
24
        return 'rgb <%.2f>*3' % array
25
    if len(array) == 3:
26
        return 'rgb <%.2f, %.2f, %.2f>' % tuple(array)
27
    if len(array) == 4: # filter
28
        return 'rgbf <%.2f, %.2f, %.2f, %.2f>' % tuple(array)
29
    if len(array) == 5: # filter and transmit
30
        return 'rgbft <%.2f, %.2f, %.2f, %.2f, %.2f>' % tuple(array)
31

    
32

    
33
def get_bondpairs(atoms, radius=1.1):
34
    """Get all pairs of bonding atoms
35

36
    Return all pairs of atoms which are closer than radius times the
37
    sum of their respective covalent radii.  The pairs are returned as
38
    tuples::
39

40
      (a, b, (i1, i2, i3))
41

42
    so that atoms a bonds to atom b displaced by the vector::
43

44
        _     _     _
45
      i c + i c + i c ,
46
       1 1   2 2   3 3
47

48
    where c1, c2 and c3 are the unit cell vectors and i1, i2, i3 are
49
    integers."""
50
    
51
    from ase.data import covalent_radii
52
    from ase.calculators.neighborlist import NeighborList
53
    cutoffs = radius * covalent_radii[atoms.numbers]
54
    nl = NeighborList(cutoffs=cutoffs, self_interaction=False)
55
    nl.update(atoms)
56
    bondpairs = []
57
    for a in range(len(atoms)):
58
        indices, offsets = nl.get_neighbors(a)
59
        bondpairs.extend([(a, a2, offset)
60
                          for a2, offset in zip(indices, offsets)])
61
    return bondpairs
62

    
63

    
64
class POVRAY(EPS):
65
    default_settings = {
66
        # x, y is the image plane, z is *out* of the screen
67
        'display'      : True, # Display while rendering
68
        'pause'        : True, # Pause when done rendering (only if display)
69
        'transparent'  : True, # Transparent background
70
        'canvas_width' : None, # Width of canvas in pixels
71
        'canvas_height': None, # Height of canvas in pixels 
72
        'camera_dist'  : 50.,  # Distance from camera to front atom
73
        'image_plane'  : None, # Distance from front atom to image plane
74
        'camera_type'  : 'orthographic', # perspective, ultra_wide_angle
75
        'point_lights' : [],             # [[loc1, color1], [loc2, color2],...]
76
        'area_light'   : [(2., 3., 40.), # location
77
                          'White',       # color
78
                          .7, .7, 3, 3], # width, height, Nlamps_x, Nlamps_y
79
        'background'   : 'White',        # color
80
        'textures'     : None, # Length of atoms list of texture names
81
        'celllinewidth': 0.05, # Radius of the cylinders representing the cell
82
        'bondlinewidth': 0.10, # Radius of the cylinders representing the bonds
83
        'bondatoms'    : [], # [[atom1, atom2], ...] pairs of bonding atoms
84
        }
85

    
86
    def __init__(self, atoms, scale=1.0, **parameters):
87
        for k, v in self.default_settings.items():
88
            setattr(self, k, parameters.pop(k, v))
89
        EPS.__init__(self, atoms, scale=scale, **parameters)
90

    
91
    def cell_to_lines(self, A):
92
        return np.empty((0, 3)), None, None
93

    
94
    def write(self, filename, **settings):
95
        # Determine canvas width and height
96
        ratio = float(self.w) / self.h
97
        if self.canvas_width is None:
98
            if self.canvas_height is None:
99
                self.canvas_width = min(self.w * 15, 640)
100
            else:
101
                self.canvas_width = self.canvas_height * ratio
102
        elif self.canvas_height is not None:
103
            raise RuntimeError, "Can't set *both* width and height!"
104

    
105
        # Distance to image plane from camera
106
        if self.image_plane is None:
107
            if self.camera_type == 'orthographic':
108
                self.image_plane = 1 - self.camera_dist
109
            else:
110
                self.image_plane = 0
111
        self.image_plane += self.camera_dist
112

    
113
        # Produce the .ini file
114
        if filename.endswith('.pov'):
115
            ini = open(filename[:-4] + '.ini', 'w').write
116
        else:
117
            ini = open(filename + '.ini', 'w').write
118
        ini('Input_File_Name=%s\n' % filename)
119
        ini('Output_to_File=True\n')
120
        ini('Output_File_Type=N\n')
121
        ini('Output_Alpha=%s\n' % self.transparent)
122
        ini('; if you adjust Height, and width, you must preserve the ratio\n')
123
        ini('; Width / Height = %s\n' % repr(ratio))
124
        ini('Width=%s\n' % self.canvas_width)
125
        ini('Height=%s\n' % (self.canvas_width / ratio))
126
        ini('Antialias=True\n')
127
        ini('Antialias_Threshold=0.1\n')
128
        ini('Display=%s\n' % self.display)
129
        ini('Pause_When_Done=%s\n' % self.pause)
130
        ini('Verbose=False\n')
131
        del ini
132

    
133
        # Produce the .pov file
134
        w = open(filename, 'w').write
135
        w('#include "colors.inc"\n')
136
        w('#include "finish.inc"\n')
137
        w('\n')
138
        w('global_settings {assumed_gamma 1 max_trace_level 6}\n')
139
        w('background {%s}\n' % pc(self.background))
140
        w('camera {%s\n' % self.camera_type)
141
        w('  right -%.2f*x up %.2f*y\n' % (self.w, self.h))
142
        w('  direction %.2f*z\n' % self.image_plane)
143
        w('  location <0,0,%.2f> look_at <0,0,0>}\n' % self.camera_dist)
144
        for loc, rgb in self.point_lights:
145
            w('light_source {%s %s}\n' % (pa(loc), pc(rgb)))
146

    
147
        if self.area_light is not None:
148
            loc, color, width, height, nx, ny = self.area_light
149
            w('light_source {%s %s\n' % (pa(loc), pc(color)))
150
            w('  area_light <%.2f, 0, 0>, <0, %.2f, 0>, %i, %i\n' % (
151
                width, height, nx, ny))
152
            w('  adaptive 1 jitter}\n')
153

    
154
        w('\n')
155
        w('#declare simple = finish {phong 0.7}\n')
156
        w('#declare vmd = finish {'
157
          'ambient .0 '
158
          'diffuse .65 '
159
          'phong 0.1 '
160
          'phong_size 40. '
161
          'specular 0.500 }\n')
162
        w('#declare jmol = finish {'
163
          'ambient .2 '
164
          'diffuse .6 '
165
          'specular 1 '
166
          'roughness .001 '
167
          'metallic}\n')
168
        w('#declare ase2 = finish {'
169
          'ambient 0.05 '
170
          'brilliance 3 '
171
          'diffuse 0.6 '
172
          'metallic '
173
          'specular 0.70 '
174
          'roughness 0.04 '
175
          'reflection 0.15}\n')
176
        w('#declare ase3 = finish {'
177
          'ambient .15 '
178
          'brilliance 2 '
179
          'diffuse .6 '
180
          'metallic '
181
          'specular 1. '
182
          'roughness .001 '
183
          'reflection .0}\n')
184
        w('#declare glass = finish {' # Use filter 0.7
185
          'ambient .05 '
186
          'diffuse .3 '
187
          'specular 1. '
188
          'roughness .001}\n')
189
        w('#declare Rcell = %.3f;\n' % self.celllinewidth)
190
        w('#declare Rbond = %.3f;\n' % self.bondlinewidth)
191
        w('\n')
192
        w('#macro atom(LOC, R, COL, FIN)\n')
193
        w('  sphere{LOC, R texture{pigment{COL} finish{FIN}}}\n')
194
        w('#end\n')
195
        w('\n')
196
        
197
        z0 = self.X[:, 2].max()
198
        self.X -= (self.w / 2, self.h / 2, z0)
199

    
200
        # Draw unit cell
201
        if self.C is not None:
202
            self.C -= (self.w / 2, self.h / 2, z0)
203
            self.C.shape = (2, 2, 2, 3)
204
            for c in range(3):
205
                for j in ([0, 0], [1, 0], [1, 1], [0, 1]):
206
                    w('cylinder {')
207
                    for i in range(2):
208
                        j.insert(c, i)
209
                        w(pa(self.C[tuple(j)]) + ', ')
210
                        del j[c]
211
                    w('Rcell pigment {Black}}\n')
212

    
213
        # Draw atoms
214
        a = 0
215
        for loc, dia, color in zip(self.X, self.d, self.colors):
216
            tex = 'ase3'
217
            if self.textures is not None:
218
                tex = self.textures[a]
219
            w('atom(%s, %.2f, %s, %s) // #%i \n' % (
220
                pa(loc), dia / 2., pc(color), tex, a))
221
            a += 1
222

    
223
        # Draw atom bonds
224
        for pair in self.bondatoms:
225
            if len(pair) == 2:
226
                a, b = pair
227
                offset = (0, 0, 0)
228
            else:
229
                a, b, offset = pair
230
            R = np.dot(offset, self.A)
231
            mida = 0.5 * (self.X[a] + self.X[b] + R)
232
            midb = 0.5 * (self.X[a] + self.X[b] - R)
233
            if self.textures is not None:
234
                texa = self.textures[a]
235
                texb = self.textures[b]
236
            else:
237
                texa = texb = 'ase3'
238
            w('cylinder {%s, %s, Rbond texture{pigment {%s} finish{%s}}}\n' % (
239
                pa(self.X[a]), pa(mida), pc(self.colors[a]), texa))
240
            w('cylinder {%s, %s, Rbond texture{pigment {%s} finish{%s}}}\n' % (
241
                pa(self.X[b]), pa(midb), pc(self.colors[b]), texb))
242

    
243

    
244
def write_pov(filename, atoms, run_povray=False, **parameters):
245
    if isinstance(atoms, list):
246
        assert len(atoms) == 1
247
        atoms = atoms[0]
248
    assert 'scale' not in parameters
249
    POVRAY(atoms, **parameters).write(filename)
250
    if run_povray:
251
        errcode = os.system('povray %s.ini 2> /dev/null' % filename[:-4])
252
        if errcode != 0:
253
            raise OSError('Povray failed with error code %d' % errcode)