Merge branch 'master' into blender2.8
[blender-addons.git] / mesh_extra_tools / pkhg_faces.py
blob08faf560b6f78e2d50dea3d6fe3e73f9a9d574f6
1 # gpl author: PHKG
3 bl_info = {
4 "name": "PKHG faces",
5 "author": "PKHG",
6 "version": (0, 0, 6),
7 "blender": (2, 7, 1),
8 "location": "View3D > Tools > PKHG (tab)",
9 "description": "Faces selected will become added faces of different style",
10 "warning": "",
11 "wiki_url": "",
12 "category": "Mesh",
15 import bpy
16 import bmesh
17 from bpy.types import Operator
18 from mathutils import Vector
19 from bpy.props import (
20 BoolProperty,
21 StringProperty,
22 IntProperty,
23 FloatProperty,
24 EnumProperty,
28 class MESH_OT_add_faces_to_object(Operator):
29 bl_idname = "mesh.add_faces_to_object"
30 bl_label = "Face Extrude"
31 bl_description = "Set parameters and build object with added faces"
32 bl_options = {'REGISTER', 'UNDO', 'PRESET'}
34 reverse_faces = BoolProperty(
35 name="Reverse Faces",
36 default=False,
37 description="Revert the normals of selected faces"
39 name_source_object = StringProperty(
40 name="Mesh",
41 description="Choose a Source Mesh",
42 default="Cube"
44 remove_start_faces = BoolProperty(
45 name="Remove Start Faces",
46 default=True,
47 description="Make a choice about removal of Original Faces"
49 base_height = FloatProperty(
50 name="Base Height",
51 min=-20,
52 soft_max=10, max=20,
53 default=0.2,
54 description="Set general Base Height"
56 use_relative_base_height = BoolProperty(
57 name="Relative Base Height",
58 default=False,
59 description="Relative or absolute Base Height"
61 second_height = FloatProperty(
62 name="2nd height", min=-5,
63 soft_max=5, max=20,
64 default=0.2,
65 description="Second height for various shapes"
67 width = FloatProperty(
68 name="Width Faces",
69 min=-20, max=20,
70 default=0.5,
71 description="Set general width"
73 repeat_extrude = IntProperty(
74 name="Repeat",
75 min=1,
76 soft_max=5, max=20,
77 description="For longer base"
79 move_inside = FloatProperty(
80 name="Move Inside",
81 min=0.0,
82 max=1.0,
83 default=0.5,
84 description="How much move to inside"
86 thickness = FloatProperty(
87 name="Thickness",
88 soft_min=0.01, min=0,
89 soft_max=5.0, max=20.0,
90 default=0
92 depth = FloatProperty(
93 name="Depth",
94 min=-5,
95 soft_max=5.0, max=20.0,
96 default=0
98 collapse_edges = BoolProperty(
99 name="Make Point",
100 default=False,
101 description="Collapse the vertices of edges"
103 spike_base_width = FloatProperty(
104 name="Spike Base Width",
105 default=0.4,
106 min=-4.0,
107 soft_max=1, max=20,
108 description="Base width of a spike"
110 base_height_inset = FloatProperty(
111 name="Base Height Inset",
112 default=0.0,
113 min=-5, max=5,
114 description="To elevate or drop the Base height Inset"
116 top_spike = FloatProperty(
117 name="Top Spike",
118 default=1.0,
119 min=-10.0, max=10.0,
120 description="The Base Height of a spike"
122 top_extra_height = FloatProperty(
123 name="Top Extra Height",
124 default=0.0,
125 min=-10.0, max=10.0,
126 description="Add extra height"
128 step_with_real_spike = BoolProperty(
129 name="Step with Real Spike",
130 default=False,
131 description="In stepped, use a real spike"
133 use_relative = BoolProperty(
134 name="Use Relative",
135 default=False,
136 description="Change size using area, min or max"
138 face_types = EnumProperty(
139 name="Face Types",
140 description="Different types of Faces",
141 default="no",
142 items=[
143 ('no', "Pick an Option", "Choose one of the available options"),
144 ('open_inset', "Open Inset", "Inset without closing faces (holes)"),
145 ('with_base', "With Base", "Base and ..."),
146 ('clsd_vertical', "Closed Vertical", "Closed Vertical"),
147 ('open_vertical', "Open Vertical", "Open Vertical"),
148 ('spiked', "Spiked", "Spike"),
149 ('stepped', "Stepped", "Stepped"),
150 ('boxed', "Boxed", "Boxed"),
151 ('bar', "Bar", "Bar"),
154 strange_boxed_effect = BoolProperty(
155 name="Strange Effect",
156 default=False,
157 description="Do not show one extrusion"
159 use_boundary = BoolProperty(
160 name="Use Boundary",
161 default=True
163 use_even_offset = BoolProperty(
164 name="Even Offset",
165 default=True
167 use_relative_offset = BoolProperty(
168 name="Relative Offset",
169 default=True
171 use_edge_rail = BoolProperty(
172 name="Edge Rail",
173 default=False
175 use_outset = BoolProperty(
176 name="Outset",
177 default=False
179 use_select_inset = BoolProperty(
180 name="Inset",
181 default=False
183 use_interpolate = BoolProperty(
184 name="Interpolate",
185 default=True
188 @classmethod
189 def poll(cls, context):
190 result = False
191 active_object = context.active_object
192 if active_object:
193 mesh_objects_name = [el.name for el in bpy.data.objects if el.type == "MESH"]
194 if active_object.name in mesh_objects_name:
195 result = True
197 return result
199 def draw(self, context):
200 layout = self.layout
201 col = layout.column()
203 col.separator()
204 col.label(text="Using Active Object", icon="INFO")
205 col.separator()
206 col.label("Face Types:")
207 col.prop(self, "face_types", text="")
208 col.separator()
209 col.prop(self, "use_relative")
211 if self.face_types == "open_inset":
212 col.prop(self, "move_inside")
213 col.prop(self, "base_height")
215 elif self.face_types == "with_base":
216 col.prop(self, "move_inside")
217 col.prop(self, "base_height")
218 col.prop(self, "second_height")
219 col.prop(self, "width")
221 elif self.face_types == "clsd_vertical":
222 col.prop(self, "base_height")
224 elif self.face_types == "open_vertical":
225 col.prop(self, "base_height")
227 elif self.face_types == "boxed":
228 col.prop(self, "move_inside")
229 col.prop(self, "base_height")
230 col.prop(self, "top_spike")
231 col.prop(self, "strange_boxed_effect")
233 elif self.face_types == "spiked":
234 col.prop(self, "spike_base_width")
235 col.prop(self, "base_height_inset")
236 col.prop(self, "top_spike")
238 elif self.face_types == "bar":
239 col.prop(self, "spike_base_width")
240 col.prop(self, "top_spike")
241 col.prop(self, "top_extra_height")
243 elif self.face_types == "stepped":
244 col.prop(self, "spike_base_width")
245 col.prop(self, "base_height_inset")
246 col.prop(self, "top_extra_height")
247 col.prop(self, "second_height")
248 col.prop(self, "step_with_real_spike")
250 def execute(self, context):
251 obj_name = self.name_source_object
252 face_type = self.face_types
254 is_selected = check_is_selected()
256 if not is_selected:
257 self.report({'WARNING'},
258 "Operation Cancelled. No selected Faces found on the Active Object")
259 return {'CANCELLED'}
261 if face_type == "spiked":
262 Spiked(spike_base_width=self.spike_base_width,
263 base_height_inset=self.base_height_inset,
264 top_spike=self.top_spike, top_relative=self.use_relative)
266 elif face_type == "boxed":
267 startinfo = prepare(self, context, self.remove_start_faces)
268 bm = startinfo['bm']
269 top = self.top_spike
270 obj = startinfo['obj']
271 obj_matrix_local = obj.matrix_local
273 distance = None
274 base_heights = None
275 t = self.move_inside
276 areas = startinfo['areas']
277 base_height = self.base_height
279 if self.use_relative:
280 distance = [min(t * area, 1.0) for i, area in enumerate(areas)]
281 base_heights = [base_height * area for i, area in enumerate(areas)]
282 else:
283 distance = [t] * len(areas)
284 base_heights = [base_height] * len(areas)
286 rings = startinfo['rings']
287 centers = startinfo['centers']
288 normals = startinfo['normals']
289 for i in range(len(rings)):
290 make_one_inset(self, context, bm=bm, ringvectors=rings[i],
291 center=centers[i], normal=normals[i],
292 t=distance[i], base_height=base_heights[i])
293 bpy.ops.mesh.select_mode(type="EDGE")
294 bpy.ops.mesh.select_more()
295 bpy.ops.mesh.select_more()
296 bpy.ops.object.mode_set(mode='OBJECT')
297 # PKHG>INFO base extrusion done and set to the mesh
299 # PKHG>INFO if the extrusion is NOT done ... it'll look strange soon!
300 if not self.strange_boxed_effect:
301 bpy.ops.object.mode_set(mode='EDIT')
302 obj = context.active_object
303 bm = bmesh.from_edit_mesh(obj.data)
304 bmfaces = [face for face in bm.faces if face.select]
305 res = extrude_faces(self, context, bm=bm, face_l=bmfaces)
306 ring_edges = [face.edges[:] for face in res]
308 bpy.ops.object.mode_set(mode='OBJECT')
310 # PKHG>INFO now the extruded facec have to move in normal direction
311 bpy.ops.object.mode_set(mode='EDIT')
312 obj = bpy.context.scene.objects.active
313 bm = bmesh.from_edit_mesh(obj.data)
314 todo_faces = [face for face in bm.faces if face.select]
315 for face in todo_faces:
316 bmesh.ops.translate(bm, vec=face.normal * top, space=obj_matrix_local,
317 verts=face.verts)
318 bpy.ops.object.mode_set(mode='OBJECT')
320 elif face_type == "stepped":
321 Stepped(spike_base_width=self.spike_base_width,
322 base_height_inset=self.base_height_inset,
323 top_spike=self.second_height,
324 top_extra_height=self.top_extra_height,
325 use_relative_offset=self.use_relative, with_spike=self.step_with_real_spike)
327 elif face_type == "open_inset":
328 startinfo = prepare(self, context, self.remove_start_faces)
329 bm = startinfo['bm']
331 # PKHG>INFO adjust for relative, via areas
332 t = self.move_inside
333 areas = startinfo['areas']
334 base_height = self.base_height
335 base_heights = None
336 distance = None
337 if self.use_relative:
338 distance = [min(t * area, 1.0) for i, area in enumerate(areas)]
339 base_heights = [base_height * area for i, area in enumerate(areas)]
340 else:
341 distance = [t] * len(areas)
342 base_heights = [base_height] * len(areas)
344 rings = startinfo['rings']
345 centers = startinfo['centers']
346 normals = startinfo['normals']
347 for i in range(len(rings)):
348 make_one_inset(self, context, bm=bm, ringvectors=rings[i],
349 center=centers[i], normal=normals[i],
350 t=distance[i], base_height=base_heights[i])
351 bpy.ops.object.mode_set(mode='OBJECT')
353 elif face_type == "with_base":
354 startinfo = prepare(self, context, self.remove_start_faces)
355 bm = startinfo['bm']
356 obj = startinfo['obj']
357 object_matrix = obj.matrix_local
359 # PKHG>INFO for relative (using areas)
360 t = self.move_inside
361 areas = startinfo['areas']
362 base_height = self.base_height
363 distance = None
364 base_heights = None
366 if self.use_relative:
367 distance = [min(t * area, 1.0) for i, area in enumerate(areas)]
368 base_heights = [base_height * area for i, area in enumerate(areas)]
369 else:
370 distance = [t] * len(areas)
371 base_heights = [base_height] * len(areas)
373 next_rings = []
374 rings = startinfo['rings']
375 centers = startinfo['centers']
376 normals = startinfo['normals']
377 for i in range(len(rings)):
378 next_rings.append(make_one_inset(self, context, bm=bm, ringvectors=rings[i],
379 center=centers[i], normal=normals[i],
380 t=distance[i], base_height=base_heights[i]))
382 prepare_ring = extrude_edges(self, context, bm=bm, edge_l_l=next_rings)
384 second_height = self.second_height
385 width = self.width
386 vectors = [[ele.verts[:] for ele in edge] for edge in prepare_ring]
387 n_ring_vecs = []
389 for rings in vectors:
390 v = []
391 for edgv in rings:
392 v.extend(edgv)
393 # PKHF>INFO no double verts allowed, coming from two adjacents edges!
394 bm.verts.ensure_lookup_table()
395 vv = list(set([ele.index for ele in v]))
397 vvv = [bm.verts[i].co for i in vv]
398 n_ring_vecs.append(vvv)
400 for i, ring in enumerate(n_ring_vecs):
401 make_one_inset(self, context, bm=bm, ringvectors=ring,
402 center=centers[i], normal=normals[i],
403 t=width, base_height=base_heights[i] + second_height)
404 bpy.ops.object.mode_set(mode='OBJECT')
406 else:
407 if face_type == "clsd_vertical":
408 obj_name = context.active_object.name
409 ClosedVertical(name=obj_name, base_height=self.base_height,
410 use_relative_base_height=self.use_relative)
412 elif face_type == "open_vertical":
413 obj_name = context.active_object.name
414 OpenVertical(name=obj_name, base_height=self.base_height,
415 use_relative_base_height=self.use_relative)
417 elif face_type == "bar":
418 startinfo = prepare(self, context, self.remove_start_faces)
420 result = []
421 bm = startinfo['bm']
422 rings = startinfo['rings']
423 centers = startinfo['centers']
424 normals = startinfo['normals']
425 spike_base_width = self.spike_base_width
426 for i, ring in enumerate(rings):
427 result.append(make_one_inset(self, context, bm=bm,
428 ringvectors=ring, center=centers[i],
429 normal=normals[i], t=spike_base_width))
431 next_ring_edges_list = extrude_edges(self, context, bm=bm,
432 edge_l_l=result)
433 top_spike = self.top_spike
434 fac = top_spike
435 object_matrix = startinfo['obj'].matrix_local
436 for i in range(len(next_ring_edges_list)):
437 translate_ONE_ring(
438 self, context, bm=bm,
439 object_matrix=object_matrix,
440 ring_edges=next_ring_edges_list[i],
441 normal=normals[i], distance=fac
443 next_ring_edges_list_2 = extrude_edges(self, context, bm=bm,
444 edge_l_l=next_ring_edges_list)
446 top_extra_height = self.top_extra_height
447 for i in range(len(next_ring_edges_list_2)):
448 move_corner_vecs_outside(
449 self, context, bm=bm,
450 edge_list=next_ring_edges_list_2[i],
451 center=centers[i], normal=normals[i],
452 base_height_erlier=fac + top_extra_height,
453 distance=fac
455 bpy.ops.mesh.select_mode(type="VERT")
456 bpy.ops.mesh.select_more()
458 bpy.ops.object.mode_set(mode='OBJECT')
460 return {'FINISHED'}
463 def find_one_ring(sel_vertices):
464 ring0 = sel_vertices.pop(0)
465 to_delete = []
467 for i, edge in enumerate(sel_vertices):
468 len_nu = len(ring0)
469 if len(ring0 - edge) < len_nu:
470 to_delete.append(i)
471 ring0 = ring0.union(edge)
473 to_delete.reverse()
475 for el in to_delete:
476 sel_vertices.pop(el)
478 return (ring0, sel_vertices)
481 class Stepped:
482 def __init__(self, spike_base_width=0.5, base_height_inset=0.0, top_spike=0.2,
483 top_relative=False, top_extra_height=0, use_relative_offset=False,
484 with_spike=False):
486 bpy.ops.object.mode_set(mode='EDIT')
487 bpy.ops.mesh.inset(
488 use_boundary=True, use_even_offset=True, use_relative_offset=False,
489 use_edge_rail=False, thickness=spike_base_width, depth=0, use_outset=True,
490 use_select_inset=False, use_individual=True, use_interpolate=True
492 bpy.ops.mesh.inset(
493 use_boundary=True, use_even_offset=True, use_relative_offset=use_relative_offset,
494 use_edge_rail=False, thickness=top_extra_height, depth=base_height_inset,
495 use_outset=True, use_select_inset=False, use_individual=True, use_interpolate=True
497 bpy.ops.mesh.inset(
498 use_boundary=True, use_even_offset=True, use_relative_offset=use_relative_offset,
499 use_edge_rail=False, thickness=spike_base_width, depth=0, use_outset=True,
500 use_select_inset=False, use_individual=True, use_interpolate=True
502 bpy.ops.mesh.inset(
503 use_boundary=True, use_even_offset=True, use_relative_offset=False,
504 use_edge_rail=False, thickness=0, depth=top_spike, use_outset=True,
505 use_select_inset=False, use_individual=True, use_interpolate=True
507 if with_spike:
508 bpy.ops.mesh.merge(type='COLLAPSE')
510 bpy.ops.object.mode_set(mode='OBJECT')
513 class Spiked:
514 def __init__(self, spike_base_width=0.5, base_height_inset=0.0, top_spike=0.2, top_relative=False):
516 obj = bpy.context.active_object
517 bpy.ops.object.mode_set(mode='EDIT')
518 bpy.ops.mesh.inset(
519 use_boundary=True, use_even_offset=True, use_relative_offset=False,
520 use_edge_rail=False, thickness=spike_base_width, depth=base_height_inset,
521 use_outset=True, use_select_inset=False, use_individual=True, use_interpolate=True
523 bpy.ops.mesh.inset(
524 use_boundary=True, use_even_offset=True, use_relative_offset=top_relative,
525 use_edge_rail=False, thickness=0, depth=top_spike, use_outset=True,
526 use_select_inset=False, use_individual=True, use_interpolate=True
529 bm = bmesh.from_edit_mesh(obj.data)
530 bpy.ops.mesh.merge(type='COLLAPSE')
531 bpy.ops.object.mode_set(mode='OBJECT')
534 class ClosedVertical:
535 def __init__(self, name="Plane", base_height=1, use_relative_base_height=False):
536 obj = bpy.data.objects[name]
538 bm = bmesh.new()
539 bm.from_mesh(obj.data)
540 # PKHG>INFO deselect chosen faces
541 sel = [f for f in bm.faces if f.select]
542 for f in sel:
543 f.select = False
544 res = bmesh.ops.extrude_discrete_faces(bm, faces=sel)
545 # PKHG>INFO select extruded faces
546 for f in res['faces']:
547 f.select = True
549 factor = base_height
550 for face in res['faces']:
551 if use_relative_base_height:
552 area = face.calc_area()
553 factor = area * base_height
554 else:
555 factor = base_height
556 for el in face.verts:
557 tmp = el.co + face.normal * factor
558 el.co = tmp
560 me = bpy.data.meshes[name]
561 bm.to_mesh(me)
562 bm.free()
565 class OpenVertical:
566 def __init__(self, name="Plane", base_height=1, use_relative_base_height=False):
568 obj = bpy.data.objects[name]
569 bm = bmesh.new()
570 bm.from_mesh(obj.data)
571 # PKHG>INFO deselect chosen faces
572 sel = [f for f in bm.faces if f.select]
573 for f in sel:
574 f.select = False
575 res = bmesh.ops.extrude_discrete_faces(bm, faces=sel)
576 # PKHG>INFO select extruded faces
577 for f in res['faces']:
578 f.select = True
580 # PKHG>INFO adjust extrusion by a vector
581 factor = base_height
582 for face in res['faces']:
583 if use_relative_base_height:
584 area = face.calc_area()
585 factor = area * base_height
586 else:
587 factor = base_height
588 for el in face.verts:
589 tmp = el.co + face.normal * factor
590 el.co = tmp
592 me = bpy.data.meshes[name]
593 bm.to_mesh(me)
594 bm.free()
596 bpy.ops.object.editmode_toggle()
597 bpy.ops.mesh.delete(type='FACE')
598 bpy.ops.object.editmode_toggle()
601 class StripFaces:
602 def __init__(self, use_boundary=True, use_even_offset=True, use_relative_offset=False,
603 use_edge_rail=True, thickness=0.0, depth=0.0, use_outset=False,
604 use_select_inset=False, use_individual=True, use_interpolate=True):
606 bpy.ops.object.mode_set(mode='EDIT')
607 bpy.ops.mesh.inset(
608 use_boundary=use_boundary, use_even_offset=True, use_relative_offset=False,
609 use_edge_rail=True, thickness=thickness, depth=depth, use_outset=use_outset,
610 use_select_inset=use_select_inset, use_individual=use_individual,
611 use_interpolate=use_interpolate
614 bpy.ops.object.mode_set(mode='OBJECT')
616 # PKHG>IMFO only 3 parameters inc execution context supported!!
617 if False:
618 bpy.ops.mesh.inset(
619 use_boundary, use_even_offset, use_relative_offset, use_edge_rail,
620 thickness, depth, use_outset, use_select_inset, use_individual,
621 use_interpolate
623 elif type == 0:
624 bpy.ops.mesh.inset(
625 use_boundary=True, use_even_offset=True, use_relative_offset=False,
626 use_edge_rail=True, thickness=thickness, depth=depth, use_outset=False,
627 use_select_inset=False, use_individual=True, use_interpolate=True
629 elif type == 1:
630 bpy.ops.mesh.inset(
631 use_boundary=True, use_even_offset=True, use_relative_offset=False,
632 use_edge_rail=True, thickness=thickness, depth=depth, use_outset=False,
633 use_select_inset=False, use_individual=True, use_interpolate=False
635 bpy.ops.mesh.delete(type='FACE')
637 elif type == 2:
638 bpy.ops.mesh.inset(
639 use_boundary=True, use_even_offset=False, use_relative_offset=True,
640 use_edge_rail=True, thickness=thickness, depth=depth, use_outset=False,
641 use_select_inset=False, use_individual=True, use_interpolate=False
644 bpy.ops.mesh.delete(type='FACE')
646 elif type == 3:
647 bpy.ops.mesh.inset(
648 use_boundary=True, use_even_offset=False, use_relative_offset=True,
649 use_edge_rail=True, thickness=depth, depth=thickness, use_outset=False,
650 use_select_inset=False, use_individual=True, use_interpolate=True
652 bpy.ops.mesh.delete(type='FACE')
653 elif type == 4:
654 bpy.ops.mesh.inset(
655 use_boundary=True, use_even_offset=False, use_relative_offset=True,
656 use_edge_rail=True, thickness=thickness, depth=depth, use_outset=True,
657 use_select_inset=False, use_individual=True, use_interpolate=True
659 bpy.ops.mesh.inset(
660 use_boundary=True, use_even_offset=False, use_relative_offset=True,
661 use_edge_rail=True, thickness=thickness, depth=depth, use_outset=True,
662 use_select_inset=False, use_individual=True, use_interpolate=True
664 bpy.ops.mesh.delete(type='FACE')
666 bpy.ops.object.mode_set(mode='OBJECT')
669 def check_is_selected():
670 is_selected = False
671 for face in bpy.context.active_object.data.polygons:
672 if face.select:
673 is_selected = True
674 break
675 return is_selected
678 def prepare(self, context, remove_start_faces=True):
680 Start for a face selected change of faces
681 select an object of type mesh, with activated several (all) faces
683 obj = bpy.context.scene.objects.active
684 bpy.ops.object.mode_set(mode='OBJECT')
685 selectedpolygons = [el for el in obj.data.polygons if el.select]
687 # PKHG>INFO copies of the vectors are needed, otherwise Blender crashes!
688 centers = [face.center for face in selectedpolygons]
689 centers_copy = [Vector((el[0], el[1], el[2])) for el in centers]
690 normals = [face.normal for face in selectedpolygons]
691 normals_copy = [Vector((el[0], el[1], el[2])) for el in normals]
693 vertindicesofpolgons = [
694 [vert for vert in face.vertices] for face in selectedpolygons
696 vertVectorsOfSelectedFaces = [
697 [obj.data.vertices[ind].co for ind in vertIndiceofface] for
698 vertIndiceofface in vertindicesofpolgons
700 vertVectorsOfSelectedFaces_copy = [
701 [Vector((el[0], el[1], el[2])) for el in listofvecs] for
702 listofvecs in vertVectorsOfSelectedFaces
705 bpy.ops.object.mode_set(mode='EDIT')
706 bm = bmesh.from_edit_mesh(obj.data)
707 selected_bm_faces = [ele for ele in bm.faces if ele.select]
709 selected_edges_per_face_ind = [
710 [ele.index for ele in face.edges] for face in selected_bm_faces
712 indices = [el.index for el in selectedpolygons]
713 selected_faces_areas = [bm.faces[:][i] for i in indices]
714 tmp_area = [el.calc_area() for el in selected_faces_areas]
716 # PKHG>INFO, selected faces are removed, only their edges are used!
717 if remove_start_faces:
718 bpy.ops.mesh.delete(type='ONLY_FACE')
719 bpy.ops.object.mode_set(mode='OBJECT')
720 obj.data.update()
721 bpy.ops.object.mode_set(mode='EDIT')
722 bm = bmesh.from_edit_mesh(obj.data)
723 bm.verts.ensure_lookup_table()
724 bm.faces.ensure_lookup_table()
726 start_ring_raw = [
727 [bm.verts[ind].index for ind in vertIndiceofface] for
728 vertIndiceofface in vertindicesofpolgons
730 start_ring = []
732 for el in start_ring_raw:
733 start_ring.append(set(el))
734 bm.edges.ensure_lookup_table()
736 bm_selected_edges_l_l = [
737 [bm.edges[i] for i in bm_ind_list] for
738 bm_ind_list in selected_edges_per_face_ind
740 result = {
741 'obj': obj, 'centers': centers_copy, 'normals': normals_copy,
742 'rings': vertVectorsOfSelectedFaces_copy, 'bm': bm,
743 'areas': tmp_area, 'startBMRingVerts': start_ring,
744 'base_edges': bm_selected_edges_l_l
747 return result
750 def make_one_inset(self, context, bm=None, ringvectors=None, center=None,
751 normal=None, t=None, base_height=0):
752 # a face will get 'inserted' faces to create (normally) a hole if t is > 0 and < 1)
753 tmp = []
755 for el in ringvectors:
756 tmp.append((el * (1 - t) + center * t) + normal * base_height)
758 tmp = [bm.verts.new(v) for v in tmp] # the new corner bmvectors
759 # PKHG>INFO so to say sentinells, to use ONE for ...
760 tmp.append(tmp[0])
761 vectorsFace_i = [bm.verts.new(v) for v in ringvectors]
762 vectorsFace_i.append(vectorsFace_i[0])
763 myres = []
764 for ii in range(len(vectorsFace_i) - 1):
765 # PKHG>INFO next line: sequence is important! for added edge
766 bmvecs = [vectorsFace_i[ii], vectorsFace_i[ii + 1], tmp[ii + 1], tmp[ii]]
767 res = bm.faces.new(bmvecs)
768 myres.append(res.edges[2])
769 myres[-1].select = True # PKHG>INFO to be used later selected!
770 return (myres)
773 def extrude_faces(self, context, bm=None, face_l=None):
774 # to make a ring extrusion
775 res = bmesh.ops.extrude_discrete_faces(bm, faces=face_l)['faces']
777 for face in res:
778 face.select = True
779 return res
782 def extrude_edges(self, context, bm=None, edge_l_l=None):
783 # to make a ring extrusion
784 all_results = []
785 for edge_l in edge_l_l:
786 for edge in edge_l:
787 edge.select = False
788 res = bmesh.ops.extrude_edge_only(bm, edges=edge_l)
789 tmp = [ele for ele in res['geom'] if isinstance(ele, bmesh.types.BMEdge)]
790 for edge in tmp:
791 edge.select = True
792 all_results.append(tmp)
793 return all_results
796 def translate_ONE_ring(self, context, bm=None, object_matrix=None, ring_edges=None,
797 normal=(0, 0, 1), distance=0.5):
798 # translate a ring in given (normal?!) direction with given (global) amount
799 tmp = []
800 for edge in ring_edges:
801 tmp.extend(edge.verts[:])
802 # PKHG>INFO no double vertices allowed by bmesh!
803 tmp = set(tmp)
804 tmp = list(tmp)
805 bmesh.ops.translate(bm, vec=normal * distance, space=object_matrix, verts=tmp)
806 # PKHG>INFO relevant edges will stay selected
807 return ring_edges
810 def move_corner_vecs_outside(self, context, bm=None, edge_list=None, center=None,
811 normal=None, base_height_erlier=0.5, distance=0.5):
812 # move corners (outside meant mostly) dependent on the parameters
813 tmp = []
814 for edge in edge_list:
815 tmp.extend([ele for ele in edge.verts if isinstance(ele, bmesh.types.BMVert)])
816 # PKHG>INFO to remove vertices, they are all used twice in the ring!
817 tmp = set(tmp)
818 tmp = list(tmp)
820 for i in range(len(tmp)):
821 vec = tmp[i].co
822 direction = vec + (vec - (normal * base_height_erlier + center)) * distance
823 tmp[i].co = direction
826 def register():
827 bpy.utils.register_module(__name__)
830 def unregister():
831 bpy.utils.unregister_module(__name__)
834 if __name__ == "__main__":
835 register()