1 # SPDX-License-Identifier: GPL-2.0-or-later
3 # based upon the functionality of Mesh to wall by luxuy_BlenderCN
4 # thanks to meta-androcto
7 "name": "Edge Floor Plan",
8 "author": "lijenstina",
10 "blender": (2, 78, 0),
11 "location": "View3D > EditMode > Mesh",
12 "description": "Make a Floor Plan from Edges",
19 from bpy
.types
import Operator
20 from bpy
.props
import (
29 # Handle error notifications
30 def error_handlers(self
, error
, reports
="ERROR"):
32 self
.report({'WARNING'}, reports
+ " (See Console for more info)")
34 print("\n[mesh.edges_floor_plan]\nError: {}\n".format(error
))
37 class MESH_OT_edges_floor_plan(Operator
):
38 bl_idname
= "mesh.edges_floor_plan"
39 bl_label
= "Edges Floor Plan"
40 bl_description
= "Top View, Extrude Flat Along Edges"
41 bl_options
= {'REGISTER', 'UNDO'}
45 description
="Set the width of the generated walls\n",
51 description
="Set the height of the inner wall edges",
55 connect_ends
: BoolProperty(
57 description
="Connect the ends of the boundary Edge loops",
60 repeat_cleanup
: IntProperty(
61 name
="Recursive Prepare",
62 description
="Number of times that the preparation phase runs\n"
63 "at the start of the script\n"
64 "If parts of the mesh are not modified, increase this value",
69 ('EDGE_NET', "Edge Net",
70 "Edge Net Method for mesh preparation - Initial Fill\n"
71 "The filled in faces will be Inset individually\n"
72 "Supports simple 3D objects"),
73 ('SINGLE_FACE', "Single Face",
74 "Single Face Method for mesh preparation - Initial Fill\n"
75 "The produced face will be Triangulated before Inset Region\n"
76 "Good for edges forming a circle, avoid 3D objects"),
77 ('SOLIDIFY', "Solidify",
78 "Extrude and Solidify Method\n"
79 "Useful for complex meshes, however works best on flat surfaces\n"
80 "as the extrude direction has to be defined")
82 fill_type
: EnumProperty(
85 description
="Choose the method for creating geometry",
88 keep_faces
: BoolProperty(
90 description
="Keep or not the fill faces\n"
91 "Can depend on Remove Ngons state",
94 tri_faces
: BoolProperty(
95 name
="Triangulate Faces",
96 description
="Triangulate the created fill faces\n"
97 "Sometimes can lead to unsatisfactory results",
100 initial_extrude
: FloatVectorProperty(
101 name
="Initial Extrude",
103 default
=(0.0, 0.0, 0.1),
109 remove_ngons
: BoolProperty(
111 description
="Keep or not the Ngon Faces\n"
112 "Note about limitations:\n"
113 "Sometimes the kept Faces could be Ngons\n"
114 "Removing the Ngons can lead to no geometry created",
117 offset
: FloatProperty(
119 description
="Set the offset for the Solidify modifier",
123 only_rim
: BoolProperty(
125 description
="Solidify Fill Rim only option",
130 def poll(cls
, context
):
131 ob
= context
.active_object
132 return (ob
and ob
.type == 'MESH' and context
.mode
== 'EDIT_MESH')
134 def check_edge(self
, context
):
135 bpy
.ops
.object.mode_set(mode
='OBJECT')
136 bpy
.ops
.object.mode_set(mode
='EDIT')
137 obj
= bpy
.context
.object
139 if len(me_check
.edges
) < 1:
147 bm
.verts
.ensure_lookup_table()
148 bm
.edges
.ensure_lookup_table()
149 bm
.faces
.ensure_lookup_table()
151 def solidify_mod(self
, context
, ob
, wid
, offset
, only_rim
):
153 mods
= ob
.modifiers
.new(
154 name
="_Mesh_Solidify_Wall", type='SOLIDIFY'
157 mods
.use_quality_normals
= True
159 mods
.use_even_offset
= True
161 mods
.use_rim_only
= only_rim
162 mods
.show_on_cage
= True
164 bpy
.ops
.object.modifier_apply(
165 modifier
="_Mesh_Solidify_Wall"
167 except Exception as e
:
168 error_handlers(self
, e
,
169 reports
="Adding a Solidify Modifier failed")
172 def draw(self
, context
):
176 box
.label(text
="Choose Method:", icon
="NONE")
177 box
.prop(self
, "fill_type")
179 col
= box
.column(align
=True)
181 if self
.fill_type
== 'EDGE_NET':
182 col
.prop(self
, "repeat_cleanup")
183 col
.prop(self
, "remove_ngons", toggle
=True)
185 elif self
.fill_type
== 'SOLIDIFY':
186 col
.prop(self
, "offset", slider
=True)
187 col
.prop(self
, "initial_extrude")
190 col
.prop(self
, "remove_ngons", toggle
=True)
191 col
.prop(self
, "tri_faces", toggle
=True)
194 box
.label(text
="Settings:", icon
="NONE")
196 col
= box
.column(align
=True)
197 col
.prop(self
, "wid")
199 if self
.fill_type
!= 'SOLIDIFY':
200 col
.prop(self
, "depth")
201 col
.prop(self
, "connect_ends", toggle
=True)
202 col
.prop(self
, "keep_faces", toggle
=True)
204 col
.prop(self
, "only_rim", toggle
=True)
206 def execute(self
, context
):
207 if not self
.check_edge(context
):
208 self
.report({'WARNING'},
209 "Operation Cancelled. Needs a Mesh with at least one edge")
213 depth
= self
.depth
* 0.1
214 offset
= self
.offset
* 0.1
215 store_selection_mode
= context
.tool_settings
.mesh_select_mode
216 # Note: the remove_doubles called after bmesh creation would make
217 # blender crash with certain meshes - keep it in mind for the future
218 bpy
.ops
.mesh
.remove_doubles(threshold
=0.003)
219 bpy
.ops
.object.mode_set(mode
='OBJECT')
220 bpy
.ops
.object.mode_set(mode
='EDIT')
221 ob
= bpy
.context
.object
224 bm
= bmesh
.from_edit_mesh(me
)
226 bmesh
.ops
.delete(bm
, geom
=bm
.faces
, context
='FACES_ONLY')
228 context
.tool_settings
.mesh_select_mode
= (False, True, False)
229 original_edges
= [edge
.index
for edge
in bm
.edges
]
230 original_verts
= [vert
.index
for vert
in bm
.verts
]
232 bpy
.ops
.mesh
.select_all(action
='DESELECT')
234 if self
.fill_type
== 'EDGE_NET':
235 for i
in range(self
.repeat_cleanup
):
236 bmesh
.ops
.edgenet_prepare(bm
, edges
=bm
.edges
)
238 bmesh
.ops
.edgenet_fill(bm
, edges
=bm
.edges
, mat_nr
=0, use_smooth
=True, sides
=0)
240 if self
.remove_ngons
:
241 ngons
= [face
for face
in bm
.faces
if len(face
.edges
) > 4]
243 bmesh
.ops
.delete(bm
, geom
=ngons
, context
='FACES') # 5 - delete faces
247 elif self
.fill_type
== 'SOLIDIFY':
248 for vert
in bm
.verts
:
251 bmesh
.ops
.extrude_edge_only(
252 bm
, edges
=bm
.edges
, use_select_history
=False
255 verts_extrude
= [vert
for vert
in bm
.verts
if vert
.index
in original_verts
]
260 vec
=(self
.initial_extrude
)
266 for edge
in bm
.edges
:
270 bm
= bmesh
.update_edit_mesh(ob
.data
, loop_triangles
=True, destructive
=True)
272 bpy
.ops
.object.mode_set(mode
='OBJECT')
273 self
.solidify_mod(context
, ob
, wid
, offset
, self
.only_rim
)
275 bpy
.ops
.object.mode_set(mode
='EDIT')
277 context
.tool_settings
.mesh_select_mode
= store_selection_mode
282 bm
.faces
.new(bm
.verts
)
286 bmesh
.ops
.triangle_fill(
287 bm
, use_beauty
=True, use_dissolve
=False, edges
=bm
.edges
291 if self
.remove_ngons
and self
.fill_type
!= 'EDGE_NET':
292 ngons
= [face
for face
in bm
.faces
if len(face
.edges
) > 4]
294 bmesh
.ops
.delete(bm
, geom
=ngons
, context
='FACES') # 5 - delete faces
298 del_boundary
= [edge
for edge
in bm
.edges
if edge
.index
not in original_edges
]
304 if self
.fill_type
== 'EDGE_NET':
305 extrude_inner
= bmesh
.ops
.inset_individual(
306 bm
, faces
=bm
.faces
, thickness
=wid
, depth
=depth
,
307 use_even_offset
=True, use_interpolate
=False,
308 use_relative_offset
=False
311 extrude_inner
= bmesh
.ops
.inset_region(
312 bm
, faces
=bm
.faces
, faces_exclude
=[], use_boundary
=True,
313 use_even_offset
=True, use_interpolate
=False,
314 use_relative_offset
=False, use_edge_rail
=False,
315 thickness
=wid
, depth
=depth
, use_outset
=False
319 del_faces
= [faces
for faces
in bm
.faces
if faces
not in extrude_inner
["faces"]]
324 if not self
.keep_faces
:
325 bmesh
.ops
.delete(bm
, geom
=del_faces
, context
='FACES') # 5 delete faces
330 for face
in bm
.faces
:
331 for edge
in del_boundary
:
332 if isinstance(edge
, bmesh
.types
.BMEdge
):
333 if edge
in face
.edges
:
336 face_del
= list(face_del
)
342 if not self
.connect_ends
:
343 bmesh
.ops
.delete(bm
, geom
=face_del
, context
='FACES')
349 for edge
in bm
.edges
:
353 bm
= bmesh
.update_edit_mesh(ob
.data
, loop_triangles
=True, destructive
=True)
355 context
.tool_settings
.mesh_select_mode
= store_selection_mode
361 bpy
.utils
.register_class(MESH_OT_edges_floor_plan
)
365 bpy
.utils
.unregister_class(MESH_OT_edges_floor_plan
)
368 if __name__
== "__main__":