GPencil Tools: Box deform multi object edit
[blender-addons.git] / archipack / bmesh_utils.py
blob16be7a5485d624b1cac7311b8e9744baa454aa6f
1 # -*- coding:utf-8 -*-
3 # ##### BEGIN GPL LICENSE BLOCK #####
5 # This program is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU General Public License
7 # as published by the Free Software Foundation; either version 2
8 # of the License, or (at your option) any later version.
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software Foundation,
17 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110- 1301, USA.
19 # ##### END GPL LICENSE BLOCK #####
21 # <pep8 compliant>
23 # ----------------------------------------------------------
24 # Author: Stephen Leger (s-leger)
26 # ----------------------------------------------------------
27 import bpy
28 import bmesh
31 class BmeshEdit():
32 @staticmethod
33 def _start(context, o):
34 """
35 private, start bmesh editing of active object
36 """
37 o.select_set(state=True)
38 context.view_layer.objects.active = o
39 bpy.ops.object.mode_set(mode='EDIT')
40 bm = bmesh.from_edit_mesh(o.data)
41 bm.verts.ensure_lookup_table()
42 bm.faces.ensure_lookup_table()
43 return bm
45 @staticmethod
46 def bmesh_join(context, o, list_of_bmeshes, normal_update=False):
47 """
48 takes as input a list of bm references and outputs a single merged bmesh
49 allows an additional 'normal_update=True' to force _normal_ calculations.
50 """
51 bm = BmeshEdit._start(context, o)
53 add_vert = bm.verts.new
54 add_face = bm.faces.new
55 add_edge = bm.edges.new
57 for bm_to_add in list_of_bmeshes:
58 offset = len(bm.verts)
60 for v in bm_to_add.verts:
61 add_vert(v.co)
63 bm.verts.index_update()
64 bm.verts.ensure_lookup_table()
66 if bm_to_add.faces:
67 layer = bm_to_add.loops.layers.uv.verify()
68 dest = bm.loops.layers.uv.verify()
69 for face in bm_to_add.faces:
70 f = add_face(tuple(bm.verts[i.index + offset] for i in face.verts))
71 f.material_index = face.material_index
72 for j, loop in enumerate(face.loops):
73 f.loops[j][dest].uv = loop[layer].uv
74 bm.faces.index_update()
76 if bm_to_add.edges:
77 for edge in bm_to_add.edges:
78 edge_seq = tuple(bm.verts[i.index + offset] for i in edge.verts)
79 try:
80 add_edge(edge_seq)
81 except ValueError:
82 # edge exists!
83 pass
84 bm.edges.index_update()
86 # cleanup
87 for old_bm in list_of_bmeshes:
88 old_bm.free()
90 if normal_update:
91 bm.normal_update()
93 BmeshEdit._end(bm, o)
95 @staticmethod
96 def _end(bm, o):
97 """
98 private, end bmesh editing of active object
99 """
100 bm.normal_update()
101 bmesh.update_edit_mesh(o.data, loop_triangles=True)
102 bpy.ops.object.mode_set(mode='OBJECT')
103 bm.free()
105 @staticmethod
106 def _matids(bm, matids):
107 for i, matid in enumerate(matids):
108 bm.faces[i].material_index = matid
110 @staticmethod
111 def _uvs(bm, uvs):
112 layer = bm.loops.layers.uv.verify()
113 l_i = len(uvs)
114 for i, face in enumerate(bm.faces):
115 if i > l_i:
116 raise RuntimeError("Missing uvs for face {}".format(i))
117 l_j = len(uvs[i])
118 for j, loop in enumerate(face.loops):
119 if j > l_j:
120 raise RuntimeError("Missing uv {} for face {}".format(j, i))
121 loop[layer].uv = uvs[i][j]
123 @staticmethod
124 def _verts(bm, verts):
125 for i, v in enumerate(verts):
126 bm.verts[i].co = v
128 @staticmethod
129 def buildmesh(context, o, verts, faces,
130 matids=None, uvs=None, weld=False,
131 clean=False, auto_smooth=True, temporary=False):
133 if temporary:
134 bm = bmesh.new()
135 else:
136 bm = BmeshEdit._start(context, o)
137 bm.clear()
139 for v in verts:
140 bm.verts.new(v)
141 bm.verts.index_update()
142 bm.verts.ensure_lookup_table()
144 for f in faces:
145 bm.faces.new([bm.verts[i] for i in f])
146 bm.faces.index_update()
147 bm.faces.ensure_lookup_table()
149 if matids is not None:
150 BmeshEdit._matids(bm, matids)
152 if uvs is not None:
153 BmeshEdit._uvs(bm, uvs)
155 if temporary:
156 return bm
158 if weld:
159 bmesh.ops.remove_doubles(bm, verts=bm.verts, dist=0.001)
160 BmeshEdit._end(bm, o)
161 bpy.ops.object.mode_set(mode='EDIT')
162 bpy.ops.mesh.select_all(action='SELECT')
163 if auto_smooth:
164 bpy.ops.mesh.faces_shade_smooth()
165 o.data.use_auto_smooth = True
166 else:
167 bpy.ops.mesh.faces_shade_flat()
168 if clean:
169 bpy.ops.mesh.delete_loose()
170 bpy.ops.object.mode_set(mode='OBJECT')
172 @staticmethod
173 def addmesh(context, o, verts, faces, matids=None, uvs=None, weld=False, clean=False, auto_smooth=True):
174 bm = BmeshEdit._start(context, o)
175 nv = len(bm.verts)
176 nf = len(bm.faces)
178 for v in verts:
179 bm.verts.new(v)
181 bm.verts.ensure_lookup_table()
183 for f in faces:
184 bm.faces.new([bm.verts[nv + i] for i in f])
186 bm.faces.ensure_lookup_table()
188 if matids is not None:
189 for i, matid in enumerate(matids):
190 bm.faces[nf + i].material_index = matid
192 if uvs is not None:
193 layer = bm.loops.layers.uv.verify()
194 for i, face in enumerate(bm.faces[nf:]):
195 for j, loop in enumerate(face.loops):
196 loop[layer].uv = uvs[i][j]
198 if weld:
199 bmesh.ops.remove_doubles(bm, verts=bm.verts, dist=0.001)
200 BmeshEdit._end(bm, o)
201 bpy.ops.object.mode_set(mode='EDIT')
202 bpy.ops.mesh.select_all(action='SELECT')
203 if auto_smooth:
204 bpy.ops.mesh.faces_shade_smooth()
205 o.data.use_auto_smooth = True
206 else:
207 bpy.ops.mesh.faces_shade_flat()
208 if clean:
209 bpy.ops.mesh.delete_loose()
210 bpy.ops.object.mode_set(mode='OBJECT')
212 @staticmethod
213 def bevel(context, o,
214 offset,
215 offset_type='OFFSET',
216 segments=1,
217 profile=0.5,
218 # vertex_only=False,
219 clamp_overlap=True,
220 material=-1,
221 use_selection=True):
223 /* Bevel offset_type slot values */
224 enum {
225 BEVEL_AMT_OFFSET,
226 BEVEL_AMT_WIDTH,
227 BEVEL_AMT_DEPTH,
228 BEVEL_AMT_PERCENT
231 bm = bmesh.new()
232 bm.from_mesh(o.data)
233 bm.verts.ensure_lookup_table()
234 if use_selection:
235 geom = [v for v in bm.verts if v.select]
236 geom.extend([ed for ed in bm.edges if ed.select])
237 else:
238 geom = bm.verts[:]
239 geom.extend(bm.edges[:])
241 bmesh.ops.bevel(bm,
242 geom=geom,
243 offset=offset,
244 offset_type=offset_type,
245 segments=segments,
246 profile=profile,
247 # vertex_only=vertex_only,
248 clamp_overlap=clamp_overlap,
249 material=material)
251 bm.to_mesh(o.data)
252 bm.free()
254 @staticmethod
255 def bissect(context, o,
256 plane_co,
257 plane_no,
258 dist=0.001,
259 use_snap_center=False,
260 clear_outer=True,
261 clear_inner=False
264 bm = bmesh.new()
265 bm.from_mesh(o.data)
266 bm.verts.ensure_lookup_table()
267 geom = bm.verts[:]
268 geom.extend(bm.edges[:])
269 geom.extend(bm.faces[:])
271 bmesh.ops.bisect_plane(bm,
272 geom=geom,
273 dist=dist,
274 plane_co=plane_co,
275 plane_no=plane_no,
276 use_snap_center=False,
277 clear_outer=clear_outer,
278 clear_inner=clear_inner
281 bm.to_mesh(o.data)
282 bm.free()
284 @staticmethod
285 def solidify(context, o, amt, floor_bottom=False, altitude=0):
286 bm = bmesh.new()
287 bm.from_mesh(o.data)
288 bm.verts.ensure_lookup_table()
289 geom = bm.faces[:]
290 bmesh.ops.solidify(bm, geom=geom, thickness=amt)
291 if floor_bottom:
292 for v in bm.verts:
293 if not v.select:
294 v.co.z = altitude
295 bm.to_mesh(o.data)
296 bm.free()
298 @staticmethod
299 def verts(context, o, verts):
301 update vertex position of active object
303 bm = BmeshEdit._start(context, o)
304 BmeshEdit._verts(bm, verts)
305 BmeshEdit._end(bm, o)
307 @staticmethod
308 def aspect(context, o, matids, uvs):
310 update material id and uvmap of active object
312 bm = BmeshEdit._start(context, o)
313 BmeshEdit._matids(bm, matids)
314 BmeshEdit._uvs(bm, uvs)
315 BmeshEdit._end(bm, o)