I re-designed the code for replacing/separating atoms: one can now
[blender-addons-contrib.git] / io_atomblend_utilities / __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 #
25 #  Start of project              : 2011-12-01 by Clemens Barth
26 #  First publication in Blender  : 2012-11-03
27 #  Last modified                 : 2013-01-23
28 #
29 #  Acknowledgements 
30 #  ================
31 #
32 #  Blender: ideasman_42, meta_androcto, truman, kilon, CoDEmanX, dairin0d, PKHG, 
33 #           Valter, ...
34 #  Other: Frank Palmino
35 #
36
37 bl_info = {
38     "name": "Atomic Blender - Utilities",
39     "description": "Utilities for manipulating atom structures",
40     "author": "Clemens Barth",
41     "version": (0, 95),
42     "blender": (2, 60, 0),
43     "location": "Panel: View 3D - Tools",
44     "warning": "",
45     "wiki_url": "http://wiki.blender.org/index.php/Extensions:2.6/"
46                 "Py/Scripts/Import-Export/PDB",
47     "tracker_url": "http://projects.blender.org/tracker/"
48                    "index.php?func=detail&aid=33071&group_id=153&atid=467",
49     "category": "Import-Export"
50 }
51
52 import bpy
53 from bpy.types import Operator, Panel
54 from bpy.props import (StringProperty,
55                        EnumProperty,
56                        FloatProperty,
57                        BoolProperty)
58
59 from . import io_atomblend_utilities
60
61 # -----------------------------------------------------------------------------
62 #                                                                           GUI
63
64 # The panel.
65 class PreparePanel(Panel):
66     bl_label       = "Atomic Blender Utilities"
67     bl_space_type  = "VIEW_3D"
68     bl_region_type = "TOOL_PROPS"
69
70     def draw(self, context):
71         layout = self.layout
72         scn    = context.scene.atom_blend
73
74         box = layout.box()
75         col = box.column(align=True) 
76         col.label(text="Custom data file")
77         col.prop(scn, "datafile")
78         col.operator("atom_blend.datafile_apply")
79         
80         box = layout.box()  
81         col = box.column(align=True)   
82         col.label(text="Measure distances")
83         col.operator("atom_blend.button_distance")
84         col.prop(scn, "distance")      
85         
86         box = layout.box()
87         col = box.column(align=True)
88         col.label(text="All changes concern:")
89         col.prop(scn, "obj_who")
90
91         box = layout.box()
92         col = box.column(align=True)
93         col.label(text="Change atom size")
94         col.label(text="1. Type of radii")
95         col.prop(scn, "radius_type")
96         col2 = col.column()
97         col2.active = (scn.radius_type == '3')
98         col2.prop(scn, "radius_type_ionic")   
99         col = box.column(align=True)
100         col.label(text="2. Radii in pm")
101         col.prop(scn, "radius_pm_name")
102         col.prop(scn, "radius_pm")
103         col = box.column(align=True)
104         col.label(text="3. Radii by scale")
105         col.prop(scn, "radius_all")
106         row = col.row()
107         row.operator("atom_blend.radius_all_smaller")
108         row.operator("atom_blend.radius_all_bigger")
109         
110         box = layout.box()
111         col = box.column(align=True)
112         col.label(text="Change stick size")
113         col.prop(scn, "sticks_all")
114         row = col.row()
115         row.operator("atom_blend.sticks_all_smaller")
116         row.operator("atom_blend.sticks_all_bigger")
117
118         box = layout.box()
119         col = box.column(align=True)
120         col.label(text="Change atom shape")
121         col.prop(scn, "replace_objs")
122         col.prop(scn, "replace_objs_material")
123         col.operator("atom_blend.replace_atom")  
124         col.label(text="Default values")
125         col.operator("atom_blend.default_atoms") 
126
127         box = layout.box()
128         col = box.column(align=True)
129         col.label(text="Separate atoms")
130         col2 = col.column()
131         col2.active = (bpy.context.mode == 'EDIT_MESH')
132         col2.prop(scn, "separate_objs")
133         col2.prop(scn, "separate_objs_material")        
134         col2.operator("atom_blend.separate_atom")
135         
136
137 # The properties of buttons etc. in the panel.
138 class PanelProperties(bpy.types.PropertyGroup):
139
140     def Callback_radius_type(self, context):
141         scn = bpy.context.scene.atom_blend
142         io_atomblend_utilities.choose_objects("ATOM_RADIUS_TYPE", 
143                                               scn.obj_who, 
144                                               None,
145                                               None,
146                                               scn.radius_type,
147                                               scn.radius_type_ionic,
148                                               None) 
149     def Callback_radius_pm(self, context):
150         scn = bpy.context.scene.atom_blend
151         io_atomblend_utilities.choose_objects("ATOM_RADIUS_PM", 
152                                               scn.obj_who, 
153                                               None,
154                                               [scn.radius_pm_name,
155                                               scn.radius_pm],
156                                               None,
157                                               None,
158                                               None) 
159         
160     datafile = StringProperty(
161         name = "", description="Path to your custom data file",
162         maxlen = 256, default = "", subtype='FILE_PATH')
163     XYZ_file = StringProperty(
164         name = "Path to file", default="",
165         description = "Path of the XYZ file")
166     number_atoms = StringProperty(name="",
167         default="Number", description = "This output shows "
168         "the number of atoms which have been loaded")
169     distance = StringProperty(
170         name="", default="Distance (A)",
171         description="Distance of 2 objects in Angstrom")
172     replace_objs = EnumProperty(
173         name="Shape",
174         description="Choose a different shape.",
175         items=(('0',"Unchanged", "Do not change the shape"),
176                ('1a',"Sphere (Mesh)", "Replace with a sphere (Mesh)"),
177                ('1b',"Sphere (NURBS)", "Replace with a sphere (NURBS)"),        
178                ('2',"Cube", "Replace with a cube"),
179                ('3',"Plane", "Replace with a plane"),
180                ('4a',"Circle (Mesh)", "Replace with a circle (Mesh)"),
181                ('4b',"Circle (NURBS)", "Replace with a circle (NURBS)"),               
182                ('5a',"Icosphere 1", "Replace with a icosphere, subd=1"),  
183                ('5b',"Icosphere 2", "Replace with a icosphere, subd=2"),  
184                ('5c',"Icosphere 3", "Replace with a icosphere, subd=3"),  
185                ('5d',"Icosphere 4", "Replace with a icosphere, subd=4"),
186                ('5e',"Icosphere 5", "Replace with a icosphere, subd=5"),                                                         
187                ('6a',"Cylinder (Mesh)", "Replace with a cylinder (Mesh)"),
188                ('6b',"Cylinder (NURBS)", "Replace with a cylinder (NURBS)"),               
189                ('7',"Cone", "Replace with a cone"),
190                ('8a',"Torus (Mesh)", "Replace with a torus (Mesh)"),
191                ('8b',"Torus (NURBS)", "Replace with a torus (NURBS)")),
192                default='0',)     
193     replace_objs_material = EnumProperty(
194         name="Material",
195         description="Choose a different material.",
196         items=(('0',"Unchanged", "Leave the material unchanged"),
197                ('1',"Normal", "Use normal material (no transparency and reflection)"),
198                ('2',"Transparent", "Use transparent material"),
199                ('3',"Reflecting", "Use reflecting material"),
200                ('4',"Transparent + reflecting", "Use transparent and reflecting material")),
201                default='0',)                           
202     obj_who = EnumProperty(
203         name="",
204         description="Which objects shall be modified?",
205         items=(('ALL_ACTIVE',"all active objects", "in the current layer"),
206                ('ALL_IN_LAYER',"all in all selected layers",
207                 "in selected layer(s)")),
208                default='ALL_ACTIVE',)
209     radius_type = EnumProperty(
210         name="Type",
211         description="Which type of atom radii?",
212         items=(('0',"predefined", "Use pre-defined radii"),
213                ('1',"atomic", "Use atomic radii"),
214                ('2',"van der Waals","Use van der Waals radii"),
215                ('3',"ionic radii", "Use ionic radii")),
216                default='0',update=Callback_radius_type)
217     radius_type_ionic = EnumProperty(
218         name="Charge",
219         description="Charge state of the ions if existing.",
220         items=(('0',"-4", "Charge state -4"),
221                ('1',"-3", "Charge state -3"),
222                ('2',"-2", "Charge state -2"),
223                ('3',"-1", "Charge state -1"),
224                ('4'," 0", "Charge state  0: nothing is done"),              
225                ('5',"+1", "Charge state +1"),
226                ('6',"+2", "Charge state +2"),
227                ('7',"+3", "Charge state +3"),
228                ('8',"+4", "Charge state +4"),
229                ('9',"+5", "Charge state +5"),
230                ('10',"+6", "Charge state +6"),
231                ('11',"+7", "Charge state +7")),
232                default='4',update=Callback_radius_type)           
233     radius_pm_name = StringProperty(
234         name="", default="Atom name",
235         description="Put in the name of the atom (e.g. Hydrogen)")
236     radius_pm = FloatProperty(
237         name="", default=100.0, min=0.0,
238         description="Put in the radius of the atom (in pm)",
239         update=Callback_radius_pm)
240     radius_all = FloatProperty(
241         name="Scale", default = 1.05, min=1.0, max=5.0,
242         description="Put in the scale factor")
243     sticks_all = FloatProperty(
244         name="Scale", default = 1.05, min=1.0, max=5.0,
245         description="Put in the scale factor")
246     separate_objs = EnumProperty(
247         name="Shape",
248         description="Choose a different shape.",
249         items=(('0',"Unchanged", "Do not change the shape"),
250                ('1a',"Sphere (Mesh)", "Replace with a sphere (Mesh)"),
251                ('1b',"Sphere (NURBS)", "Replace with a sphere (NURBS)"),        
252                ('2',"Cube", "Replace with a cube"),
253                ('3',"Plane", "Replace with a plane"),
254                ('4a',"Circle (Mesh)", "Replace with a circle (Mesh)"),
255                ('4b',"Circle (NURBS)", "Replace with a circle (NURBS)"),               
256                ('5a',"Icosphere 1", "Replace with a icosphere, subd=1"),  
257                ('5b',"Icosphere 2", "Replace with a icosphere, subd=2"),  
258                ('5c',"Icosphere 3", "Replace with a icosphere, subd=3"),  
259                ('5d',"Icosphere 4", "Replace with a icosphere, subd=4"),
260                ('5e',"Icosphere 5", "Replace with a icosphere, subd=5"),                                                         
261                ('6a',"Cylinder (Mesh)", "Replace with a cylinder (Mesh)"),
262                ('6b',"Cylinder (NURBS)", "Replace with a cylinder (NURBS)"),               
263                ('7',"Cone", "Replace with a cone"),
264                ('8a',"Torus (Mesh)", "Replace with a torus (Mesh)"),
265                ('8b',"Torus (NURBS)", "Replace with a torus (NURBS)")),
266                default='0',)     
267     separate_objs_material = EnumProperty(
268         name="Material",
269         description="Choose a different material.",
270         items=(('0',"Unchanged", "Leave the material unchanged"),
271                ('1',"Normal", "Use normal material (no transparency and reflection)"),
272                ('2',"Transparent", "Use transparent material"),
273                ('3',"Reflecting", "Use reflecting material"),
274                ('4',"Transparent + reflecting", "Use transparent and reflecting material")),
275                default='0',)  
276
277 # Button loading a custom data file
278 class DatafileApply(Operator):
279     bl_idname = "atom_blend.datafile_apply"
280     bl_label = "Apply"
281     bl_description = "Use color and radii values stored in the custom file"
282
283     def execute(self, context):
284         scn = bpy.context.scene.atom_blend
285
286         if scn.datafile == "":
287             return {'FINISHED'}
288
289         io_atomblend_utilities.custom_datafile(scn.datafile)
290         io_atomblend_utilities.custom_datafile_change_atom_props()
291
292         return {'FINISHED'}
293
294
295 # Button for separating single atoms from a dupliverts structure
296 class DefaultAtom(Operator):
297     bl_idname = "atom_blend.default_atoms"
298     bl_label = "Default"
299     bl_description = ("Use default shapes and colors for atoms.")
300
301     def execute(self, context):
302         scn = bpy.context.scene.atom_blend
303         io_atomblend_utilities.choose_objects("ATOM_DEFAULT_OBJ", 
304                                               scn.obj_who, 
305                                               None, 
306                                               None,
307                                               None,
308                                               None,
309                                               None) 
310         return {'FINISHED'}
311
312
313 # Button for separating single atoms from a dupliverts structure
314 class ReplaceAtom(Operator):
315     bl_idname = "atom_blend.replace_atom"
316     bl_label = "Replace"
317     bl_description = ("Replace selected atoms with atoms of different shape.")
318
319     def execute(self, context):
320         scn = bpy.context.scene.atom_blend
321         io_atomblend_utilities.choose_objects("ATOM_REPLACE_OBJ", 
322                                               scn.obj_who, 
323                                               None, 
324                                               None,
325                                               None,
326                                               None,
327                                               None) 
328         return {'FINISHED'}
329
330
331 # Button for separating single atoms from a dupliverts structure
332 class SeparateAtom(Operator):
333     bl_idname = "atom_blend.separate_atom"
334     bl_label = "Separate"
335     bl_description = ("Separate selected atoms in a dupliverts structure. "
336                       "You have to be in the 'Edit Mode'")
337
338     def execute(self, context):
339         scn = bpy.context.scene.atom_blend
340
341         io_atomblend_utilities.separate_atoms(scn)
342
343         return {'FINISHED'}
344
345
346 # Button for measuring the distance of active objects
347 class DistanceButton(Operator):
348     bl_idname = "atom_blend.button_distance"
349     bl_label = "Measure ..."
350     bl_description = "Measure the distance between two atoms (objects)."
351
352     def execute(self, context):
353         scn  = bpy.context.scene.atom_blend
354         dist = io_atomblend_utilities.distance()
355
356         # Put the distance into the string of the output field.
357         scn.distance = dist
358         return {'FINISHED'}
359
360
361 # Button for increasing the radii of all selected atoms
362 class RadiusAllBiggerButton(Operator):
363     bl_idname = "atom_blend.radius_all_bigger"
364     bl_label = "Bigger ..."
365     bl_description = "Increase the radii of selected atoms"
366
367     def execute(self, context):
368         scn = bpy.context.scene.atom_blend     
369         io_atomblend_utilities.choose_objects("ATOM_RADIUS_ALL", 
370                                               scn.obj_who, 
371                                               scn.radius_all, 
372                                               None,
373                                               None,
374                                               None,
375                                               None)        
376         return {'FINISHED'}
377
378
379 # Button for decreasing the radii of all selected atoms
380 class RadiusAllSmallerButton(Operator):
381     bl_idname = "atom_blend.radius_all_smaller"
382     bl_label = "Smaller ..."
383     bl_description = "Decrease the radii of selected atoms"
384
385     def execute(self, context):
386         scn = bpy.context.scene.atom_blend
387         io_atomblend_utilities.choose_objects("ATOM_RADIUS_ALL", 
388                                               scn.obj_who, 
389                                               1.0/scn.radius_all, 
390                                               None,
391                                               None,
392                                               None,
393                                               None)                                     
394         return {'FINISHED'}
395
396
397 # Button for increasing the radii of all selected sticks
398 class SticksAllBiggerButton(Operator):
399     bl_idname = "atom_blend.sticks_all_bigger"
400     bl_label = "Bigger ..."
401     bl_description = "Increase the radii of selected sticks"
402
403     def execute(self, context):
404         scn = bpy.context.scene.atom_blend     
405         io_atomblend_utilities.choose_objects("STICKS_RADIUS_ALL", 
406                                               scn.obj_who, 
407                                               None, 
408                                               None,
409                                               None,
410                                               None,
411                                               scn.sticks_all)        
412         return {'FINISHED'}
413
414
415 # Button for decreasing the radii of all selected sticks
416 class SticksAllSmallerButton(Operator):
417     bl_idname = "atom_blend.sticks_all_smaller"
418     bl_label = "Smaller ..."
419     bl_description = "Decrease the radii of selected sticks"
420
421     def execute(self, context):
422         scn = bpy.context.scene.atom_blend
423         io_atomblend_utilities.choose_objects("STICKS_RADIUS_ALL", 
424                                               scn.obj_who, 
425                                               None, 
426                                               None,
427                                               None,
428                                               None,
429                                               1.0/scn.sticks_all)                                     
430         return {'FINISHED'}
431
432
433 def register():
434     io_atomblend_utilities.read_elements()  
435     bpy.utils.register_module(__name__)
436     bpy.types.Scene.atom_blend = bpy.props.PointerProperty(type=PanelProperties)
437
438 def unregister():
439     bpy.utils.unregister_module(__name__)
440
441
442 if __name__ == "__main__":
443
444     register()