Fix for bl_info blender versions, many addons used (2, 6, x) instead of (2, 6x, x...
[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, 3, 0),
44         "blender": (2, 63, 0),
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
53
54 import bpy
55 import bpy_extras
56 import bmesh
57 from bgl import glColor3f, glBegin, GL_QUADS, glVertex2f, glEnd
58 from mathutils import Vector, Matrix
59 import math
60 from bpy.app.handlers import persistent
61
62
63 started = False
64 oldobjs = []
65
66
67
68 bpy.types.Scene.UseSel = bpy.props.BoolProperty(
69                 name = "Use Selection",
70                 description = "Use selected area as From object",
71                 default = False)
72
73 bpy.types.Scene.FromObject = bpy.props.EnumProperty(
74                 items = [("Empty", "Empty", "Empty")],
75                 name = "From",
76                 description = "Object to project",
77                 default = "Empty")
78
79 bpy.types.Scene.ToObject = bpy.props.EnumProperty(
80                 items = [("Empty", "Empty", "Empty")],
81                 name = "To",
82                 description = "Object to project onto")
83
84
85
86
87
88 class SelProject(bpy.types.Operator):
89         bl_idname = "mesh.selproject"
90         bl_label = "SelProject"
91         bl_description = "Use object projection as selection tool"
92         bl_options = {'REGISTER', 'UNDO'}
93
94         def invoke(self, context, event):
95
96                 global started
97
98                 started = True
99
100                 self.area = context.area
101                 self.area.header_text_set(text="SelProject :  Enter to confirm - ESC to exit")
102
103                 self.init_selproject(context)
104
105                 context.window_manager.modal_handler_add(self)
106
107                 self._handle = bpy.types.SpaceView3D.draw_handler_add(self.redraw, (), 'WINDOW', 'POST_PIXEL')
108
109                 return {'RUNNING_MODAL'}
110
111
112         def modal(self, context, event):
113
114                 global started
115
116                 if event.type in {'RET', 'NUMPAD_ENTER'}:
117                         self.area.header_text_set()
118                         if self.obhide != None:
119                                 bpy.ops.object.select_all(action = 'DESELECT')
120                                 self.obF.select = True
121                                 bpy.context.scene.objects.active = self.obF
122                                 bpy.ops.object.delete()
123                                 self.obhide.hide = False
124                         bpy.ops.object.select_all(action = 'DESELECT')
125                         self.empt.select = True
126                         bpy.context.scene.objects.active = self.empt
127                         bpy.ops.object.delete()
128                         self.obT.select = True
129                         bpy.context.scene.objects.active = self.obT
130                         started = False
131                         for v in self.vsellist:
132                                 v.select = True
133                         for e in self.esellist:
134                                 e.select = True
135                         for f in self.fsellist:
136                                 f.select = True
137                         self.obF.location = self.originobF
138                         self.obT.location = self.originobT
139                         self.bmT.select_flush(1)
140                         self.bmT.to_mesh(self.meT)
141                         self.meT.update()
142                         self.bmF.free()
143                         self.bmT.free()
144                         bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW')
145                         bpy.ops.object.editmode_toggle()
146                         return {'FINISHED'}
147
148                 elif event.type == 'ESC':
149                         self.area.header_text_set()
150                         if self.obhide != None:
151                                 bpy.ops.object.select_all(action = 'DESELECT')
152                                 self.obF.select = True
153                                 bpy.context.scene.objects.active = self.obF
154                                 bpy.ops.object.delete()
155                                 self.obhide.hide = False
156                         bpy.ops.object.select_all(action = 'DESELECT')
157                         self.empt.select = True
158                         bpy.context.scene.objects.active = self.empt
159                         bpy.ops.object.delete()
160                         started = False
161                         self.obF.location = self.originobF
162                         self.obT.location = self.originobT
163                         self.bmF.free()
164                         self.bmT.free()
165                         for obj in self.oldobjlist:
166                                 obj.select = True
167                         self.scn.objects.active = self.oldobj
168                         bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW')
169                         if self.oldmode == 'EDIT':
170                                 bpy.ops.object.editmode_toggle()
171                         return {'CANCELLED'}
172
173                 elif event.type in {'LEFTMOUSE', 'MIDDLEMOUSE', 'RIGHTMOUSE', 'WHEELDOWNMOUSE', 'WHEELUPMOUSE', 'G', 'S', 'R', 'X', 'Y', 'Z', 'MOUSEMOVE'}:
174                         context.region.tag_redraw()
175                         return {'PASS_THROUGH'}
176
177                 return {'RUNNING_MODAL'}
178
179
180         def getmatrix(self, selobj):
181
182                 # Rotating / panning / zooming 3D view is handled here.
183                 # Creates a matrix.
184                 if selobj.rotation_mode == 'AXIS_ANGLE':
185                         # object rotation_quaternionmode axisangle
186                         ang, x, y, z =  selobj.rotation_axis_angle
187                         matrix = Matrix.Rotation(-ang, 4, Vector((x, y, z)))
188                 elif selobj.rotation_mode == 'QUATERNION':
189                         # object rotation_quaternionmode euler
190                         w, x, y, z = selobj.rotation_quaternion
191                         x = -x
192                         y = -y
193                         z = -z
194                         self.quat = Quaternion([w, x, y, z])
195                         matrix = self.quat.to_matrix()
196                         matrix.resize_4x4()
197                 else:
198                         # object rotation_quaternionmode euler
199                         ax, ay, az = selobj.rotation_euler
200                         mat_rotX = Matrix.Rotation(-ax, 4, 'X')
201                         mat_rotY = Matrix.Rotation(-ay, 4, 'Y')
202                         mat_rotZ = Matrix.Rotation(-az, 4, 'Z')
203                 if selobj.rotation_mode == 'XYZ':
204                         matrix = mat_rotX * mat_rotY * mat_rotZ
205                 elif selobj.rotation_mode == 'XZY':
206                         matrix = mat_rotX * mat_rotZ * mat_rotY
207                 elif selobj.rotation_mode == 'YXZ':
208                         matrix = mat_rotY * mat_rotX * mat_rotZ
209                 elif selobj.rotation_mode == 'YZX':
210                         matrix = mat_rotY * mat_rotZ * mat_rotX
211                 elif selobj.rotation_mode == 'ZXY':
212                         matrix = mat_rotZ * mat_rotX * mat_rotY
213                 elif selobj.rotation_mode == 'ZYX':
214                         matrix = mat_rotZ * mat_rotY * mat_rotX
215
216                 # handle object scaling
217                 sx, sy, sz = selobj.scale
218                 mat_scX = Matrix.Scale(sx, 4, Vector([1, 0, 0]))
219                 mat_scY = Matrix.Scale(sy, 4, Vector([0, 1, 0]))
220                 mat_scZ = Matrix.Scale(sz, 4, Vector([0, 0, 1]))
221                 matrix = mat_scX * mat_scY * mat_scZ * matrix
222
223                 return matrix
224
225
226         def getscreencoords(self, vector):
227                 # calculate screencoords of given Vector
228                 vector = vector * self.matrixT
229                 vector = vector + self.obT.location
230
231                 svector = bpy_extras.view3d_utils.location_3d_to_region_2d(self.region, self.rv3d, vector)
232                 if svector == None:
233                         return [0, 0]
234                 else:
235                         return [svector[0], svector[1]]
236
237
238
239
240         def checksel(self):
241
242                 self.selverts = []
243                 self.matrixT = self.getmatrix(self.obT)
244                 self.matrixF = self.getmatrix(self.obF).inverted()
245                 direc1 =  (self.obF.location - self.empt.location) * self.matrixF
246                 direc2 =  (self.obF.location - self.empt.location) * self.matrixT.inverted()
247                 direc2.length = 10000
248                 for v in self.bmT.verts:
249                         vno1 = v.normal
250                         vno1.length = 0.0001
251                         vco1 = v.co + vno1
252                         hit1 = self.obT.ray_cast(vco1, vco1 + direc2)
253                         vno2 = -v.normal
254                         vno2.length = 0.0001
255                         vco2 = v.co + vno2
256                         hit2 = self.obT.ray_cast(vco2, vco2 + direc2)
257                         if hit1[2] == -1 or hit2[2] == -1:
258                                 vco = ((v.co * self.matrixT + self.obT.location) - self.obF.location) * self.matrixF
259                                 hit = self.obF.ray_cast(vco, vco + direc1)
260                                 if hit[2] != -1:
261                                         v.select = True
262                                         self.selverts.append(v)
263
264
265
266
267
268         def init_selproject(self, context):
269
270                 self.obhide = None
271                 # main operation
272                 self.scn = context.scene
273                 self.region = context.region
274                 self.rv3d = context.space_data.region_3d
275                 self.oldobjlist = list(self.scn.objects)
276                 self.oldobj = context.active_object
277                 self.oldmode = self.oldobj.mode
278                 mesh = self.oldobj.data
279
280                 if self.scn.UseSel and context.mode == 'EDIT_MESH':
281                         self.obhide = context.active_object
282                         me = self.obhide.data
283                         bmundo = bmesh.new()
284                         bmundo.from_mesh(me)
285                         objlist = []
286                         for obj in self.scn.objects:
287                                 objlist.append(obj)
288                         bpy.ops.mesh.separate(type = 'SELECTED')
289                         for obj in self.scn.objects:
290                                 if not(obj in objlist):
291                                         self.obF = obj
292                         bmundo.to_mesh(me)
293                         bmundo.free()
294                         self.obhide.hide = True
295                 else:
296                         self.obF = bpy.data.objects.get(self.scn.FromObject)
297                 if context.mode == 'EDIT_MESH':
298                         bpy.ops.object.editmode_toggle()
299                 self.obF.select = True
300                 self.scn.objects.active = self.obF
301                 self.originobF = self.obF.location
302                 bpy.ops.object.origin_set(type = 'ORIGIN_GEOMETRY')
303                 self.meF = self.obF.to_mesh(self.scn, 1, 'PREVIEW')
304                 self.bmF = bmesh.new()
305                 self.bmF.from_mesh(self.meF)
306
307                 self.obT = bpy.data.objects.get(self.scn.ToObject)
308                 self.obT.select = True
309                 self.scn.objects.active = self.obT
310                 self.originobT = self.obT.location
311                 bpy.ops.object.origin_set(type='ORIGIN_GEOMETRY')
312                 self.meT = self.obT.data
313                 self.bmT = bmesh.new()
314                 self.bmT.from_mesh(self.meT)
315
316                 self.vsellist = []
317                 for v in self.bmT.verts:
318                         if v.select:
319                                 self.vsellist.append(v)
320                 self.esellist = []
321                 for e in self.bmT.edges:
322                         if e.select:
323                                 self.esellist.append(e)
324                 self.fsellist = []
325                 for f in self.bmT.faces:
326                         if f.select:
327                                 self.fsellist.append(f)
328
329                 bpy.ops.object.add(type='EMPTY', location=(self.obF.location + self.obT.location) / 2)
330                 self.empt = context.active_object
331                 self.empt.name = "SelProject_dir_empty"
332
333                 self.selverts = []
334
335
336         def redraw(self):
337
338                 if started:
339                         self.checksel()
340                         glColor3f(1.0, 1.0, 0)
341                         for v in self.selverts:
342                                 glBegin(GL_QUADS)
343                                 x, y = self.getscreencoords(v.co)
344                                 glVertex2f(x-2, y-2)
345                                 glVertex2f(x-2, y+2)
346                                 glVertex2f(x+2, y+2)
347                                 glVertex2f(x+2, y-2)
348                                 glEnd()
349
350
351
352
353
354 def panel_func(self, context):
355
356         self.scn = context.scene
357
358         self.layout.label(text="SelProject:")
359         if started:
360                 self.layout.operator("mesh.selproject", text="SelProject")
361         else:
362                 self.layout.operator("mesh.selproject", text="SelProject")
363                 if context.mode == 'EDIT_MESH':
364                         self.layout.prop(self.scn, "UseSel")
365                         if not(self.scn.UseSel):
366                                 self.layout.prop(self.scn, "FromObject")
367                         else:
368                                 self.scn.FromObject = bpy.context.active_object.name
369                                 context.region.tag_redraw()
370                 else:
371                         self.layout.prop(self.scn, "FromObject")
372                 self.layout.prop(self.scn, "ToObject")
373
374
375
376
377 def register():
378
379         bpy.app.handlers.scene_update_post.append(sceneupdate_handler)
380
381         bpy.utils.register_module(__name__)
382         bpy.types.VIEW3D_PT_tools_meshedit.append(panel_func)
383         bpy.types.VIEW3D_PT_tools_objectmode.append(panel_func)
384
385
386 def unregister():
387         bpy.app.handlers.scene_update_post.remove(sceneupdate_handler)
388
389         bpy.utils.unregister_module(__name__)
390         bpy.types.VIEW3D_PT_tools_meshedit.remove(panel_func)
391         bpy.types.VIEW3D_PT_tools_objectmode.append(panel_func)
392
393
394 if __name__ == "__main__":
395         register()
396
397
398
399
400
401
402 @persistent
403 def sceneupdate_handler(dummy):
404
405         global oldobjs
406
407         scn = bpy.context.scene
408
409         if not(list(scn.objects) == oldobjs):
410                 itemlist = []
411                 objs = list(scn.objects)
412                 for ob in objs:
413                         if ob.type == 'MESH':
414                                 itemlist.append((ob.name, ob.name, "Set From:"))
415                 bpy.types.Scene.FromObject = bpy.props.EnumProperty(
416                                 items = itemlist,
417                                 name = "From",
418                                 description = "Object to project")
419                 bpy.types.Scene.ToObject = bpy.props.EnumProperty(
420                                 items = itemlist,
421                                 name = "To",
422                                 description = "Object to project onto")
423                 oldobjs = list(scn.objects)
424
425         return {'RUNNING_MODAL'}
426
427
428
429