Cleanup: trailing space
[blender-addons.git] / mesh_tools / pkhg_faces.py
blob1e9ee349a4bc482f9f71a2ed666ec9a49ace90ad
1 # SPDX-License-Identifier: GPL-2.0-or-later
3 bl_info = {
4 "name": "PKHG faces",
5 "author": "PKHG",
6 "version": (0, 0, 6),
7 "blender": (2, 71, 0),
8 "location": "View3D > Tools > PKHG (tab)",
9 "description": "Faces selected will become added faces of different style",
10 "warning": "",
11 "doc_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 Shape"
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(text="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.view_layer.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]
537 bpy.ops.object.mode_set(mode='OBJECT')
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 bpy.ops.object.mode_set(mode='OBJECT')
570 bm = bmesh.new()
571 bm.from_mesh(obj.data)
572 # PKHG>INFO deselect chosen faces
573 sel = [f for f in bm.faces if f.select]
574 for f in sel:
575 f.select = False
576 res = bmesh.ops.extrude_discrete_faces(bm, faces=sel)
577 # PKHG>INFO select extruded faces
578 for f in res['faces']:
579 f.select = True
581 # PKHG>INFO adjust extrusion by a vector
582 factor = base_height
583 for face in res['faces']:
584 if use_relative_base_height:
585 area = face.calc_area()
586 factor = area * base_height
587 else:
588 factor = base_height
589 for el in face.verts:
590 tmp = el.co + face.normal * factor
591 el.co = tmp
593 me = bpy.data.meshes[name]
594 bm.to_mesh(me)
595 bm.free()
597 bpy.ops.object.editmode_toggle()
598 bpy.ops.mesh.delete(type='FACE')
599 bpy.ops.object.editmode_toggle()
602 class StripFaces:
603 def __init__(self, use_boundary=True, use_even_offset=True, use_relative_offset=False,
604 use_edge_rail=True, thickness=0.0, depth=0.0, use_outset=False,
605 use_select_inset=False, use_individual=True, use_interpolate=True):
607 bpy.ops.object.mode_set(mode='EDIT')
608 bpy.ops.mesh.inset(
609 use_boundary=use_boundary, use_even_offset=True, use_relative_offset=False,
610 use_edge_rail=True, thickness=thickness, depth=depth, use_outset=use_outset,
611 use_select_inset=use_select_inset, use_individual=use_individual,
612 use_interpolate=use_interpolate
615 bpy.ops.object.mode_set(mode='OBJECT')
617 # PKHG>IMFO only 3 parameters inc execution context supported!!
618 if False:
619 bpy.ops.mesh.inset(
620 use_boundary, use_even_offset, use_relative_offset, use_edge_rail,
621 thickness, depth, use_outset, use_select_inset, use_individual,
622 use_interpolate
624 elif type == 0:
625 bpy.ops.mesh.inset(
626 use_boundary=True, use_even_offset=True, use_relative_offset=False,
627 use_edge_rail=True, thickness=thickness, depth=depth, use_outset=False,
628 use_select_inset=False, use_individual=True, use_interpolate=True
630 elif type == 1:
631 bpy.ops.mesh.inset(
632 use_boundary=True, use_even_offset=True, use_relative_offset=False,
633 use_edge_rail=True, thickness=thickness, depth=depth, use_outset=False,
634 use_select_inset=False, use_individual=True, use_interpolate=False
636 bpy.ops.mesh.delete(type='FACE')
638 elif type == 2:
639 bpy.ops.mesh.inset(
640 use_boundary=True, use_even_offset=False, use_relative_offset=True,
641 use_edge_rail=True, thickness=thickness, depth=depth, use_outset=False,
642 use_select_inset=False, use_individual=True, use_interpolate=False
645 bpy.ops.mesh.delete(type='FACE')
647 elif type == 3:
648 bpy.ops.mesh.inset(
649 use_boundary=True, use_even_offset=False, use_relative_offset=True,
650 use_edge_rail=True, thickness=depth, depth=thickness, use_outset=False,
651 use_select_inset=False, use_individual=True, use_interpolate=True
653 bpy.ops.mesh.delete(type='FACE')
654 elif type == 4:
655 bpy.ops.mesh.inset(
656 use_boundary=True, use_even_offset=False, use_relative_offset=True,
657 use_edge_rail=True, thickness=thickness, depth=depth, use_outset=True,
658 use_select_inset=False, use_individual=True, use_interpolate=True
660 bpy.ops.mesh.inset(
661 use_boundary=True, use_even_offset=False, use_relative_offset=True,
662 use_edge_rail=True, thickness=thickness, depth=depth, use_outset=True,
663 use_select_inset=False, use_individual=True, use_interpolate=True
665 bpy.ops.mesh.delete(type='FACE')
667 bpy.ops.object.mode_set(mode='OBJECT')
670 def check_is_selected():
671 is_selected = False
672 for face in bpy.context.active_object.data.polygons:
673 if face.select:
674 is_selected = True
675 break
676 return is_selected
679 def prepare(self, context, remove_start_faces=True):
681 Start for a face selected change of faces
682 select an object of type mesh, with activated several (all) faces
684 obj = bpy.context.view_layer.objects.active
685 bpy.ops.object.mode_set(mode='OBJECT')
686 selectedpolygons = [el for el in obj.data.polygons if el.select]
688 # PKHG>INFO copies of the vectors are needed, otherwise Blender crashes!
689 centers = [face.center for face in selectedpolygons]
690 centers_copy = [Vector((el[0], el[1], el[2])) for el in centers]
691 normals = [face.normal for face in selectedpolygons]
692 normals_copy = [Vector((el[0], el[1], el[2])) for el in normals]
694 vertindicesofpolgons = [
695 [vert for vert in face.vertices] for face in selectedpolygons
697 vertVectorsOfSelectedFaces = [
698 [obj.data.vertices[ind].co for ind in vertIndiceofface] for
699 vertIndiceofface in vertindicesofpolgons
701 vertVectorsOfSelectedFaces_copy = [
702 [Vector((el[0], el[1], el[2])) for el in listofvecs] for
703 listofvecs in vertVectorsOfSelectedFaces
706 bpy.ops.object.mode_set(mode='EDIT')
707 bm = bmesh.from_edit_mesh(obj.data)
708 selected_bm_faces = [ele for ele in bm.faces if ele.select]
710 selected_edges_per_face_ind = [
711 [ele.index for ele in face.edges] for face in selected_bm_faces
713 indices = [el.index for el in selectedpolygons]
714 selected_faces_areas = [bm.faces[:][i] for i in indices]
715 tmp_area = [el.calc_area() for el in selected_faces_areas]
717 # PKHG>INFO, selected faces are removed, only their edges are used!
718 if remove_start_faces:
719 bpy.ops.mesh.delete(type='ONLY_FACE')
720 bpy.ops.object.mode_set(mode='OBJECT')
721 obj.data.update()
722 bpy.ops.object.mode_set(mode='EDIT')
723 bm = bmesh.from_edit_mesh(obj.data)
724 bm.verts.ensure_lookup_table()
725 bm.faces.ensure_lookup_table()
727 start_ring_raw = [
728 [bm.verts[ind].index for ind in vertIndiceofface] for
729 vertIndiceofface in vertindicesofpolgons
731 start_ring = []
733 for el in start_ring_raw:
734 start_ring.append(set(el))
735 bm.edges.ensure_lookup_table()
737 bm_selected_edges_l_l = [
738 [bm.edges[i] for i in bm_ind_list] for
739 bm_ind_list in selected_edges_per_face_ind
741 result = {
742 'obj': obj, 'centers': centers_copy, 'normals': normals_copy,
743 'rings': vertVectorsOfSelectedFaces_copy, 'bm': bm,
744 'areas': tmp_area, 'startBMRingVerts': start_ring,
745 'base_edges': bm_selected_edges_l_l
748 return result
751 def make_one_inset(self, context, bm=None, ringvectors=None, center=None,
752 normal=None, t=None, base_height=0):
753 # a face will get 'inserted' faces to create (normally) a hole if t is > 0 and < 1)
754 tmp = []
756 for el in ringvectors:
757 tmp.append((el * (1 - t) + center * t) + normal * base_height)
759 tmp = [bm.verts.new(v) for v in tmp] # the new corner bmvectors
760 # PKHG>INFO so to say sentinells, to use ONE for ...
761 tmp.append(tmp[0])
762 vectorsFace_i = [bm.verts.new(v) for v in ringvectors]
763 vectorsFace_i.append(vectorsFace_i[0])
764 myres = []
765 for ii in range(len(vectorsFace_i) - 1):
766 # PKHG>INFO next line: sequence is important! for added edge
767 bmvecs = [vectorsFace_i[ii], vectorsFace_i[ii + 1], tmp[ii + 1], tmp[ii]]
768 res = bm.faces.new(bmvecs)
769 myres.append(res.edges[2])
770 myres[-1].select = True # PKHG>INFO to be used later selected!
771 return (myres)
774 def extrude_faces(self, context, bm=None, face_l=None):
775 # to make a ring extrusion
776 res = bmesh.ops.extrude_discrete_faces(bm, faces=face_l)['faces']
778 for face in res:
779 face.select = True
780 return res
783 def extrude_edges(self, context, bm=None, edge_l_l=None):
784 # to make a ring extrusion
785 all_results = []
786 for edge_l in edge_l_l:
787 for edge in edge_l:
788 edge.select = False
789 res = bmesh.ops.extrude_edge_only(bm, edges=edge_l)
790 tmp = [ele for ele in res['geom'] if isinstance(ele, bmesh.types.BMEdge)]
791 for edge in tmp:
792 edge.select = True
793 all_results.append(tmp)
794 return all_results
797 def translate_ONE_ring(self, context, bm=None, object_matrix=None, ring_edges=None,
798 normal=(0, 0, 1), distance=0.5):
799 # translate a ring in given (normal?!) direction with given (global) amount
800 tmp = []
801 for edge in ring_edges:
802 tmp.extend(edge.verts[:])
803 # PKHG>INFO no double vertices allowed by bmesh!
804 tmp = set(tmp)
805 tmp = list(tmp)
806 bmesh.ops.translate(bm, vec=normal * distance, space=object_matrix, verts=tmp)
807 # PKHG>INFO relevant edges will stay selected
808 return ring_edges
811 def move_corner_vecs_outside(self, context, bm=None, edge_list=None, center=None,
812 normal=None, base_height_erlier=0.5, distance=0.5):
813 # move corners (outside meant mostly) dependent on the parameters
814 tmp = []
815 for edge in edge_list:
816 tmp.extend([ele for ele in edge.verts if isinstance(ele, bmesh.types.BMVert)])
817 # PKHG>INFO to remove vertices, they are all used twice in the ring!
818 tmp = set(tmp)
819 tmp = list(tmp)
821 for i in range(len(tmp)):
822 vec = tmp[i].co
823 direction = vec + (vec - (normal * base_height_erlier + center)) * distance
824 tmp[i].co = direction
826 # define classes for registration
827 classes = (
828 MESH_OT_add_faces_to_object,
831 def register():
832 for cls in classes:
833 bpy.utils.register_class(cls)
836 def unregister():
837 for cls in classes:
838 bpy.utils.unregister_class(cls)
841 if __name__ == "__main__":
842 register()