Coverage for /builds/ase/ase/ase/gui/render.py: 86.79%

106 statements  

« prev     ^ index     » next       coverage.py v7.5.3, created at 2025-08-02 00:12 +0000

1# fmt: off 

2 

3from os import unlink 

4 

5import numpy as np 

6 

7import ase.gui.ui as ui 

8from ase.gui.i18n import _ 

9from ase.io.pov import get_bondpairs, write_pov 

10 

11pack = error = Help = 42 

12 

13 

14class Render: 

15 texture_list = ['ase2', 'ase3', 'glass', 'simple', 'pale', 

16 'intermediate', 'vmd', 'jmol'] 

17 cameras = ['orthographic', 'perspective', 'ultra_wide_angle'] 

18 

19 def __init__(self, gui): 

20 self.gui = gui 

21 self.win = win = ui.Window( 

22 _('Render current view in povray ... '), wmtype='utility') 

23 win.add(ui.Label(_("Rendering %d atoms.") % len(self.gui.atoms))) 

24 

25 guiwidth, guiheight = self.get_guisize() 

26 self.width_widget = ui.SpinBox(guiwidth, start=1, end=9999, step=1) 

27 self.height_widget = ui.SpinBox(guiheight, start=1, end=9999, step=1) 

28 win.add([ui.Label(_('Size')), self.width_widget, 

29 ui.Label('⨯'), self.height_widget]) 

30 

31 self.linewidth_widget = ui.SpinBox(0.07, start=0.01, end=9.99, 

32 step=0.01) 

33 win.add([ui.Label(_('Line width')), self.linewidth_widget, 

34 ui.Label(_('Ångström'))]) 

35 

36 self.constraints_widget = ui.CheckButton(_("Render constraints")) 

37 self.cell_widget = ui.CheckButton(_("Render unit cell"), value=True) 

38 win.add([self.cell_widget, self.constraints_widget]) 

39 

40 formula = gui.atoms.get_chemical_formula(mode='hill') 

41 self.basename_widget = ui.Entry(width=30, value=formula, 

42 callback=self.update_outputname) 

43 win.add([ui.Label(_('Output basename: ')), self.basename_widget]) 

44 self.povray_executable = ui.Entry(width=30, value='povray') 

45 win.add([ui.Label(_('POVRAY executable')), self.povray_executable]) 

46 self.outputname_widget = ui.Label() 

47 win.add([ui.Label(_('Output filename: ')), self.outputname_widget]) 

48 self.update_outputname() 

49 

50 self.texture_widget = ui.ComboBox(labels=self.texture_list, 

51 values=self.texture_list) 

52 win.add([ui.Label(_('Atomic texture set:')), 

53 self.texture_widget]) 

54 # complicated texture stuff 

55 

56 self.camera_widget = ui.ComboBox(labels=self.cameras, 

57 values=self.cameras) 

58 self.camera_distance_widget = ui.SpinBox(50.0, -99.0, 99.0, 1.0) 

59 win.add([ui.Label(_('Camera type: ')), self.camera_widget]) 

60 win.add([ui.Label(_('Camera distance')), self.camera_distance_widget]) 

61 

62 # render current frame/all frames 

63 self.frames_widget = ui.RadioButtons([_('Render current frame'), 

64 _('Render all frames')]) 

65 win.add(self.frames_widget) 

66 if len(gui.images) == 1: 

67 self.frames_widget.buttons[1].widget.configure(state='disabled') 

68 

69 self.run_povray_widget = ui.CheckButton(_('Run povray'), True) 

70 self.keep_files_widget = ui.CheckButton(_('Keep povray files'), False) 

71 self.show_output_widget = ui.CheckButton(_('Show output window'), True) 

72 self.transparent = ui.CheckButton(_("Transparent background"), True) 

73 win.add(self.transparent) 

74 win.add([self.run_povray_widget, self.keep_files_widget, 

75 self.show_output_widget]) 

76 win.add(ui.Button(_('Render'), self.ok)) 

77 

78 def get_guisize(self): 

79 win = self.gui.window.win 

80 return win.winfo_width(), win.winfo_height() 

81 

82 def ok(self, *args): 

83 print("Rendering with povray:") 

84 _guiwidth, guiheight = self.get_guisize() 

85 width = self.width_widget.value 

86 height = self.height_widget.value 

87 # (Do width/height become inconsistent upon gui resize? Not critical) 

88 scale = self.gui.scale * height / guiheight 

89 bbox = np.empty(4) 

90 size = np.array([width, height]) / scale 

91 bbox[0:2] = np.dot(self.gui.center, self.gui.axes[:, :2]) - size / 2 

92 bbox[2:] = bbox[:2] + size 

93 

94 plotting_var_settings = { 

95 'bbox': bbox, 

96 'rotation': self.gui.axes, 

97 'show_unit_cell': self.cell_widget.value 

98 } 

99 

100 povray_settings = { 

101 'display': self.show_output_widget.value, 

102 'transparent': self.transparent.value, 

103 'camera_type': self.camera_widget.value, 

104 'camera_dist': self.camera_distance_widget.value, 

105 'canvas_width': width, 

106 'celllinewidth': self.linewidth_widget.value, 

107 'exportconstraints': self.constraints_widget.value, 

108 } 

109 

110 multiframe = bool(self.frames_widget.value) 

111 if multiframe: 

112 assert len(self.gui.images) > 1 

113 

114 if multiframe: 

115 frames = range(len(self.gui.images)) 

116 else: 

117 frames = [self.gui.frame] 

118 

119 initial_frame = self.gui.frame 

120 for frame in frames: 

121 self.gui.set_frame(frame) 

122 povray_settings['textures'] = self.get_textures() 

123 povray_settings['colors'] = self.gui.get_colors(rgb=True) 

124 atoms = self.gui.images.get_atoms(frame) 

125 radii_scale = 1 # atom size multiplier 

126 # self.gui.config['show_bonds'] is always False 

127 if self.gui.window['toggle-show-bonds']: 

128 print(" | Building bonds") 

129 povray_settings['bondatoms'] = get_bondpairs(atoms) 

130 radii_scale = 0.65 # value from draw method of View class 

131 filename = self.update_outputname() 

132 print(" | Writing files for image", filename, "...") 

133 plotting_var_settings['radii'] = radii_scale * \ 

134 self.gui.get_covalent_radii() 

135 renderer = write_pov( 

136 filename, atoms, 

137 povray_settings=povray_settings, 

138 **plotting_var_settings) 

139 if self.run_povray_widget.value: 

140 renderer.render( 

141 povray_executable=self.povray_executable.value, 

142 clean_up=False) 

143 if not self.keep_files_widget.value: 

144 print(" | Deleting temporary file ", filename) 

145 unlink(filename) 

146 filename = filename[:-4] + '.ini' 

147 print(" | Deleting temporary file ", filename) 

148 unlink(filename) 

149 self.gui.set_frame(initial_frame) 

150 self.update_outputname() 

151 

152 def update_outputname(self): 

153 tokens = [self.basename_widget.value] 

154 movielen = len(self.gui.images) 

155 if movielen > 1: 

156 ndigits = len(str(movielen)) 

157 token = ('{:0' + str(ndigits) + 'd}').format(self.gui.frame) 

158 tokens.append(token) 

159 tokens.append('pov') 

160 fname = '.'.join(tokens) 

161 self.outputname_widget.text = fname 

162 return fname 

163 # if self.movie.get_active(): 

164 # while len(movie_index) + len(str(self.iframe)) < len( 

165 # str(self.nimages)): 

166 # movie_index += '0' 

167 # movie_index = '.' + movie_index + str(self.iframe) 

168 # name = self.basename.get_text() + movie_index + '.pov' 

169 # self.outputname.set_text(name) 

170 

171 def get_textures(self): 

172 return [self.texture_widget.value] * len(self.gui.atoms) 

173 # natoms = len(self.gui.atoms) 

174 # textures = natoms * [ 

175 # self.texture_list[0] #self.default_texture.get_active()] 

176 # ] 

177 # for mat in self.materials: 

178 # sel = mat[1] 

179 # t = self.finish_list[mat[2].get_active()] 

180 # if mat[0]: 

181 # for n, val in enumerate(sel): 

182 # if val: 

183 # textures[n] = t 

184 # return textures