ebe41baaa81159a35bb2236a7005f5da085e051d
[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 bl_info = {
20     "name": "XYZ Atomic Blender",
21     "description": "Loading and manipulating atoms from XYZ files",
22     "author": "Clemens Barth",
23     "version": (0,5),
24     "blender": (2,6),
25     "location": "File -> Import -> XYZ (.xyz), Panel: View 3D - Tools",
26     "warning": "",
27     "wiki_url": "http://wiki.blender.org/index.php/Extensions:2.6/Py/Scripts/"
28                 "Import-Export/XYZ",
29     "tracker_url": "http://projects.blender.org/tracker/"
30                    "index.php?func=detail&aid=29646&group_id=153&atid=468",
31     "category": "Import-Export"
32 }
33
34
35 import bpy
36 from bpy.types import Operator, Panel
37 from bpy_extras.io_utils import ImportHelper
38 from bpy.props import (StringProperty,
39                        BoolProperty,
40                        EnumProperty,
41                        IntProperty,
42                        FloatProperty)
43
44
45
46 from . import import_xyz
47 ATOM_XYZ_ERROR = ""
48 ATOM_XYZ_NOTE  = ""
49
50 # -----------------------------------------------------------------------------
51 #                                                                           GUI
52
53 # This is the panel, which can be used to prepare the scene.
54 # It is loaded after the file has been chosen via the menu 'File -> Import'
55 class CLASS_atom_xyz_prepare_panel(Panel):
56     bl_label       = "XYZ - Atomic Blender"
57     #bl_space_type  = "PROPERTIES"
58     #bl_region_type = "WINDOW"
59     #bl_context     = "physics"
60     # This could be also an option ... :
61     bl_space_type  = "VIEW_3D"
62     bl_region_type = "TOOL_PROPS"
63
64     @classmethod
65     def poll(self, context):
66         if import_xyz.ATOM_XYZ_FILEPATH == "":
67             return False
68         else:
69             return True
70
71     def draw(self, context):
72         layout = self.layout
73         scn    = bpy.context.scene
74
75         row = layout.row()
76         row.label(text="Outputs and custom data file")
77
78         box = layout.box()
79         row = box.row()
80         row.label(text="Custom data file")
81         row = box.row()
82         col = row.column()
83         col.prop(scn, "atom_xyz_datafile")
84         col.operator("atom_xyz.datafile_apply")
85         row = box.row()
86         col = row.column(align=True)
87         col.prop(scn, "atom_xyz_XYZ_file")
88         row = box.row()
89         # TODO, use lanel() instead
90         row.prop(scn, "atom_xyz_number_atoms")
91         row = box.row()
92         row.operator("atom_xyz.button_distance")
93         row.prop(scn, "atom_xyz_distance")
94
95         row = layout.row()
96         row.label(text="Choice of atom radii")
97         box = layout.box()
98
99         row = box.row()
100         row.label(text="All changes concern:")
101         row = box.row()
102         row.prop(scn, "atom_xyz_radius_how")
103
104         row = box.row()
105         row.label(text="1. Change type of radii")
106         row = box.row()
107         row.prop(scn, "atom_xyz_radius_type")
108
109         row = box.row()
110         row.label(text="2. Change atom radii in pm")
111         row = box.row()
112         row.prop(scn, "atom_xyz_radius_pm_name")
113         row = box.row()
114         row.prop(scn, "atom_xyz_radius_pm")
115
116         row = box.row()
117         row.label(text="3. Change atom radii by scale")
118         row = box.row()
119         col = row.column()
120         col.prop(scn, "atom_xyz_radius_all")
121         col = row.column(align=True)
122         col.operator( "atom_xyz.radius_all_bigger" )
123         col.operator( "atom_xyz.radius_all_smaller" )
124
125         if bpy.context.mode == 'EDIT_MESH':
126
127             layout.separator()
128             row = box.row()
129             row.operator( "atom_xyz.separate_atom" )
130
131         row = layout.row()
132         row.label(text="Loading frames")
133
134         box = layout.box()
135         row = box.row()
136         col = row.column()
137         col.label(text="Frames")
138         col = row.column()
139         col.prop(scn, "atom_xyz_number_frames")
140         row = box.row()
141         col = row.column()
142         col.label(text="Skip frames")
143         col = row.column()
144         col.prop(scn, "atom_xyz_skip_frames")
145         row = box.row()
146         col = row.column()
147         col.label(text="Frames/key")
148         col = row.column()
149         col.prop(scn, "atom_xyz_images_per_key")        
150         
151         row = box.row()
152         row.operator("atom_xyz.load_frames")
153         row = box.row()
154         row.operator("atom_xyz.delete_keys")
155         row = box.row()
156         row.operator( "atom_xyz.create_command")
157         row = box.row()
158         row.operator( "atom_xyz.render")
159
160
161 class CLASS_atom_xyz_IO(bpy.types.PropertyGroup):
162
163     def Callback_radius_type(self, context):
164         scnn = bpy.context.scene
165         import_xyz.DEF_atom_xyz_radius_type(
166                 scnn.atom_xyz_radius_type,
167                 scnn.atom_xyz_radius_how,
168                 )
169
170     def Callback_radius_pm(self, context):
171         scnn = bpy.context.scene
172         import_xyz.DEF_atom_xyz_radius_pm(
173                 scnn.atom_xyz_radius_pm_name,
174                 scnn.atom_xyz_radius_pm,
175                 scnn.atom_xyz_radius_how,
176                 )
177
178     # In the file dialog window
179     scn = bpy.types.Scene
180     scn.use_atom_xyz_cam = BoolProperty(
181         name="Camera", default=False,
182         description="Do you need a camera?")
183     scn.use_atom_xyz_lamp = BoolProperty(
184         name="Lamp", default=False,
185         description = "Do you need a lamp?")
186     scn.use_atom_xyz_mesh = BoolProperty(
187         name = "Mesh balls", default=False,
188         description = "Do you want to use mesh balls instead of NURBS?")
189     scn.atom_xyz_mesh_azimuth = IntProperty(
190         name = "Azimuth", default=32, min=0,
191         description = "Number of sectors (azimuth)")
192     scn.atom_xyz_mesh_zenith = IntProperty(
193         name = "Zenith", default=32, min=0,
194         description = "Number of sectors (zenith)")
195     scn.atom_xyz_scale_ballradius = FloatProperty(
196         name = "Balls", default=1.0, min=0.0,
197         description = "Scale factor for all atom radii")
198     scn.atom_xyz_scale_distances = FloatProperty (
199         name = "Distances", default=1.0, min=0.0,
200         description = "Scale factor for all distances")
201     scn.use_atom_xyz_center = BoolProperty(
202         name = "Object to origin", default=False,
203         description = "Shall the object first put into the global origin "
204         "before applying the offsets on the left?")
205     scn.atom_xyz_atomradius = EnumProperty(
206         name="Type of radius",
207         description="Choose type of atom radius",
208         items=(('0', "Pre-defined", "Use pre-defined radii"),
209                ('1', "Atomic", "Use atomic radii"),
210                ('2', "van der Waals", "Use van der Waals radii")),
211                default='0',)
212
213     # In the panel, first part
214     scn.atom_xyz_datafile = StringProperty(
215         name = "", description="Path to your custom data file",
216         maxlen = 256, default = "", subtype='FILE_PATH')
217     scn.atom_xyz_XYZ_file = StringProperty(
218         name = "Path to file", default="",
219         description = "Path of the XYZ file")
220     # TODO, remove this property, its used for display only!
221     scn.atom_xyz_number_atoms = StringProperty(name="",
222         default="Number", description = "This output shows "
223         "the number of atoms which have been loaded")
224     scn.atom_xyz_distance = StringProperty(
225         name="", default="Distance (A)",
226         description="Distance of 2 objects in Angstrom")
227     scn.atom_xyz_radius_how = EnumProperty(
228         name="",
229         description="Which objects shall be modified?",
230         items=(('ALL_ACTIVE',"all active objects", "in the current layer"),
231                ('ALL_IN_LAYER',"all"," in active layer(s)")),
232                default='ALL_ACTIVE',)
233     scn.atom_xyz_radius_type = EnumProperty(
234         name="Type",
235         description="Which type of atom radii?",
236         items=(('0',"predefined", "Use pre-defined radii"),
237                ('1',"atomic", "Use atomic radii"),
238                ('2',"van der Waals","Use van der Waals radii")),
239                default='0',update=Callback_radius_type)
240     scn.atom_xyz_radius_pm_name = StringProperty(
241         name="", default="Atom name",
242         description="Put in the name of the atom (e.g. Hydrogen)")
243     scn.atom_xyz_radius_pm = FloatProperty(
244         name="", default=100.0, min=0.0,
245         description="Put in the radius of the atom (in pm)",
246         update=Callback_radius_pm)
247     scn.atom_xyz_radius_all = FloatProperty(
248         name="Scale", default = 1.05, min=1.0,
249         description="Put in the scale factor")
250
251
252     # In the panel, second part
253     scn.atom_xyz_number_frames = StringProperty(
254         name="", default="0",
255         description="This is the total number of frames stored in the xyz file")
256     scn.atom_xyz_skip_frames = IntProperty(
257         name="", default=0, min=0,
258         description="Number of frames you want to skip.")
259     scn.atom_xyz_images_per_key = IntProperty(
260         name="", default=1, min=0,
261         description="Choose the number of images between 2 keys.")
262
263
264
265 # Button for creating a file that contains the command for rendering
266 class CLASS_atom_xyz_create_command(Operator):
267     bl_idname = "atom_xyz.create_command"
268     bl_label = "Create command"
269     bl_description = "Create a shell command for rendering the scene"
270
271     def execute(self, context):
272         global ATOM_XYZ_ERROR
273         global ATOM_XYZ_NOTE
274         import os
275  
276         scn = bpy.context.scene
277
278         fstart = scn.frame_start
279         fend = scn.frame_end
280         file_blend = bpy.context.blend_data.filepath
281         
282         if file_blend == "":
283             ATOM_XYZ_ERROR = "Save your scene first !"
284             bpy.ops.atom_xyz.error_dialog('INVOKE_DEFAULT')
285             return {'FINISHED'}
286             
287         cameras = []    
288         FOUND = False    
289         for obj in bpy.context.scene.objects:  
290             if obj.type == "CAMERA":
291                 cameras.append(obj)
292                 FOUND = True   
293         if FOUND == False:
294             ATOM_XYZ_ERROR = "No camera => no images !"
295             bpy.ops.atom_xyz.error_dialog('INVOKE_DEFAULT')
296             return {'FINISHED'}      
297         if bpy.context.scene.camera == None:
298             bpy.context.scene.camera = cameras[0]
299             
300         KEYS_PRESENT = True
301         for element in import_xyz.STRUCTURE:
302             bpy.ops.object.select_all(action='DESELECT')
303             bpy.context.scene.objects.active = element
304             element.select = True
305             if element.data.shape_keys == None:
306                 KEYS_PRESENT = False
307                 break       
308         if KEYS_PRESENT == False:
309             ATOM_XYZ_ERROR = "No frames => no movie !"
310             bpy.ops.atom_xyz.error_dialog('INVOKE_DEFAULT')
311             return {'FINISHED'}     
312         
313         bpy.ops.wm.save_mainfile()
314         
315         file_name = bpy.path.basename(file_blend)
316         file_path = file_blend.replace(file_name,"")
317         file_movie = bpy.path.display_name_from_filepath(file_blend)
318         blender_exe = bpy.app.binary_path
319                 
320         if os.name == "posix":
321             execute = (blender_exe+" -b \'"+file_blend+"\' -x 1 -o //"+file_movie+
322                   "_ -F AVIJPEG -s "+str(fstart)+" -e "+str(scn.frame_end)+" -a")
323         else:
324             execute = ("\""+blender_exe+"\" -b "+file_blend+" -x 1 -o //"+file_movie+
325                   "_ -F AVIJPEG -s "+str(fstart)+" -e "+str(scn.frame_end)+" -a")
326
327         if os.name == "posix":
328             command_file = file_path + file_movie + ".sh"
329         else:
330             command_file = file_path + file_movie + ".txt"
331         command_fp = open(command_file,"w")
332            
333         if os.name == "posix":        
334             command_fp.write("#!/bin/sh\n")   
335         command_fp.write("\n"+execute+"\n")     
336         command_fp.close()
337         
338         ATOM_XYZ_NOTE = "The command has been stored (dir. of the .blend file)"
339         bpy.ops.atom_xyz.note_dialog('INVOKE_DEFAULT')
340
341         return {'FINISHED'}
342
343
344 # Button for rendering the scene in a terminal
345 class CLASS_atom_xyz_render(Operator):
346     bl_idname = "atom_xyz.render"
347     bl_label = "Render"
348     bl_description = "Render the scene"
349
350     def execute(self, context):
351         global ATOM_XYZ_ERROR
352         import os
353  
354         scn = bpy.context.scene
355
356         fstart = scn.frame_start
357         fend = scn.frame_end
358         file_blend = bpy.context.blend_data.filepath
359         
360         if file_blend == "":
361             ATOM_XYZ_ERROR = "Save your scene first!"
362             bpy.ops.atom_xyz.error_dialog('INVOKE_DEFAULT')
363             return {'FINISHED'}
364             
365         cameras = []    
366         FOUND = False    
367         for obj in bpy.context.scene.objects:  
368             if obj.type == "CAMERA":
369                 cameras.append(obj)
370                 FOUND = True   
371         if FOUND == False:
372             ATOM_XYZ_ERROR = "No camera => no images !"
373             bpy.ops.atom_xyz.error_dialog('INVOKE_DEFAULT')
374             return {'FINISHED'}      
375         if bpy.context.scene.camera == None:
376             bpy.context.scene.camera = cameras[0]
377             
378             
379         KEYS_PRESENT = True
380         for element in import_xyz.STRUCTURE:
381             bpy.ops.object.select_all(action='DESELECT')
382             bpy.context.scene.objects.active = element
383             element.select = True
384             if element.data.shape_keys == None:
385                 KEYS_PRESENT = False
386                 break       
387         if KEYS_PRESENT == False:
388             ATOM_XYZ_ERROR = "No frames => no movie !"
389             bpy.ops.atom_xyz.error_dialog('INVOKE_DEFAULT')
390             return {'FINISHED'}    
391             
392         bpy.ops.wm.save_mainfile()    
393         
394         file_name = bpy.path.basename(file_blend)
395         file_path = file_blend.replace(file_name,"")
396         file_movie = bpy.path.display_name_from_filepath(file_blend)
397         blender_exe = bpy.app.binary_path
398  
399         if os.name == "posix":
400             execute = (blender_exe+" -b \'"+file_blend+"\' -x 1 -o //"+file_movie+
401                   "_ -F AVIJPEG -s "+str(fstart)+" -e "+str(scn.frame_end)+" -a")
402             os_str = "xterm -e \"" + execute + "\" &"
403         else:
404             execute = ("\""+blender_exe+"\" -b "+file_blend+" -x 1 -o //"+file_movie+
405                   "_ -F AVIJPEG -s "+str(fstart)+" -e "+str(scn.frame_end)+" -a")
406             os_str = "C:\WINDOWS\system32\cmd.exe /C " + execute
407             
408         #print(os_str)    
409         os.system(os_str)    
410         
411         return {'FINISHED'}
412
413
414 # Button deleting all shape keys of the structure
415 class CLASS_atom_xyz_delete_keys(Operator):
416     bl_idname = "atom_xyz.delete_keys"
417     bl_label = "Delete keys"
418     bl_description = "Delete the shape keys"
419
420     def execute(self, context):
421     
422         for element in import_xyz.STRUCTURE:
423         
424             if element.data.shape_keys == None:
425                 break
426         
427             bpy.ops.object.select_all(action='DESELECT')
428             bpy.context.scene.objects.active = element
429             element.select = True
430         
431             for key in element.data.shape_keys.key_blocks:
432             
433                 bpy.ops.object.shape_key_remove()
434         
435
436         return {'FINISHED'}
437
438
439 # Button loading the shape keys
440 class CLASS_atom_xyz_load_frames(Operator):
441     bl_idname = "atom_xyz.load_frames"
442     bl_label = "Load frames"
443     bl_description = "Load the frames"
444
445     def execute(self, context):
446         global ATOM_XYZ_ERROR
447     
448         scn = bpy.context.scene
449         
450         KEYS_PRESENT = False
451         for element in import_xyz.STRUCTURE:
452             bpy.ops.object.select_all(action='DESELECT')
453             bpy.context.scene.objects.active = element
454             element.select = True
455             if element.data.shape_keys != None:
456                 KEYS_PRESENT = True
457                 break
458                 
459         if KEYS_PRESENT == True:
460             ATOM_XYZ_ERROR = "Delete first the keys"
461             bpy.ops.atom_xyz.error_dialog('INVOKE_DEFAULT')
462             return {'FINISHED'}
463         
464         
465         import_xyz.DEF_atom_xyz_build_frames(scn.atom_xyz_images_per_key, scn.atom_xyz_skip_frames)
466
467         return {'FINISHED'}
468
469
470
471 # Button loading a custom data file
472 class CLASS_atom_xyz_datafile_apply(Operator):
473     bl_idname = "atom_xyz.datafile_apply"
474     bl_label = "Apply"
475     bl_description = "Use color and radii values stored in the custom file"
476
477     def execute(self, context):
478     
479         scn = bpy.context.scene
480
481         if scn.atom_xyz_datafile == "":
482             return {'FINISHED'}
483
484         import_xyz.DEF_atom_xyz_custom_datafile(scn.atom_xyz_datafile)
485
486         # TODO, move this into 'import_xyz' and call the function
487         for obj in bpy.context.selected_objects:
488             if len(obj.children) != 0:
489                 child = obj.children[0]
490                 if child.type == "SURFACE" or child.type  == "MESH":
491                     for element in import_xyz.ATOM_XYZ_ELEMENTS:
492                         if element.name in obj.name:
493                             child.scale = (element.radii[0],) * 3
494                             child.active_material.diffuse_color = element.color
495             else:
496                 if obj.type == "SURFACE" or obj.type == "MESH":
497                     for element in import_xyz.ATOM_XYZ_ELEMENTS:
498                         if element.name in obj.name:
499                             obj.scale = (element.radii[0],) * 3
500                             obj.active_material.diffuse_color = element.color
501
502         return {'FINISHED'}
503
504
505 # Button for separating a single atom from a structure
506 class CLASS_atom_xyz_separate_atom(Operator):
507     bl_idname = "atom_xyz.separate_atom"
508     bl_label = "Separate atom"
509     bl_description = "Separate the atom you have chosen"
510
511     def execute(self, context):
512         scn    = bpy.context.scene
513
514         # Get first all important properties from the atom which the user
515         # has chosen: location, color, scale
516         obj = bpy.context.edit_object
517         name = obj.name
518         loc_obj_vec = obj.location
519         scale = obj.children[0].scale
520         material = obj.children[0].active_material
521
522         # Separate the vertex from the main mesh and create a new mesh.
523         bpy.ops.mesh.separate()
524         new_object = bpy.context.scene.objects[0]
525         # Keep in mind the coordinates <= We only need this
526         loc_vec = new_object.data.vertices[0].co
527
528         # And now, switch to the OBJECT mode such that we can ...
529         bpy.ops.object.mode_set(mode='OBJECT', toggle=False)
530         # ... delete the new mesh including the separated vertex
531         bpy.ops.object.select_all(action='DESELECT')
532         new_object.select = True
533         bpy.ops.object.delete()  # TODO, use scene.objects.unlink()
534
535         # Create a new atom/vacancy at the position of the old atom
536         current_layers=bpy.context.scene.layers
537
538         if "Vacancy" not in name:
539             if scn.use_atom_xyz_mesh == False:
540                 bpy.ops.surface.primitive_nurbs_surface_sphere_add(
541                                     view_align=False, enter_editmode=False,
542                                     location=loc_vec+loc_obj_vec,
543                                     rotation=(0.0, 0.0, 0.0),
544                                     layers=current_layers)
545             else:
546                 bpy.ops.mesh.primitive_uv_sphere_add(
547                                 segments=scn.atom_xyz_mesh_azimuth,
548                                 ring_count=scn.atom_xyz_mesh_zenith,
549                                 size=1, view_align=False, enter_editmode=False,
550                                 location=loc_vec+loc_obj_vec,
551                                 rotation=(0, 0, 0),
552                                 layers=current_layers)
553         else:
554             bpy.ops.mesh.primitive_cube_add(
555                                view_align=False, enter_editmode=False,
556                                location=loc_vec+loc_obj_vec,
557                                rotation=(0.0, 0.0, 0.0),
558                                layers=current_layers)
559
560         new_atom = bpy.context.scene.objects.active
561         # Scale, material and name it.
562         new_atom.scale = scale
563         new_atom.active_material = material
564         new_atom.name = name + "_sep"
565
566         # Switch back into the 'Edit mode' because we would like to seprate
567         # other atoms may be (more convinient)
568         new_atom.select = False
569         obj.select = True
570         bpy.context.scene.objects.active = obj
571         bpy.ops.object.select_all(action='DESELECT')
572         bpy.ops.object.mode_set(mode='EDIT', toggle=False)
573
574         return {'FINISHED'}
575
576
577 # Button for measuring the distance of the active objects
578 class CLASS_atom_xyz_distance_button(Operator):
579     bl_idname = "atom_xyz.button_distance"
580     bl_label = "Measure ..."
581     bl_description = "Measure the distance between two objects"
582
583     def execute(self, context):
584         scn    = bpy.context.scene
585         dist   = import_xyz.DEF_atom_xyz_distance()
586
587         if dist != "N.A.":
588            # The string length is cut, 3 digits after the first 3 digits
589            # after the '.'. Append also "Angstrom".
590            # Remember: 1 Angstrom = 10^(-10) m
591            pos    = str.find(dist, ".")
592            dist   = dist[:pos+4]
593            dist   = dist + " A"
594
595         # Put the distance into the string of the output field.
596         scn.atom_xyz_distance = dist
597         return {'FINISHED'}
598
599
600 # Button for increasing the radii of all atoms
601 class CLASS_atom_xyz_radius_all_bigger_button(Operator):
602     bl_idname = "atom_xyz.radius_all_bigger"
603     bl_label = "Bigger ..."
604     bl_description = "Increase the radii of the atoms"
605
606     def execute(self, context):
607         scn = bpy.context.scene
608         import_xyz.DEF_atom_xyz_radius_all(
609                 scn.atom_xyz_radius_all,
610                 scn.atom_xyz_radius_how,
611                 )
612         return {'FINISHED'}
613
614
615 # Button for decreasing the radii of all atoms
616 class CLASS_atom_xyz_radius_all_smaller_button(Operator):
617     bl_idname = "atom_xyz.radius_all_smaller"
618     bl_label = "Smaller ..."
619     bl_description = "Decrease the radii of the atoms"
620
621     def execute(self, context):
622         scn = bpy.context.scene
623         import_xyz.DEF_atom_xyz_radius_all(
624                 1.0/scn.atom_xyz_radius_all,
625                 scn.atom_xyz_radius_how,
626                 )
627         return {'FINISHED'}
628
629
630
631 # This is the class for the file dialog.
632 class ImportXYZ(Operator, ImportHelper):
633     bl_idname = "import_mesh.xyz"
634     bl_label  = "Import XYZ (*.xyz)"
635
636     filename_ext = ".xyz"
637     filter_glob  = StringProperty(default="*.xyz", options={'HIDDEN'},)
638
639     def draw(self, context):
640         layout = self.layout
641         scn = bpy.context.scene
642
643         row = layout.row()
644         row.prop(scn, "use_atom_xyz_cam")
645         row.prop(scn, "use_atom_xyz_lamp")
646         row = layout.row()
647         col = row.column()
648         col.prop(scn, "use_atom_xyz_mesh")
649         col = row.column(align=True)
650         col.prop(scn, "atom_xyz_mesh_azimuth")
651         col.prop(scn, "atom_xyz_mesh_zenith")
652
653         row = layout.row()
654         col = row.column()
655         col.label(text="Scaling factors")
656         col = row.column(align=True)
657         col.prop(scn, "atom_xyz_scale_ballradius")
658         col.prop(scn, "atom_xyz_scale_distances")
659
660         row = layout.row()
661         row.prop(scn, "use_atom_xyz_center")
662
663         row = layout.row()
664         row.prop(scn, "atom_xyz_atomradius")
665
666     def execute(self, context):
667         scn = bpy.context.scene
668         
669         import_xyz.ALL_FRAMES[:] = []
670         import_xyz.NUMBER_FRAMES = 0
671         import_xyz.ATOM_XYZ_ELEMENTS[:] = []
672         import_xyz.ATOM_XYZ_FILEPATH = ""
673         import_xyz.STRUCTURE[:] = []
674
675         # This is in order to solve this strange 'relative path' thing.
676         import_xyz.ATOM_XYZ_FILEPATH = bpy.path.abspath(self.filepath)
677
678         scn.atom_xyz_XYZ_file = import_xyz.ATOM_XYZ_FILEPATH
679
680         azimuth    = scn.atom_xyz_mesh_azimuth
681         zenith     = scn.atom_xyz_mesh_zenith
682         bradius    = scn.atom_xyz_scale_ballradius
683         bdistance  = scn.atom_xyz_scale_distances
684         radiustype = scn.atom_xyz_atomradius
685         center     = scn.use_atom_xyz_center
686         cam        = scn.use_atom_xyz_cam
687         lamp       = scn.use_atom_xyz_lamp
688         mesh       = scn.use_atom_xyz_mesh
689         datafile   = scn.atom_xyz_datafile
690
691         # Execute main routine
692         atom_number = import_xyz.DEF_atom_xyz_main(
693                 mesh, azimuth, zenith, bradius,
694                 radiustype, bdistance, 
695                 center, cam, lamp, datafile)
696
697         scn.atom_xyz_number_atoms = str(atom_number) + " atoms"
698         scn.atom_xyz_number_frames = str(import_xyz.NUMBER_FRAMES)
699         
700         return {'FINISHED'}
701         
702    
703 class CLASS_atom_xyz_error_dialog(bpy.types.Operator):
704     bl_idname = "atom_xyz.error_dialog"
705     bl_label = "Attention !"
706     
707     def draw(self, context):
708         layout = self.layout
709         row = layout.row()
710         row.label(text="                          "+ATOM_XYZ_ERROR) 
711     def execute(self, context):
712         print("Atomic Blender - Error: "+ATOM_XYZ_ERROR+"\n")
713         return {'FINISHED'}
714     def invoke(self, context, event):
715         return context.window_manager.invoke_props_dialog(self)
716
717
718 class CLASS_atom_xyz_note_dialog(bpy.types.Operator):
719     bl_idname = "atom_xyz.note_dialog"
720     bl_label = "Attention !"
721     
722     def draw(self, context):
723         layout = self.layout
724         row = layout.row()
725         row.label(text=ATOM_XYZ_NOTE) 
726     def execute(self, context):
727         print("Atomic Blender - Note: "+ATOM_XYZ_NOTE+"\n")
728         return {'FINISHED'}
729     def invoke(self, context, event):
730         return context.window_manager.invoke_props_dialog(self)
731         
732
733 # The entry into the menu 'file -> import'
734 def menu_func(self, context):
735     self.layout.operator(ImportXYZ.bl_idname, text="XYZ (.xyz)")
736
737
738 def register():
739     bpy.utils.register_module(__name__)
740     bpy.types.INFO_MT_file_import.append(menu_func)
741
742 def unregister():
743     bpy.utils.unregister_module(__name__)
744     bpy.types.INFO_MT_file_import.remove(menu_func)
745
746 if __name__ == "__main__":
747
748     register()