Removed home-grown Profiling class, cProfile does a much better job.
[blender-addons-contrib.git] / mesh_easylattice.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 bl_info = {
21     "name": "Easy Lattice Object",
22     "author": "Kursad Karatas",
23     "version": (0, 5),
24     "blender": (2, 66, 0),
25     "location": "View3D > Easy Lattice",
26     "description": "Create a lattice for shape editing",
27     "warning": "",
28     "wiki_url": "http://wiki.blender.org/index.php/Easy_Lattice_Editing_Addon",
29     "tracker_url": "https://bitbucket.org/kursad/blender_addons_easylattice/src",
30     "category": "Mesh"}
31
32
33 import bpy
34 import mathutils
35 import math
36
37 # Cleanup
38 def modifiersDelete( obj ):
39
40     for mod in obj.modifiers:
41         print(mod)
42         if mod.name == "latticeeasytemp":
43             try:
44                 if mod.object == bpy.data.objects['LatticeEasytTemp']:
45                     print("applying modifier")
46                     bpy.ops.object.modifier_apply( apply_as = 'DATA', modifier = mod.name )
47
48             except:
49                 bpy.ops.object.modifier_remove( modifier = mod.name )
50
51 # Cleanup
52 def modifiersApplyRemove( obj ):
53
54 #     print("passed object is", obj)
55 #     print("current object is", bpy.context.active_object)
56
57     bpy.ops.object.select_all( action = 'DESELECT' )
58     bpy.ops.object.select_pattern(pattern=obj.name, extend=False)
59     bpy.context.scene.objects.active=obj
60
61     for mod in obj.modifiers:
62 #         print("modifier is ", mod)
63         if mod.name == "latticeeasytemp":
64 #             try:
65             if mod.object == bpy.data.objects['LatticeEasytTemp']:
66 #                 print("mod object is ", mod.object)
67 #                 print("applying modifier", mod," - ", mod.name)
68
69                 #obj.select= True
70 #                 print("current object is", bpy.context.active_object)
71                 bpy.ops.object.modifier_apply( apply_as = 'DATA', modifier = mod.name )
72                 #obj.modifiers.remove(mod)
73
74 #             except:
75 #                 bpy.ops.object.modifier_remove( modifier = mod.name )
76
77
78 # Cleanup
79 def latticeDelete(obj):
80     bpy.ops.object.select_all( action = 'DESELECT' )
81     for ob in bpy.context.scene.objects:
82          if "LatticeEasytTemp" in ob.name:
83              ob.select = True
84     bpy.ops.object.delete( use_global = False )
85
86     #select the original object back
87     obj.select=True
88
89 def createLattice( obj, size, pos, props ):
90     # Create lattice and object
91     lat = bpy.data.lattices.new( 'LatticeEasytTemp' )
92     ob = bpy.data.objects.new( 'LatticeEasytTemp', lat )
93
94     loc,rot,scl = getTransformations( obj )
95
96     #get the combined rotation matrix and apply to the lattice
97     #ob.matrix_world=buildRot_WorldMat(obj)*ob.matrix_world
98
99     #the position comes from the bbox
100     ob.location = pos
101         #ob.location=(pos.x+loc.x,pos.y+loc.y,pos.z+loc.z)
102
103     #the size  from bbox
104     ob.scale = size
105         #ob.scale=(size.x*scl.x, size.y*scl.y,size.z*scl.z)
106
107     #the rotation comes from the combined obj world matrix which was converted to euler pairs.
108     ob.rotation_euler = buildRot_World(obj)
109
110     ob.show_x_ray = True
111     # Link object to scene
112     scn = bpy.context.scene
113     scn.objects.link( ob )
114     scn.objects.active = ob
115     scn.update()
116
117     # Set lattice attributes
118     lat.interpolation_type_u = props[3]
119     lat.interpolation_type_v = props[3]
120     lat.interpolation_type_w = props[3]
121
122     lat.use_outside = False
123
124     lat.points_u = props[0]
125     lat.points_v = props[1]
126     lat.points_w = props[2]
127
128     #Set lattice points
129     '''s = 0.0
130     points = [
131         (-s,-s,-s), (s,-s,-s), (-s,s,-s), (s,s,-s),
132         (-s,-s,s), (s,-s,s), (-s,s,s), (s,s,s)
133     ]
134     for n,pt in enumerate(lat.points):
135         for k in range(3):
136             #pt.co[k] = points[n][k]
137     '''
138
139     return ob
140
141
142 def selectedVerts_Grp( obj ):
143 #     vertices=bpy.context.active_object.data.vertices
144     vertices = obj.data.vertices
145
146     selverts = []
147
148     if obj.mode == "EDIT":
149         bpy.ops.object.editmode_toggle()
150
151     for grp in obj.vertex_groups:
152
153         if "templatticegrp" in grp.name:
154             bpy.ops.object.vertex_group_set_active( group = grp.name )
155             bpy.ops.object.vertex_group_remove()
156
157     tempgroup = obj.vertex_groups.new( "templatticegrp" )
158
159     # selverts=[vert for vert in vertices if vert.select==True]
160     for vert in vertices:
161         if vert.select == True:
162             selverts.append( vert )
163             tempgroup.add( [vert.index], 1.0, "REPLACE" )
164
165     # print(selverts)
166
167     return selverts
168
169 def getTransformations( obj ):
170     rot = obj.rotation_euler
171     loc = obj.location
172     size = obj.scale
173
174     return [loc, rot, size]
175
176 def findBBox( obj, selvertsarray ):
177
178 #     mat = buildTrnSclMat( obj )
179     mat =buildTrnScl_WorldMat(obj)
180
181     mat_world = obj.matrix_world
182
183     minx, miny, minz = selvertsarray[0].co
184     maxx, maxy, maxz = selvertsarray[0].co
185
186     c = 1
187 #     for vert in selvertsarray:
188     for c in range( len( selvertsarray ) ):
189         # co=obj.matrix_world*vert.co.to_4d()
190
191 #         co = vert.co
192         co = selvertsarray[c].co
193
194         if co.x < minx: minx = co.x
195         if co.y < miny: miny = co.y
196         if co.z < minz: minz = co.z
197
198         if co.x > maxx: maxx = co.x
199         if co.y > maxy: maxy = co.y
200         if co.z > maxz: maxz = co.z
201
202 #         print("local cord", selvertsarray[c].co)
203 #         print("world cord", co)
204         c += 1
205
206 #     print("total verts", len(selvertsarray))
207 #     print("counted verts",c)
208
209     # Based on world coords
210 #     print("-> minx miny minz",minx, miny, minz )
211 #     print("-> maxx maxy maxz",maxx, maxy, maxz )
212
213     minpoint = mathutils.Vector( ( minx, miny, minz ) )
214     maxpoint = mathutils.Vector( ( maxx, maxy, maxz ) )
215
216     # middle point has to be calculated based on the real world matrix
217     #middle = mat_world * mathutils.Vector((x_sum, y_sum, z_sum))/float(c)
218     middle = ( ( minpoint + maxpoint ) / 2 )
219
220     minpoint = mat * minpoint  # Calculate only based on loc/scale
221     maxpoint = mat * maxpoint  # Calculate only based on loc/scale
222     middle = mat_world * middle  # the middle has to be calculated based on the real world matrix
223
224     size = maxpoint - minpoint
225     size = mathutils.Vector( ( abs( size.x ), abs( size.y ), abs( size.z ) ) )
226
227     # local coords
228     #####################################################
229     '''minpoint=mathutils.Vector((minx,miny,minz))
230     maxpoint=mathutils.Vector((maxx,maxy,maxz))
231     middle=mathutils.Vector( (x_sum/float(len(selvertsarray)), y_sum/float(len(selvertsarray)), z_sum/float(len(selvertsarray))) )
232     size=maxpoint-minpoint
233     size=mathutils.Vector((abs(size.x),abs(size.y),abs(size.z)))
234     '''
235     #####################################################
236
237
238     return [minpoint, maxpoint, size, middle  ]
239
240
241 def buildTrnSclMat( obj ):
242     # This function builds a local matrix that encodes translation and scale and it leaves out the rotation matrix
243     # The rotation is applied at obejct level if there is any
244     mat_trans = mathutils.Matrix.Translation( obj.location )
245     mat_scale = mathutils.Matrix.Scale( obj.scale[0], 4, ( 1, 0, 0 ) )
246     mat_scale *= mathutils.Matrix.Scale( obj.scale[1], 4, ( 0, 1, 0 ) )
247     mat_scale *= mathutils.Matrix.Scale( obj.scale[2], 4, ( 0, 0, 1 ) )
248
249     mat_final = mat_trans * mat_scale
250
251
252     return mat_final
253
254 def buildTrnScl_WorldMat( obj ):
255     # This function builds a real world matrix that encodes translation and scale and it leaves out the rotation matrix
256     # The rotation is applied at obejct level if there is any
257     loc,rot,scl=obj.matrix_world.decompose()
258     mat_trans = mathutils.Matrix.Translation( loc)
259
260     mat_scale = mathutils.Matrix.Scale( scl[0], 4, ( 1, 0, 0 ) )
261     mat_scale *= mathutils.Matrix.Scale( scl[1], 4, ( 0, 1, 0 ) )
262     mat_scale *= mathutils.Matrix.Scale( scl[2], 4, ( 0, 0, 1 ) )
263
264     mat_final = mat_trans * mat_scale
265
266
267     return mat_final
268
269 #Feature use
270 def buildRot_WorldMat( obj ):
271     # This function builds a real world matrix that encodes rotation and it leaves out translation and scale matrices
272     loc,rot,scl=obj.matrix_world.decompose()
273     rot=rot.to_euler()
274
275     mat_rot = mathutils.Matrix.Rotation(rot[0], 4,'X')
276     mat_rot *= mathutils.Matrix.Rotation(rot[1],4,'Z')
277     mat_rot *= mathutils.Matrix.Rotation(rot[2], 4,'Y')
278     return mat_rot
279
280 #Feature use
281 def buildTrn_WorldMat( obj ):
282     # This function builds a real world matrix that encodes translation and scale and it leaves out the rotation matrix
283     # The rotation is applied at obejct level if there is any
284     loc,rot,scl=obj.matrix_world.decompose()
285     mat_trans = mathutils.Matrix.Translation( loc)
286
287     return mat_trans
288
289 #Feature use
290 def buildScl_WorldMat( obj ):
291     # This function builds a real world matrix that encodes translation and scale and it leaves out the rotation matrix
292     # The rotation is applied at obejct level if there is any
293     loc,rot,scl=obj.matrix_world.decompose()
294
295     mat_scale = mathutils.Matrix.Scale( scl[0], 4, ( 1, 0, 0 ) )
296     mat_scale *= mathutils.Matrix.Scale( scl[1], 4, ( 0, 1, 0 ) )
297     mat_scale *= mathutils.Matrix.Scale( scl[2], 4, ( 0, 0, 1 ) )
298
299     return mat_scale
300
301 def buildRot_World( obj ):
302     # This function builds a real world rotation values
303     loc,rot,scl=obj.matrix_world.decompose()
304     rot=rot.to_euler()
305
306     return rot
307
308 def run( lat_props ):
309
310 #     print("<-------------------------------->")
311     #obj = bpy.context.active_object
312     obj = bpy.context.object
313
314     if obj.type == "MESH":
315         # set global property for the currently active latticed object
316         bpy.types.Scene.activelatticeobject = bpy.props.StringProperty( name = "currentlatticeobject", default = "" )
317         bpy.types.Scene.activelatticeobject = obj.name
318
319         modifiersDelete( obj )
320         selvertsarray = selectedVerts_Grp( obj )
321         bbox = findBBox( obj, selvertsarray )
322
323         size = bbox[2]
324         pos = bbox[3]
325
326 #         print("lattce size, pos", size, " ", pos)
327         latticeDelete(obj)
328         lat = createLattice( obj, size, pos, lat_props )
329
330         modif = obj.modifiers.new( "latticeeasytemp", "LATTICE" )
331         modif.object = lat
332         modif.vertex_group = "templatticegrp"
333
334
335         bpy.ops.object.select_all( action = 'DESELECT' )
336         bpy.ops.object.select_pattern(pattern=lat.name, extend=False)
337         bpy.context.scene.objects.active=lat
338
339         bpy.context.scene.update()
340         bpy.ops.object.mode_set( mode = 'EDIT' )
341
342     if obj.type == "LATTICE":
343
344
345         if bpy.types.Scene.activelatticeobject:
346             name = bpy.types.Scene.activelatticeobject
347             print("last active latticed object", name)
348
349             #Are we in edit lattice mode? If so move on to object mode
350             if obj.mode=="EDIT":
351                 bpy.ops.object.editmode_toggle()
352
353             for ob in bpy.context.scene.objects:
354                 if ob.name == name:  # found the object with the lattice mod
355                     print("apply mod on", ob)
356                     object = ob
357                     modifiersApplyRemove(object)
358                     #modifiersDelete( object )  # apply the modifier and delete the lattice
359                     latticeDelete(obj)
360
361     return
362
363
364 def main( context, latticeprops ):
365     run( latticeprops )
366
367 class EasyLattice( bpy.types.Operator ):
368     """Tooltip"""
369     bl_idname = "object.easy_lattice"
370     bl_label = "Easy Lattice Creator"
371     bl_space_type = "VIEW_3D"
372     bl_region_type = "TOOLS"
373
374     lat_u = bpy.props.IntProperty( name = "Lattice u", default = 3 )
375     lat_w = bpy.props.IntProperty( name = "Lattice w", default = 3 )
376     lat_m = bpy.props.IntProperty( name = "Lattice m", default = 3 )
377
378     lat_types = ( ( '0', 'KEY_LINEAR', '0' ), ( '1', 'KEY_CARDINAL', '1' ), ( '2', 'KEY_BSPLINE', '2' ) )
379     lat_type = bpy.props.EnumProperty( name = "Lattice Type", items = lat_types, default = '0' )
380
381
382     @classmethod
383     def poll( cls, context ):
384         return context.active_object is not None
385
386     def execute( self, context ):
387
388         lat_u = self.lat_u
389         lat_w = self.lat_w
390         lat_m = self.lat_m
391
392         # this is a reference to the "items" used to generate the
393         # enum property.
394         lat_type = self.lat_types[int( self.lat_type )][1]
395         lat_props = [lat_u, lat_w, lat_m, lat_type]
396
397         main( context, lat_props )
398         return {'FINISHED'}
399
400     def invoke( self, context, event ):
401         wm = context.window_manager
402         return wm.invoke_props_dialog( self )
403
404 def menu_draw( self, context ):
405     self.layout.operator_context = 'INVOKE_REGION_WIN'
406     self.layout.operator( EasyLattice.bl_idname, "Easy Lattice" )
407
408 def register():
409     bpy.utils.register_class( EasyLattice )
410     # bpy.utils.register
411     # menu_func = (lambda self, context: self.layout.operator('EasyLattice'))
412     # bpy.types.VIEW3D_PT_tools_objectmode.append(menu_draw)
413     bpy.types.VIEW3D_MT_edit_mesh_specials.append( menu_draw )
414
415
416 def unregister():
417     bpy.utils.unregister_class( EasyLattice )
418     # bpy.types.VIEW3D_PT_tools_objectmode.remove(menu_draw)
419     bpy.types.VIEW3D_MT_edit_mesh_specials.remove( menu_draw )
420
421 if __name__ == "__main__":
422     register()
423     # run()
424 #     bpy.ops.object.easy_lattice()
425
426
427
428