1
# ##### BEGIN GPL LICENSE BLOCK #####
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; version 2
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.
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.
17 # ##### END GPL LICENSE BLOCK #####
19 # based upon the functionality of Mesh to wall by luxuy_BlenderCN
20 # thanks to meta-androcto
23 "name": "Edge Floor Plan",
24 "author": "lijenstina",
26 "blender": (2, 78, 0),
27 "location": "View3D > EditMode > Mesh",
28 "description": "Make a Floor Plan from Edges",
34 from bpy
.types
import Operator
35 from bpy
.props
import (
44 # Handle error notifications
45 def error_handlers(self
, error
, reports
="ERROR"):
47 self
.report({'WARNING'}, reports
+ " (See Console for more info)")
49 print("\n[mesh.edges_floor_plan]\nError: {}\n".format(error
))
52 class MESH_OT_edges_floor_plan(Operator
):
53 bl_idname
= "mesh.edges_floor_plan"
54 bl_label
= "Edges Floor Plan"
55 bl_description
= "Top View, Extrude Flat Along Edges"
56 bl_options
= {'REGISTER', 'UNDO'}
60 description
="Set the width of the generated walls\n",
64 depth
= FloatProperty(
66 description
="Set the height of the inner wall edges",
70 connect_ends
= BoolProperty(
72 description
="Connect the ends of the boundary Edge loops",
75 repeat_cleanup
= IntProperty(
76 name
="Recursive Prepare",
77 description
="Number of times that the preparation phase runs\n"
78 "at the start of the script\n"
79 "If parts of the mesh are not modified, increase this value",
84 ('EDGE_NET', "Edge Net",
85 "Edge Net Method for mesh preparation - Initial Fill\n"
86 "The filled in faces will be Inset individually\n"
87 "Supports simple 3D objects"),
88 ('SINGLE_FACE', "Single Face",
89 "Single Face Method for mesh preparation - Initial Fill\n"
90 "The produced face will be Triangulated before Inset Region\n"
91 "Good for edges forming a circle, avoid 3D objects"),
92 ('SOLIDIFY', "Solidify",
93 "Extrude and Solidify Method\n"
94 "Useful for complex meshes, however works best on flat surfaces\n"
95 "as the extrude direction has to be defined")
97 fill_type
= EnumProperty(
100 description
="Choose the method for creating geometry",
103 keep_faces
= BoolProperty(
105 description
="Keep or not the fill faces\n"
106 "Can depend on Remove Ngons state",
109 tri_faces
= BoolProperty(
110 name
="Triangulate Faces",
111 description
="Triangulate the created fill faces\n"
112 "Sometimes can lead to unsatisfactory results",
115 initial_extrude
= FloatVectorProperty(
116 name
="Initial Extrude",
118 default
=(0.0, 0.0, 0.1),
124 remove_ngons
= BoolProperty(
126 description
="Keep or not the Ngon Faces\n"
127 "Note about limitations:\n"
128 "Sometimes the kept Faces could be Ngons\n"
129 "Removing the Ngons can lead to no geometry created",
132 offset
= FloatProperty(
134 description
="Set the offset for the Solidify modifier",
138 only_rim
= BoolProperty(
140 description
="Solidify Fill Rim only option",
145 def poll(cls
, context
):
146 ob
= context
.active_object
147 return (ob
and ob
.type == 'MESH' and context
.mode
== 'EDIT_MESH')
149 def check_edge(self
, context
):
150 bpy
.ops
.object.mode_set(mode
='OBJECT')
151 bpy
.ops
.object.mode_set(mode
='EDIT')
152 obj
= bpy
.context
.object
154 if len(me_check
.edges
) < 1:
162 bm
.verts
.ensure_lookup_table()
163 bm
.edges
.ensure_lookup_table()
164 bm
.faces
.ensure_lookup_table()
166 def solidify_mod(self
, context
, ob
, wid
, offset
, only_rim
):
168 mods
= ob
.modifiers
.new(
169 name
="_Mesh_Solidify_Wall", type='SOLIDIFY'
172 mods
.use_quality_normals
= True
174 mods
.use_even_offset
= True
176 mods
.use_rim_only
= only_rim
177 mods
.show_on_cage
= True
179 bpy
.ops
.object.modifier_apply(
180 modifier
="_Mesh_Solidify_Wall"
182 except Exception as e
:
183 error_handlers(self
, e
,
184 reports
="Adding a Solidify Modifier failed")
187 def draw(self
, context
):
191 box
.label(text
="Choose Method:", icon
="SCRIPTWIN")
192 box
.prop(self
, "fill_type")
194 col
= box
.column(align
=True)
196 if self
.fill_type
== 'EDGE_NET':
197 col
.prop(self
, "repeat_cleanup")
198 col
.prop(self
, "remove_ngons", toggle
=True)
200 elif self
.fill_type
== 'SOLIDIFY':
201 col
.prop(self
, "offset", slider
=True)
202 col
.prop(self
, "initial_extrude")
205 col
.prop(self
, "remove_ngons", toggle
=True)
206 col
.prop(self
, "tri_faces", toggle
=True)
209 box
.label(text
="Settings:", icon
="MOD_BUILD")
211 col
= box
.column(align
=True)
212 col
.prop(self
, "wid")
214 if self
.fill_type
!= 'SOLIDIFY':
215 col
.prop(self
, "depth")
216 col
.prop(self
, "connect_ends", toggle
=True)
217 col
.prop(self
, "keep_faces", toggle
=True)
219 col
.prop(self
, "only_rim", toggle
=True)
221 def execute(self
, context
):
222 if not self
.check_edge(context
):
223 self
.report({'WARNING'},
224 "Operation Cancelled. Needs a Mesh with at least one edge")
228 depth
= self
.depth
* 0.1
229 offset
= self
.offset
* 0.1
230 store_selection_mode
= context
.tool_settings
.mesh_select_mode
231 # Note: the remove_doubles called after bmesh creation would make
232 # blender crash with certain meshes - keep it in mind for the future
233 bpy
.ops
.mesh
.remove_doubles(threshold
=0.003)
234 bpy
.ops
.object.mode_set(mode
='OBJECT')
235 bpy
.ops
.object.mode_set(mode
='EDIT')
236 ob
= bpy
.context
.object
239 bm
= bmesh
.from_edit_mesh(me
)
241 bmesh
.ops
.delete(bm
, geom
=bm
.faces
, context
=3)
243 context
.tool_settings
.mesh_select_mode
= (False, True, False)
244 original_edges
= [edge
.index
for edge
in bm
.edges
]
245 original_verts
= [vert
.index
for vert
in bm
.verts
]
247 bpy
.ops
.mesh
.select_all(action
='DESELECT')
249 if self
.fill_type
== 'EDGE_NET':
250 for i
in range(self
.repeat_cleanup
):
251 bmesh
.ops
.edgenet_prepare(bm
, edges
=bm
.edges
)
253 bmesh
.ops
.edgenet_fill(bm
, edges
=bm
.edges
, mat_nr
=0, use_smooth
=True, sides
=0)
255 if self
.remove_ngons
:
256 ngons
= [face
for face
in bm
.faces
if len(face
.edges
) > 4]
258 bmesh
.ops
.delete(bm
, geom
=ngons
, context
=5) # 5 - delete faces
262 elif self
.fill_type
== 'SOLIDIFY':
263 for vert
in bm
.verts
:
266 bmesh
.ops
.extrude_edge_only(
267 bm
, edges
=bm
.edges
, use_select_history
=False
270 verts_extrude
= [vert
for vert
in bm
.verts
if vert
.index
in original_verts
]
275 vec
=(self
.initial_extrude
)
281 for edge
in bm
.edges
:
285 bm
= bmesh
.update_edit_mesh(ob
.data
, 1, 1)
287 bpy
.ops
.object.mode_set(mode
='OBJECT')
288 self
.solidify_mod(context
, ob
, wid
, offset
, self
.only_rim
)
290 bpy
.ops
.object.mode_set(mode
='EDIT')
292 context
.tool_settings
.mesh_select_mode
= store_selection_mode
297 bm
.faces
.new(bm
.verts
)
301 bmesh
.ops
.triangle_fill(
302 bm
, use_beauty
=True, use_dissolve
=False, edges
=bm
.edges
306 if self
.remove_ngons
and self
.fill_type
!= 'EDGE_NET':
307 ngons
= [face
for face
in bm
.faces
if len(face
.edges
) > 4]
309 bmesh
.ops
.delete(bm
, geom
=ngons
, context
=5) # 5 - delete faces
313 del_boundary
= [edge
for edge
in bm
.edges
if edge
.index
not in original_edges
]
319 if self
.fill_type
== 'EDGE_NET':
320 extrude_inner
= bmesh
.ops
.inset_individual(
321 bm
, faces
=bm
.faces
, thickness
=wid
, depth
=depth
,
322 use_even_offset
=True, use_interpolate
=False,
323 use_relative_offset
=False
326 extrude_inner
= bmesh
.ops
.inset_region(
327 bm
, faces
=bm
.faces
, faces_exclude
=[], use_boundary
=True,
328 use_even_offset
=True, use_interpolate
=False,
329 use_relative_offset
=False, use_edge_rail
=False,
330 thickness
=wid
, depth
=depth
, use_outset
=False
334 del_faces
= [faces
for faces
in bm
.faces
if faces
not in extrude_inner
["faces"]]
339 if not self
.keep_faces
:
340 bmesh
.ops
.delete(bm
, geom
=del_faces
, context
=5) # 5 delete faces
345 for face
in bm
.faces
:
346 for edge
in del_boundary
:
347 if isinstance(edge
, bmesh
.types
.BMEdge
):
348 if edge
in face
.edges
:
351 face_del
= list(face_del
)
357 if not self
.connect_ends
:
358 bmesh
.ops
.delete(bm
, geom
=face_del
, context
=5)
364 for edge
in bm
.edges
:
368 bm
= bmesh
.update_edit_mesh(ob
.data
, 1, 1)
370 context
.tool_settings
.mesh_select_mode
= store_selection_mode
376 bpy
.utils
.register_class(MESH_OT_edges_floor_plan
)
380 bpy
.utils
.unregister_class(MESH_OT_edges_floor_plan
)
383 if __name__
== "__main__":