update window gen by sayproductions
[blender-addons-contrib.git] / io_mesh_xyz / __init__.py
1 # ##### BEGIN GPL LICENSE BLOCK #####
2 #
3 #  This program is free software; you can redistribute it and/or
4 #  modify it under the terms of the GNU General Public License
5 #  as published by the Free Software Foundation; either version 2
6 #  of the License, or (at your option) any later version.
7 #
8 #  This program is distributed in the hope that it will be useful,
9 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
10 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 #  GNU General Public License for more details.
12 #
13 #  You should have received a copy of the GNU General Public License
14 #  along with this program; if not, write to the Free Software Foundation,
15 #  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 #
17 # ##### END GPL LICENSE BLOCK #####
18
19 #
20 #
21 #  Authors           : Clemens Barth (Blendphys@root-1.de), ...
22 #
23 #  Homepage(Wiki)    : http://development.root-1.de/Atomic_Blender.php
24 #  Tracker           : http://projects.blender.org/tracker/index.php?func=detail&aid=29646&group_id=153&atid=468
25 #
26 #  Start of project              : 2011-12-01 by Clemens Barth
27 #  First publication in Blender  : 2011-12-18
28 #  Last modified                 : 2012-10-25
29 #
30 #  Acknowledgements: Thanks to ideasman, meta_androcto, truman, kilon,
31 #  dairin0d, PKHG, Valter, etc
32 #
33
34 bl_info = {
35     "name": "XYZ Atomic Blender",
36     "description": "Loading and manipulating atoms from XYZ files",
37     "author": "Clemens Barth",
38     "version": (0,7),
39     "blender": (2,6),
40     "location": "File -> Import -> XYZ (.xyz), Panel: View 3D - Tools",
41     "warning": "",
42     "wiki_url": "http://wiki.blender.org/index.php/Extensions:2.6/Py/Scripts/"
43                 "Import-Export/XYZ",
44     "tracker_url": "http://projects.blender.org/tracker/"
45                    "index.php?func=detail&aid=29646&group_id=153&atid=468",
46     "category": "Import-Export"
47 }
48
49 import os
50 import io
51 import bpy
52 import bmesh
53 from bpy.types import Operator, Panel
54 from bpy_extras.io_utils import ImportHelper, ExportHelper
55 from bpy.props import (StringProperty,
56                        BoolProperty,
57                        EnumProperty,
58                        IntProperty,
59                        FloatProperty)
60
61 from . import import_xyz
62 from . import export_xyz
63
64 ATOM_XYZ_ERROR = ""
65 ATOM_XYZ_NOTE  = ""
66 ATOM_XYZ_PANEL = ""
67
68 # -----------------------------------------------------------------------------
69 #                                                                           GUI
70
71 # This is the panel, which can be used to prepare the scene.
72 # It is loaded after the file has been chosen via the menu 'File -> Import'
73 class CLASS_atom_xyz_prepare_panel(Panel):
74     bl_label       = "XYZ - Atomic Blender"
75     bl_space_type  = "VIEW_3D"
76     bl_region_type = "TOOL_PROPS"
77
78     @classmethod
79     def poll(self, context):
80         global ATOM_XYZ_PANEL
81         
82         if ATOM_XYZ_PANEL == "0" and import_xyz.ATOM_XYZ_FILEPATH == "":
83             return False
84         if ATOM_XYZ_PANEL == "0" and import_xyz.ATOM_XYZ_FILEPATH != "":
85             return True
86         if ATOM_XYZ_PANEL == "1":
87             return True
88         if ATOM_XYZ_PANEL == "2":
89             return False
90         
91         return True
92
93
94     def draw(self, context):
95         layout = self.layout
96
97         if len(context.scene.atom_xyz) == 0:
98             bpy.context.scene.atom_xyz.add()
99
100         scn    = context.scene.atom_xyz[0]
101
102         row = layout.row()
103         row.label(text="Outputs and custom data file")
104         box = layout.box()
105         row = box.row()
106         row.label(text="Custom data file")
107         row = box.row()
108         col = row.column()
109         col.prop(scn, "datafile")
110         col.operator("atom_xyz.datafile_apply")
111         row = box.row()
112         col = row.column(align=True)
113         col.prop(scn, "XYZ_file")
114         row = box.row()
115         row.prop(scn, "number_atoms")
116         row = box.row()
117         row.operator("atom_xyz.button_distance")
118         row.prop(scn, "distance")
119         row = layout.row()
120         row.label(text="Choice of atom radii")
121         box = layout.box()
122         row = box.row()
123         row.label(text="All changes concern:")
124         row = box.row()
125         row.prop(scn, "radius_how")
126         row = box.row()
127         row.label(text="1. Change type of radii")
128         row = box.row()
129         row.prop(scn, "radius_type")
130         row = box.row()
131         row.label(text="2. Change atom radii in pm")
132         row = box.row()
133         row.prop(scn, "radius_pm_name")
134         row = box.row()
135         row.prop(scn, "radius_pm")
136         row = box.row()
137         row.label(text="3. Change atom radii by scale")
138         row = box.row()
139         col = row.column()
140         col.prop(scn, "radius_all")
141         col = row.column(align=True)
142         col.operator( "atom_xyz.radius_all_bigger" )
143         col.operator( "atom_xyz.radius_all_smaller" )
144
145         if bpy.context.mode == 'EDIT_MESH':
146             layout.separator()
147             row = box.row()
148             row.operator( "atom_xyz.separate_atom" )
149
150         row = layout.row()
151         row.label(text="Loading frames")
152         box = layout.box()
153         row = box.row()
154         col = row.column()
155         col.label(text="Frames")
156         col = row.column()
157         col.prop(scn, "number_frames")
158         row = box.row()
159         col = row.column()
160         col.label(text="Skip frames")
161         col = row.column()
162         col.prop(scn, "skip_frames")
163         row = box.row()
164         col = row.column()
165         col.label(text="Frames/key")
166         col = row.column()
167         col.prop(scn, "images_per_key")        
168         row = box.row()
169         row.operator("atom_xyz.load_frames")
170         row = box.row()
171         row.operator("atom_xyz.delete_keys")
172         row = box.row()
173         row.operator( "atom_xyz.create_command")
174         row = box.row()
175         row.operator( "atom_xyz.render")
176
177
178 class CLASS_atom_xyz_Properties(bpy.types.PropertyGroup):
179
180     def Callback_radius_type(self, context):
181         scn = bpy.context.scene.atom_xyz[0]
182         import_xyz.DEF_atom_xyz_radius_type(
183                 scn.radius_type,
184                 scn.radius_how,)
185
186     def Callback_radius_pm(self, context):
187         scn = bpy.context.scene.atom_xyz[0]
188         import_xyz.DEF_atom_xyz_radius_pm(
189                 scn.radius_pm_name,
190                 scn.radius_pm,
191                 scn.radius_how,)
192
193     # In the file dialog window
194     use_camera = BoolProperty(
195         name="Camera", default=False,
196         description="Do you need a camera?")
197     use_lamp = BoolProperty(
198         name="Lamp", default=False,
199         description = "Do you need a lamp?")
200     use_mesh = BoolProperty(
201         name = "Mesh balls", default=False,
202         description = "Do you want to use mesh balls instead of NURBS?")
203     mesh_azimuth = IntProperty(
204         name = "Azimuth", default=32, min=0,
205         description = "Number of sectors (azimuth)")
206     mesh_zenith = IntProperty(
207         name = "Zenith", default=32, min=0,
208         description = "Number of sectors (zenith)")
209     scale_ballradius = FloatProperty(
210         name = "Balls", default=1.0, min=0.0,
211         description = "Scale factor for all atom radii")
212     scale_distances = FloatProperty (
213         name = "Distances", default=1.0, min=0.0,
214         description = "Scale factor for all distances")
215     use_center = BoolProperty(
216         name = "Object to origin (first frames)", default=False,
217         description = "Put the object into the global origin, the first frame only")           
218     use_center_all = BoolProperty(
219         name = "Object to origin (all frames)", default=True,
220         description = "Put the object into the global origin, all frames") 
221     atomradius = EnumProperty(
222         name="Type of radius",
223         description="Choose type of atom radius",
224         items=(('0', "Pre-defined", "Use pre-defined radii"),
225                ('1', "Atomic", "Use atomic radii"),
226                ('2', "van der Waals", "Use van der Waals radii")),
227                default='0',)
228     # In the panel, first part
229     datafile = StringProperty(
230         name = "", description="Path to your custom data file",
231         maxlen = 256, default = "", subtype='FILE_PATH')
232     XYZ_file = StringProperty(
233         name = "Path to file", default="",
234         description = "Path of the XYZ file")
235     number_atoms = StringProperty(name="",
236         default="Number", description = "This output shows "
237         "the number of atoms which have been loaded")
238     distance = StringProperty(
239         name="", default="Distance (A)",
240         description="Distance of 2 objects in Angstrom")
241     radius_how = EnumProperty(
242         name="",
243         description="Which objects shall be modified?",
244         items=(('ALL_ACTIVE',"all active objects", "in the current layer"),
245                ('ALL_IN_LAYER',"all"," in active layer(s)")),
246                default='ALL_ACTIVE',)
247     radius_type = EnumProperty(
248         name="Type",
249         description="Which type of atom radii?",
250         items=(('0',"predefined", "Use pre-defined radii"),
251                ('1',"atomic", "Use atomic radii"),
252                ('2',"van der Waals","Use van der Waals radii")),
253                default='0',update=Callback_radius_type)
254     radius_pm_name = StringProperty(
255         name="", default="Atom name",
256         description="Put in the name of the atom (e.g. Hydrogen)")
257     radius_pm = FloatProperty(
258         name="", default=100.0, min=0.0,
259         description="Put in the radius of the atom (in pm)",
260         update=Callback_radius_pm)
261     radius_all = FloatProperty(
262         name="Scale", default = 1.05, min=1.0, max=5.0,
263         description="Put in the scale factor")
264     # In the panel, second part
265     number_frames = StringProperty(
266         name="", default="0",
267         description="This is the total number of frames stored in the xyz file")
268     skip_frames = IntProperty(
269         name="", default=0, min=0,
270         description="Number of frames you want to skip.")
271     images_per_key = IntProperty(
272         name="", default=1, min=1,
273         description="Choose the number of images between 2 keys.")
274
275
276
277 # Button for creating a file that contains the command for rendering
278 class CLASS_atom_xyz_create_command(Operator):
279     bl_idname = "atom_xyz.create_command"
280     bl_label = "Create command"
281     bl_description = "Create a shell command for rendering the scene"
282
283     def execute(self, context):
284         global ATOM_XYZ_ERROR
285         global ATOM_XYZ_NOTE
286  
287         scn = bpy.context.scene
288
289         fstart = scn.frame_start
290         fend = scn.frame_end
291         file_blend = bpy.context.blend_data.filepath
292         
293         if file_blend == "":
294             ATOM_XYZ_ERROR = "Save your scene first !"
295             bpy.ops.atom_xyz.error_dialog('INVOKE_DEFAULT')
296             return {'FINISHED'}
297             
298         cameras = []    
299         FOUND = False    
300         for obj in bpy.context.scene.objects:  
301             if obj.type == "CAMERA":
302                 cameras.append(obj)
303                 FOUND = True   
304         if FOUND == False:
305             ATOM_XYZ_ERROR = "No camera => no images !"
306             bpy.ops.atom_xyz.error_dialog('INVOKE_DEFAULT')
307             return {'FINISHED'}      
308         if bpy.context.scene.camera == None:
309             bpy.context.scene.camera = cameras[0]
310             
311         KEYS_PRESENT = True
312         for element in import_xyz.STRUCTURE:
313             bpy.ops.object.select_all(action='DESELECT')
314             bpy.context.scene.objects.active = element
315             element.select = True
316             if element.data.shape_keys == None:
317                 KEYS_PRESENT = False
318                 break       
319         if KEYS_PRESENT == False:
320             ATOM_XYZ_ERROR = "No frames => no movie !"
321             bpy.ops.atom_xyz.error_dialog('INVOKE_DEFAULT')
322             return {'FINISHED'}     
323         
324         bpy.ops.wm.save_mainfile()
325         file_name = bpy.path.basename(file_blend)
326         file_path = file_blend.replace(file_name,"")
327         file_movie = bpy.path.display_name_from_filepath(file_blend)
328         blender_exe = bpy.app.binary_path
329                 
330         if os.name == "posix":
331             execute = (blender_exe+" -b \'"+file_blend+"\' -x 1 -o //"+file_movie+
332                   "_ -F AVIJPEG -s "+str(fstart)+" -e "+str(scn.frame_end)+" -a")
333         else:
334             execute = ("\""+blender_exe+"\" -b "+file_blend+" -x 1 -o //"+file_movie+
335                   "_ -F AVIJPEG -s "+str(fstart)+" -e "+str(scn.frame_end)+" -a")
336
337         if os.name == "posix":
338             command_file = file_path + file_movie + ".sh"
339         else:
340             command_file = file_path + file_movie + ".txt"
341         command_fp = open(command_file,"w")
342            
343         if os.name == "posix":        
344             command_fp.write("#!/bin/sh\n")   
345         command_fp.write("\n"+execute+"\n")     
346         command_fp.close()
347         
348         ATOM_XYZ_NOTE = "The command has been stored (dir. of the .blend file)"
349         bpy.ops.atom_xyz.note_dialog('INVOKE_DEFAULT')
350
351         return {'FINISHED'}
352
353
354 # Button for rendering the scene in a terminal
355 class CLASS_atom_xyz_render(Operator):
356     bl_idname = "atom_xyz.render"
357     bl_label = "Render"
358     bl_description = "Render the scene"
359
360     def execute(self, context):
361         global ATOM_XYZ_ERROR
362         scn = bpy.context.scene
363
364         fstart = scn.frame_start
365         fend = scn.frame_end
366         file_blend = bpy.context.blend_data.filepath
367         
368         if file_blend == "":
369             ATOM_XYZ_ERROR = "Save your scene first!"
370             bpy.ops.atom_xyz.error_dialog('INVOKE_DEFAULT')
371             return {'FINISHED'}
372             
373         cameras = []    
374         FOUND = False    
375         for obj in bpy.context.scene.objects:  
376             if obj.type == "CAMERA":
377                 cameras.append(obj)
378                 FOUND = True   
379         if FOUND == False:
380             ATOM_XYZ_ERROR = "No camera => no images !"
381             bpy.ops.atom_xyz.error_dialog('INVOKE_DEFAULT')
382             return {'FINISHED'}      
383         if bpy.context.scene.camera == None:
384             bpy.context.scene.camera = cameras[0]
385             
386             
387         KEYS_PRESENT = True
388         for element in import_xyz.STRUCTURE:
389             bpy.ops.object.select_all(action='DESELECT')
390             bpy.context.scene.objects.active = element
391             element.select = True
392             if element.data.shape_keys == None:
393                 KEYS_PRESENT = False
394                 break       
395         if KEYS_PRESENT == False:
396             ATOM_XYZ_ERROR = "No frames => no movie !"
397             bpy.ops.atom_xyz.error_dialog('INVOKE_DEFAULT')
398             return {'FINISHED'}    
399             
400         bpy.ops.wm.save_mainfile()    
401         
402         file_name = bpy.path.basename(file_blend)
403         file_path = file_blend.replace(file_name,"")
404         file_movie = bpy.path.display_name_from_filepath(file_blend)
405         blender_exe = bpy.app.binary_path
406  
407         if os.name == "posix":
408             execute = (blender_exe+" -b \'"+file_blend+"\' -x 1 -o //"+file_movie+
409                   "_ -F AVIJPEG -s "+str(fstart)+" -e "+str(scn.frame_end)+" -a")
410             os_str = "xterm -e \"" + execute + "\" &"
411         else:
412             execute = ("\""+blender_exe+"\" -b "+file_blend+" -x 1 -o //"+file_movie+
413                   "_ -F AVIJPEG -s "+str(fstart)+" -e "+str(scn.frame_end)+" -a")
414             os_str = "C:\WINDOWS\system32\cmd.exe /C " + execute
415              
416         os.system(os_str)    
417         
418         return {'FINISHED'}
419
420
421 # Button deleting all shape keys of the structure
422 class CLASS_atom_xyz_delete_keys(Operator):
423     bl_idname = "atom_xyz.delete_keys"
424     bl_label = "Delete keys"
425     bl_description = "Delete the shape keys"
426
427     def execute(self, context):
428
429         bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
430
431         for element in import_xyz.STRUCTURE:
432             if element.data.shape_keys == None:
433                 break
434         
435             bpy.ops.object.select_all(action='DESELECT')
436             bpy.context.scene.objects.active = element
437             element.select = True
438         
439             for key in element.data.shape_keys.key_blocks:
440                 bpy.ops.object.shape_key_remove()
441         
442         return {'FINISHED'}
443
444
445 # Button loading the shape keys
446 class CLASS_atom_xyz_load_frames(Operator):
447     bl_idname = "atom_xyz.load_frames"
448     bl_label = "Load frames"
449     bl_description = "Load the frames"
450
451     def execute(self, context):
452         global ATOM_XYZ_ERROR
453         scn = bpy.context.scene.atom_xyz[0]
454
455         bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
456         
457         KEYS_PRESENT = False
458         for element in import_xyz.STRUCTURE:
459             bpy.ops.object.select_all(action='DESELECT')
460             bpy.context.scene.objects.active = element
461             element.select = True
462             if element.data.shape_keys != None:
463                 KEYS_PRESENT = True
464                 break
465                 
466         if KEYS_PRESENT == True:
467             ATOM_XYZ_ERROR = "Delete first the keys"
468             bpy.ops.atom_xyz.error_dialog('INVOKE_DEFAULT')
469             return {'FINISHED'}
470         
471         
472         import_xyz.DEF_atom_xyz_build_frames(scn.images_per_key, 
473                                              scn.skip_frames)
474
475         return {'FINISHED'}
476
477
478
479 # Button loading a custom data file
480 class CLASS_atom_xyz_datafile_apply(Operator):
481     bl_idname = "atom_xyz.datafile_apply"
482     bl_label = "Apply"
483     bl_description = "Use color and radii values stored in the custom file"
484
485     def execute(self, context):
486         scn = bpy.context.scene.atom_xyz[0]
487
488         if scn.datafile == "":
489             return {'FINISHED'}
490
491         import_xyz.DEF_atom_xyz_custom_datafile(scn.datafile)
492
493         # TODO, move this into 'import_xyz' and call the function
494         for obj in bpy.context.selected_objects:
495             if len(obj.children) != 0:
496                 child = obj.children[0]
497                 if child.type == "SURFACE" or child.type  == "MESH":
498                     for element in import_xyz.ATOM_XYZ_ELEMENTS:
499                         if element.name in obj.name:
500                             child.scale = (element.radii[0],) * 3
501                             child.active_material.diffuse_color = element.color
502             else:
503                 if obj.type == "SURFACE" or obj.type == "MESH":
504                     for element in import_xyz.ATOM_XYZ_ELEMENTS:
505                         if element.name in obj.name:
506                             obj.scale = (element.radii[0],) * 3
507                             obj.active_material.diffuse_color = element.color
508
509         return {'FINISHED'}
510
511
512 # Button for separating a single atom from a structure
513 class CLASS_atom_xyz_separate_atom(Operator):
514     bl_idname = "atom_xyz.separate_atom"
515     bl_label = "Separate atoms"
516     bl_description = "Separate the atom you have chosen"
517
518     def execute(self, context):
519         scn = bpy.context.scene.atom_xyz[0]
520
521         # Get first all important properties from the atoms, which the user
522         # has chosen: location, color, scale
523         obj = bpy.context.edit_object
524         bm = bmesh.from_edit_mesh(obj.data)
525
526         locations = []
527
528         for v in bm.verts:
529             if v.select:
530                 locations.append(obj.matrix_world * v.co)
531
532         bm.free()
533         del(bm)
534
535         name  = obj.name
536         scale = obj.children[0].scale
537         material = obj.children[0].active_material
538
539         # Separate the vertex from the main mesh and create a new mesh.
540         bpy.ops.mesh.separate()
541         new_object = bpy.context.scene.objects[0]
542         # And now, switch to the OBJECT mode such that we can ...
543         bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
544         # ... delete the new mesh including the separated vertex
545         bpy.ops.object.select_all(action='DESELECT')
546         new_object.select = True
547         bpy.ops.object.delete()  
548
549         # Create new atoms/vacancies at the position of the old atoms
550         current_layers=bpy.context.scene.layers
551
552         # For all selected positions do:
553         for location in locations:
554             if "Vacancy" not in name:
555                 if scn.use_mesh == False:
556                     bpy.ops.surface.primitive_nurbs_surface_sphere_add(
557                                     view_align=False, enter_editmode=False,
558                                     location=location,
559                                     rotation=(0.0, 0.0, 0.0),
560                                     layers=current_layers)
561                 else:
562                     bpy.ops.mesh.primitive_uv_sphere_add(
563                                 segments=scn.mesh_azimuth,
564                                 ring_count=scn.mesh_zenith,
565                                 size=1, view_align=False, enter_editmode=False,
566                                 location=location,
567                                 rotation=(0, 0, 0),
568                                 layers=current_layers)
569             else:
570                 bpy.ops.mesh.primitive_cube_add(
571                                view_align=False, enter_editmode=False,
572                                location=location,
573                                rotation=(0.0, 0.0, 0.0),
574                                layers=current_layers)
575
576             new_atom = bpy.context.scene.objects.active
577             # Scale, material and name it.
578             new_atom.scale = scale
579             new_atom.active_material = material
580             new_atom.name = name + "_sep"
581             new_atom.select = False
582
583         bpy.context.scene.objects.active = obj
584         bpy.ops.object.select_all(action='DESELECT')
585
586         return {'FINISHED'}
587
588
589 # Button for measuring the distance of the active objects
590 class CLASS_atom_xyz_distance_button(Operator):
591     bl_idname = "atom_xyz.button_distance"
592     bl_label = "Measure ..."
593     bl_description = "Measure the distance between two objects"
594
595     def execute(self, context):
596         scn  = bpy.context.scene.atom_xyz[0]
597         dist = import_xyz.DEF_atom_xyz_distance()
598
599         if dist != "N.A.":
600            # The string length is cut, 3 digits after the first 3 digits
601            # after the '.'. Append also "Angstrom".
602            # Remember: 1 Angstrom = 10^(-10) m
603            pos    = str.find(dist, ".")
604            dist   = dist[:pos+4]
605            dist   = dist + " A"
606
607         # Put the distance into the string of the output field.
608         scn.distance = dist
609         return {'FINISHED'}
610
611
612 # Button for increasing the radii of all atoms
613 class CLASS_atom_xyz_radius_all_bigger_button(Operator):
614     bl_idname = "atom_xyz.radius_all_bigger"
615     bl_label = "Bigger ..."
616     bl_description = "Increase the radii of the atoms"
617
618     def execute(self, context):
619         scn = bpy.context.scene.atom_xyz[0]
620         import_xyz.DEF_atom_xyz_radius_all(
621                 scn.radius_all,
622                 scn.radius_how,
623                 )
624         return {'FINISHED'}
625
626
627 # Button for decreasing the radii of all atoms
628 class CLASS_atom_xyz_radius_all_smaller_button(Operator):
629     bl_idname = "atom_xyz.radius_all_smaller"
630     bl_label = "Smaller ..."
631     bl_description = "Decrease the radii of the atoms"
632
633     def execute(self, context):
634         scn = bpy.context.scene.atom_xyz[0]
635         import_xyz.DEF_atom_xyz_radius_all(
636                 1.0/scn.radius_all,
637                 scn.radius_how,
638                 )
639         return {'FINISHED'}
640
641
642 def DEF_panel_yes_no():
643     global ATOM_XYZ_PANEL
644
645     datafile_path = bpy.utils.user_resource('SCRIPTS', path='', create=False)
646     if os.path.isdir(datafile_path) == False:
647         bpy.utils.user_resource('SCRIPTS', path='', create=True)
648         
649     datafile_path = os.path.join(datafile_path, "presets")
650     if os.path.isdir(datafile_path) == False:
651         os.mkdir(datafile_path)   
652         
653     datafile = os.path.join(datafile_path, "io_mesh_xyz.pref")
654     if os.path.isfile(datafile):
655         datafile_fp = io.open(datafile, "r")
656         for line in datafile_fp:
657             if "Panel" in line:
658                 ATOM_XYZ_PANEL = line[-2:]
659                 ATOM_XYZ_PANEL = ATOM_XYZ_PANEL[0:1]
660                 bpy.context.scene.use_panel = ATOM_XYZ_PANEL
661                 break       
662         datafile_fp.close()
663     else:
664         DEF_panel_write_pref("0") 
665
666
667 def DEF_panel_write_pref(value): 
668     datafile_path = bpy.utils.user_resource('SCRIPTS', path='', create=False)
669     datafile_path = os.path.join(datafile_path, "presets")
670     datafile = os.path.join(datafile_path, "io_mesh_xyz.pref")
671     datafile_fp = io.open(datafile, "w")
672     datafile_fp.write("Atomic Blender XYZ - Import/Export - Preferences\n")
673     datafile_fp.write("================================================\n")
674     datafile_fp.write("\n")
675     datafile_fp.write("Panel: "+value+"\n\n\n")
676     datafile_fp.close()
677
678
679 class CLASS_atom_xyz_error_dialog(bpy.types.Operator):
680     bl_idname = "atom_xyz.error_dialog"
681     bl_label = "Attention !"
682     
683     def draw(self, context):
684         layout = self.layout
685         row = layout.row()
686         row.label(text="                          "+ATOM_XYZ_ERROR) 
687     def execute(self, context):
688         print("Atomic Blender - Error: "+ATOM_XYZ_ERROR+"\n")
689         return {'FINISHED'}
690     def invoke(self, context, event):
691         return context.window_manager.invoke_props_dialog(self)
692
693
694 class CLASS_atom_xyz_note_dialog(bpy.types.Operator):
695     bl_idname = "atom_xyz.note_dialog"
696     bl_label = "Attention !"
697     
698     def draw(self, context):
699         layout = self.layout
700         row = layout.row()
701         row.label(text=ATOM_XYZ_NOTE) 
702     def execute(self, context):
703         print("Atomic Blender - Note: "+ATOM_XYZ_NOTE+"\n")
704         return {'FINISHED'}
705     def invoke(self, context, event):
706         return context.window_manager.invoke_props_dialog(self)
707
708
709 # This is the class for the file dialog.
710 class CLASS_ImportXYZ(Operator, ImportHelper):
711     bl_idname = "import_mesh.xyz"
712     bl_label  = "Import XYZ (*.xyz)"
713     bl_options = {'PRESET', 'UNDO'}
714     
715     filename_ext = ".xyz"
716     filter_glob  = StringProperty(default="*.xyz", options={'HIDDEN'},)
717
718     bpy.types.Scene.use_panel = EnumProperty(
719         name="Panel",
720         description="Choose whether the panel shall appear or not in the View 3D.",
721         items=(('0', "Once", "The panel appears only in this session"),
722                ('1', "Always", "The panel always appears when Blender is started"),
723                ('2', "Never", "The panel never appears")),
724                default='0') 
725     use_camera = BoolProperty(
726         name="Camera", default=False,
727         description="Do you need a camera?")
728     use_lamp = BoolProperty(
729         name="Lamp", default=False,
730         description = "Do you need a lamp?")
731     use_mesh = BoolProperty(
732         name = "Mesh balls", default=False,
733         description = "Use mesh balls instead of NURBS")
734     mesh_azimuth = IntProperty(
735         name = "Azimuth", default=32, min=1,
736         description = "Number of sectors (azimuth)")
737     mesh_zenith = IntProperty(
738         name = "Zenith", default=32, min=1,
739         description = "Number of sectors (zenith)")
740     scale_ballradius = FloatProperty(
741         name = "Balls", default=1.0, min=0.0001,
742         description = "Scale factor for all atom radii")
743     scale_distances = FloatProperty (
744         name = "Distances", default=1.0, min=0.0001,
745         description = "Scale factor for all distances")
746     atomradius = EnumProperty(
747         name="Type of radius",
748         description="Choose type of atom radius",
749         items=(('0', "Pre-defined", "Use pre-defined radius"),
750                ('1', "Atomic", "Use atomic radius"),
751                ('2', "van der Waals", "Use van der Waals radius")),
752                default='0',)            
753     use_center = BoolProperty(
754         name = "Object to origin (first frames)", default=False,
755         description = "Put the object into the global origin, the first frame only")           
756     use_center_all = BoolProperty(
757         name = "Object to origin (all frames)", default=True,
758         description = "Put the object into the global origin, all frames") 
759     datafile = StringProperty(
760         name = "", description="Path to your custom data file",
761         maxlen = 256, default = "", subtype='FILE_PATH')    
762
763     def draw(self, context):
764         layout = self.layout
765         row = layout.row()
766         row.prop(self, "use_camera")
767         row.prop(self, "use_lamp")
768         row = layout.row()
769         col = row.column()
770         col.prop(self, "use_mesh")
771         col = row.column(align=True)
772         col.prop(self, "mesh_azimuth")
773         col.prop(self, "mesh_zenith")
774         row = layout.row()
775         col = row.column()
776         col.label(text="Scaling factors")
777         col = row.column(align=True)
778         col.prop(self, "scale_ballradius")
779         col.prop(self, "scale_distances")
780         row = layout.row()
781         row.prop(self, "use_center")
782         row = layout.row()
783         row.prop(self, "use_center_all")
784         row = layout.row()
785         row.prop(self, "atomradius")
786         row = layout.row()
787         row.prop(bpy.context.scene, "use_panel")
788
789     def execute(self, context):
790         
791         import_xyz.ALL_FRAMES[:] = []
792         import_xyz.NUMBER_FRAMES = 0
793         import_xyz.ATOM_XYZ_ELEMENTS[:] = []
794         import_xyz.ATOM_XYZ_FILEPATH = ""
795         import_xyz.STRUCTURE[:] = []
796
797         # This is in order to solve this strange 'relative path' thing.
798         import_xyz.ATOM_XYZ_FILEPATH = bpy.path.abspath(self.filepath)
799
800         # Execute main routine
801         atom_number = import_xyz.DEF_atom_xyz_main(
802                       self.use_mesh,
803                       self.mesh_azimuth,
804                       self.mesh_zenith,
805                       self.scale_ballradius,
806                       self.atomradius,
807                       self.scale_distances,
808                       self.use_center,
809                       self.use_center_all,
810                       self.use_camera,
811                       self.use_lamp,
812                       self.datafile)
813                       
814         # Copy the whole bunch of values into the property collection.
815         scn = context.scene.atom_xyz[0]
816         scn.use_mesh = self.use_mesh
817         scn.mesh_azimuth = self.mesh_azimuth
818         scn.mesh_zenith = self.mesh_zenith
819         scn.scale_ballradius = self.scale_ballradius
820         scn.atomradius = self.atomradius
821         scn.scale_distances = self.scale_distances
822         scn.use_center = self.use_center
823         scn.use_center_all = self.use_center_all
824         scn.use_camera = self.use_camera
825         scn.use_lamp = self.use_lamp
826         scn.datafile = self.datafile              
827                       
828         scn.number_atoms = str(atom_number) + " atoms"
829         scn.number_frames = str(import_xyz.NUMBER_FRAMES)
830         scn.XYZ_file = import_xyz.ATOM_XYZ_FILEPATH
831
832         global ATOM_XYZ_PANEL
833         ATOM_XYZ_PANEL = bpy.context.scene.use_panel
834         DEF_panel_write_pref(bpy.context.scene.use_panel)
835         
836         return {'FINISHED'}
837         
838
839 # This is the class for the file dialog of the exporter.
840 class CLASS_ExportXYZ(Operator, ExportHelper):
841     bl_idname = "export_mesh.xyz"
842     bl_label  = "Export XYZ (*.xyz)"
843     filename_ext = ".xyz"
844
845     filter_glob  = StringProperty(
846         default="*.xyz", options={'HIDDEN'},)
847
848     atom_xyz_export_type = EnumProperty(
849         name="Type of Objects",
850         description="Choose type of objects",
851         items=(('0', "All", "Export all active objects"),
852                ('1', "Elements", "Export only those active objects which have"
853                                  " a proper element name")),
854                default='1',) 
855
856     def draw(self, context):
857         layout = self.layout
858         row = layout.row()
859         row.prop(self, "atom_xyz_export_type")
860
861     def execute(self, context):
862         # This is in order to solve this strange 'relative path' thing.
863         export_xyz.ATOM_XYZ_FILEPATH = bpy.path.abspath(self.filepath)
864         export_xyz.DEF_atom_xyz_export(self.atom_xyz_export_type)
865
866         return {'FINISHED'}
867
868
869 # The entry into the menu 'file -> import'
870 def DEF_menu_func(self, context):
871     self.layout.operator(CLASS_ImportXYZ.bl_idname, text="XYZ (.xyz)")
872
873 # The entry into the menu 'file -> export'
874 def DEF_menu_func_export(self, context):
875     self.layout.operator(CLASS_ExportXYZ.bl_idname, text="XYZ (.xyz)")
876
877 def register():
878     DEF_panel_yes_no()
879     bpy.utils.register_module(__name__)
880     bpy.types.INFO_MT_file_import.append(DEF_menu_func)
881     bpy.types.INFO_MT_file_export.append(DEF_menu_func_export)
882     bpy.types.Scene.atom_xyz = bpy.props.CollectionProperty(type=CLASS_atom_xyz_Properties)    
883     bpy.context.scene.atom_xyz.add()
884     
885 def unregister():
886     bpy.utils.unregister_module(__name__)
887     bpy.types.INFO_MT_file_import.remove(DEF_menu_func)
888     bpy.types.INFO_MT_file_export.remove(DEF_menu_func_export)
889
890 if __name__ == "__main__":
891
892     register()