rename addon, no spaces please.
[blender-addons-contrib.git] / ewoc_projects_tools / mesh_selproject.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 __bpydoc__ = """\
20 The SelProject addon enables you to "project" an object onto another object, every vertex inside the 
21 projection's shape will be selected.
22
23
24 Documentation
25
26 First go to User Preferences->Addons and enable the SelProject addon in the Mesh category.
27 It will appear in the Tools panel.  First set From and To object from the dropdown list.  From object
28 is the object you project, To object the one you project on.  If switching to editmode first,
29 the "Use Selection" option appears.  When choosing this you will use a copy of the selected area
30 instead of a From object.
31 Press Start SelProject to start the projection.  When in Use Selection mode, the object selected from
32 will be temporarily hidden for the duration of the operation.  You can use manipulator and 
33 G, R and S (and XYZ) hotkeys as usual to transform both objects.  Also there is the direction Empty 
34 which is used in combination with the origin of the From object (which will be temporarily set to
35 object geometry median) to set the projection direction.
36 Press ENTER to finalize the selection projection operation.
37 """
38
39
40 bl_info = {
41         "name": "SelProject",
42         "author": "Gert De Roost",
43         "version": (0, 2, 8),
44         "blender": (2, 6, 3),
45         "location": "View3D > Tools",
46         "description": "Use object projection as selection tool.",
47         "warning": "",
48         "wiki_url": "",
49         "tracker_url": "",
50         "category": "Mesh"}
51
52 if "bpy" in locals():
53     import imp
54
55
56 import bpy
57 from bpy_extras import *
58 import bmesh
59 from bgl import *
60 from mathutils import *
61 import math
62
63
64 activated = 0
65 redomenus = 1
66 started = 0
67 cont = 0
68 oldobjs = None
69 selverts = []
70 fromobj = None
71 oldfromobj = None
72
73
74
75 bpy.types.Scene.UseSel = bpy.props.BoolProperty(
76                 name = "Use Selection", 
77                 description = "Use selected area as From object",
78                 default = False)
79
80 itemlist = [("Empty", "Empty", "Empty")]
81 bpy.types.Scene.FromObject = bpy.props.EnumProperty(
82                 items = itemlist,
83                 name = "From", 
84                 description = "Object to project",
85                 default = "Empty")
86
87 bpy.types.Scene.ToObject = bpy.props.EnumProperty(
88                 items = itemlist,
89                 name = "To", 
90                 description = "Object to project onto")
91
92
93
94
95
96 class Activate(bpy.types.Operator):
97         bl_idname = "selproject.activate"
98         bl_label = "Activate"
99         bl_description = "Activate addon"
100         bl_options = {"REGISTER", "UNDO"}
101         
102         def invoke(self, context, event):
103                 
104                 global activated
105                 
106                 setparam()
107                 activated = 1
108         
109                 return {'RUNNING_MODAL'}
110                 
111
112 class SelProject(bpy.types.Operator):
113         bl_idname = "mesh.selproject"
114         bl_label = "SelProject"
115         bl_description = "Use object projection as selection tool"
116         bl_options = {"REGISTER", "UNDO"}
117         
118         def invoke(self, context, event):
119                 
120                 self.save_global_undo = bpy.context.user_preferences.edit.use_global_undo
121                 bpy.context.user_preferences.edit.use_global_undo = False
122                 
123                 do_selproject()
124                 
125                 context.window_manager.modal_handler_add(self)
126                 if eval(str(bpy.app.build_revision)[2:7]) >= 53207:
127                         self._handle = bpy.types.SpaceView3D.draw_handler_add(redraw, (), 'WINDOW', 'POST_PIXEL')
128                         self._handle3 = bpy.types.SpaceView3D.draw_handler_add(setparam, (), 'WINDOW', 'POST_PIXEL')
129                 else:
130                         self._handle = context.region.callback_add(redraw, (), 'POST_PIXEL')
131                         self._handle3 = context.region.callback_add(setparam, (), 'POST_PIXEL')
132                 
133                 
134                 return {'RUNNING_MODAL'}
135                 
136         def modal(self, context, event):
137
138                 global obF, obT, bmF, bmT, meF, meT, matrixF, matrixT
139                 global oldlocF, oldrotF, oldscaF, oldlocT, oldrotT, oldscaT
140                 global started, bigxmin, bigymin
141                                 
142                 if event.type == "RET":
143                         if obhide != None:
144                                 bpy.ops.object.select_all(action="DESELECT")
145                                 obF.select = 1
146                                 bpy.context.scene.objects.active = obF
147                                 bpy.ops.object.delete()
148                                 obhide.hide = 0
149                         bpy.ops.object.select_all(action="DESELECT")
150                         empt.select = 1
151                         bpy.context.scene.objects.active = empt
152                         bpy.ops.object.delete()
153                         obT.select = 1
154                         bpy.context.scene.objects.active = obT                          
155                         started = 0
156                         for v in vsellist:
157                                 v.select = 1
158                         for e in esellist:
159                                 e.select = 1
160                         for f in fsellist:
161                                 f.select = 1
162                         obF.location = originobF
163                         obT.location = originobT
164                         bmT.select_flush(1)
165                         bmT.to_mesh(meT)
166                         meT.update()
167                         bmF.free()
168                         bmT.free()
169                         if eval(str(bpy.app.build_revision)[2:7]) >= 53207:
170                                 bpy.types.SpaceView3D.draw_handler_remove(self._handle, "WINDOW")
171                                 bpy.types.SpaceView3D.draw_handler_remove(self._handle3, "WINDOW")
172                         else:
173                                 context.region.callback_remove(self._handle)
174                                 context.region.callback_remove(self._handle3)
175                         bpy.context.user_preferences.edit.use_global_undo = self.save_global_undo
176                         bpy.ops.object.editmode_toggle()
177                         return {'FINISHED'}
178                 elif event.type in ["LEFTMOUSE", "MIDDLEMOUSE", "RIGHTMOUSE", "WHEELDOWNMOUSE", "WHEELUPMOUSE", "G", "S", "R", "X", "Y", "Z", "MOUSEMOVE"]:
179                         context.region.tag_redraw()
180                         return {"PASS_THROUGH"}
181
182                 
183                 return {'RUNNING_MODAL'}
184
185
186 def panel_func(self, context):
187         
188         global shrink, started, oldfromobj, fromobj, redomenus
189         
190         scn = bpy.context.scene
191         
192         self.layout.label(text="SelProject:")
193         if not(activated):
194                 self.layout.operator("selproject.activate", text="Activate SelProject")
195         else:
196                 if not(started):
197                         self.layout.operator("mesh.selproject", text="Start SelProject")
198                         if context.mode == "EDIT_MESH":
199                                 self.layout.prop(scn, "UseSel")
200                                 if not(scn.UseSel):
201                                         self.layout.prop(scn, "FromObject")
202                                 else:
203                                         fromobj = bpy.context.active_object.name
204                                         redomenus = 1
205                                         context.region.tag_redraw()
206                         else:
207                                 self.layout.prop(scn, "FromObject")
208                         self.layout.prop(scn, "ToObject")
209                 else:
210                         self.layout.label(text="ENTER to confirm")
211         
212                 if scn.FromObject != oldfromobj:
213                         oldfromobj = scn.FromObject
214                         redomenus = 1
215                         context.region.tag_redraw()
216                         
217                 redomenus = 1
218
219
220
221 def register():
222         bpy.utils.register_module(__name__)
223         bpy.types.VIEW3D_PT_tools_meshedit.append(panel_func)
224         bpy.types.VIEW3D_PT_tools_objectmode.append(panel_func)
225
226
227 def unregister():
228         bpy.utils.unregister_module(__name__)
229         bpy.types.VIEW3D_PT_tools_meshedit.remove(panel_func)
230         bpy.types.VIEW3D_PT_tools_objectmode.append(panel_func)
231
232
233 if __name__ == "__main__":
234         register()
235
236
237
238
239
240
241
242 def adapt(selobj):
243         
244         # Rotating / panning / zooming 3D view is handled here.
245         # Creates a matrix.
246         if selobj.rotation_mode == "AXIS_ANGLE":
247                 # object rotation_quaternionmode axisangle
248                 ang, x, y, z =  selobj.rotation_axis_angle
249                 matrix = Matrix.Rotation(-ang, 4, Vector((x, y, z)))
250         elif selobj.rotation_mode == "QUATERNION":
251                 # object rotation_quaternionmode euler
252                 w, x, y, z = selobj.rotation_quaternion
253                 x = -x
254                 y = -y
255                 z = -z
256                 quat = Quaternion([w, x, y, z])
257                 matrix = quat.to_matrix()
258                 matrix.resize_4x4()
259         else:
260                 # object rotation_quaternionmode euler
261                 ax, ay, az = selobj.rotation_euler
262                 mat_rotX = Matrix.Rotation(-ax, 4, 'X')
263                 mat_rotY = Matrix.Rotation(-ay, 4, 'Y')
264                 mat_rotZ = Matrix.Rotation(-az, 4, 'Z')
265         if selobj.rotation_mode == "XYZ":
266                 matrix = mat_rotX * mat_rotY * mat_rotZ
267         elif selobj.rotation_mode == "XZY":
268                 matrix = mat_rotX * mat_rotZ * mat_rotY
269         elif selobj.rotation_mode == "YXZ":
270                 matrix = mat_rotY * mat_rotX * mat_rotZ
271         elif selobj.rotation_mode == "YZX":
272                 matrix = mat_rotY * mat_rotZ * mat_rotX
273         elif selobj.rotation_mode == "ZXY":
274                 matrix = mat_rotZ * mat_rotX * mat_rotY
275         elif selobj.rotation_mode == "ZYX":
276                 matrix = mat_rotZ * mat_rotY * mat_rotX
277
278         # handle object scaling
279         sx, sy, sz = selobj.scale
280         mat_scX = Matrix.Scale(sx, 4, Vector([1, 0, 0]))
281         mat_scY = Matrix.Scale(sy, 4, Vector([0, 1, 0]))
282         mat_scZ = Matrix.Scale(sz, 4, Vector([0, 0, 1]))
283         matrix = mat_scX * mat_scY * mat_scZ * matrix
284         
285         return matrix
286
287
288 def getscreencoords(vector):
289         # calculate screencoords of given Vector
290         region = bpy.context.region
291         rv3d = bpy.context.space_data.region_3d 
292         pvector = vector * matrixT
293         pvector = pvector + obT.location
294         
295         svector = view3d_utils.location_3d_to_region_2d(region, rv3d, pvector)
296         if svector == None:
297                 return [0, 0]
298         else:
299                 return [svector[0], svector[1]]
300
301
302
303
304
305
306 def checksel():
307         
308         global selverts, started, matrixT
309                 
310         selverts = []
311         matrixT = adapt(obT)
312         matrixF = adapt(obF).inverted()
313         direc =  (obF.location - empt.location) * matrixF
314         for v in bmT.verts:
315                 vno = v.normal
316                 vno.length = 0.0001
317                 vco = v.co + vno
318                 hit = obT.ray_cast(vco, vco + direc)
319                 if hit[2] == -1:
320                         vco = ((v.co * matrixT + obT.location) - obF.location) * matrixF
321                         vco2 = vco.copy()
322                         vco2 += direc * 10000
323                         hit = obF.ray_cast(vco, vco2)
324                         if hit[2] != -1:
325                                 v.select = 1
326                                 selverts.append(v)      
327
328
329
330
331
332 def do_selproject():
333
334         global obF, obT, bmF, bmT, meF, meT, matrixF, matrixT, empt
335         global quat
336         global started, obhide, originobF, originobT
337         global oldlocF, oldrotF, oldscaF, oldlocT, oldrotT, oldscaT
338         global vsellist, esellist, fsellist, selverts
339
340         obhide = None
341         # main operation
342         context = bpy.context
343 #       context.region.callback_remove(_handle3)
344         region = context.region  
345         selobj = bpy.context.active_object
346         mesh = selobj.data
347         scn = bpy.context.scene
348         
349         if scn.UseSel and context.mode == "EDIT_MESH":
350                 obhide = context.active_object
351                 me = obhide.data
352                 bmundo = bmesh.new()
353                 bmundo.from_mesh(me)
354                 objlist = []
355                 for obj in scn.objects:
356                         objlist.append(obj)
357                 bpy.ops.mesh.separate(type='SELECTED')
358                 for obj in scn.objects:
359                         if not(obj in objlist):
360                                 obF = obj
361                 bmundo.to_mesh(me)
362                 bmundo.free()
363                 obhide.hide = 1
364         else:
365                 obF = bpy.data.objects.get(scn.FromObject)
366         if context.mode == "EDIT_MESH":
367                 bpy.ops.object.editmode_toggle()
368         obF.select = 1
369         scn.objects.active = obF
370         originobF = obF.location
371         bpy.ops.object.origin_set(type="ORIGIN_GEOMETRY")
372         meF = obF.to_mesh(scn, 1, "PREVIEW")
373         bmF = bmesh.new()
374         bmF.from_mesh(meF)
375         
376         obT = bpy.data.objects.get(scn.ToObject)
377         obT.select = 1
378         scn.objects.active = obT
379         originobT = obT.location
380         bpy.ops.object.origin_set(type="ORIGIN_GEOMETRY")
381         meT = obT.data
382         bmT = bmesh.new()
383         bmT.from_mesh(meT)
384                 
385         vsellist = []
386         for v in bmT.verts:
387                 if v.select:
388                         vsellist.append(v)
389         esellist = []
390         for e in bmT.edges:
391                 if e.select:
392                         esellist.append(e)
393         fsellist = []
394         for f in bmT.faces:
395                 if f.select:
396                         fsellist.append(f)
397                         
398         bpy.ops.object.add(type='EMPTY', location=(obF.location + obT.location) / 2)
399         empt = context.active_object
400         empt.name = "SelProject_dir_empty"
401
402         started = 1
403         selverts = []
404
405
406 def redraw():
407         
408         global matrixT
409         
410         if started:
411                 checksel()
412                 glColor3f(1.0,1.0,0)
413                 for v in selverts:
414                         glBegin(GL_POLYGON)
415                         x, y = getscreencoords(v.co)
416                         glVertex2f(x-2, y-2)
417                         glVertex2f(x-2, y+2)
418                         glVertex2f(x+2, y+2)
419                         glVertex2f(x+2, y-2)
420                         glEnd()
421
422
423
424 def setparam():
425         
426         global axoff, fromobj, redomenus
427
428         if redomenus:
429                 redomenus = 0
430                 scn = bpy.context.scene
431                 
432                 if fromobj != None and fromobj != "":
433                         scn.FromObject = fromobj
434                         fromobj = None
435                 
436                 itemlist = []
437                 
438                 scn.update()
439                 objs = bpy.context.scene.objects
440                 for ob in objs:
441                         if ob.type == "MESH":
442                                 itemlist.append((ob.name, ob.name, "Set From:"))
443                 bpy.types.Scene.FromObject = bpy.props.EnumProperty(
444                                 items = itemlist,
445                                 name = "From", 
446                                 description = "Object to project")
447                 if itemlist != []:
448                         itemlist.pop(itemlist.index((scn.FromObject, scn.FromObject, "Set From:")))
449                 bpy.types.Scene.ToObject = bpy.props.EnumProperty(
450                                 items = itemlist,
451                                 name = "To", 
452                                 description = "Object to project onto")
453         
454
455