1 # SPDX-FileCopyrightText: 2019-2022 Blender Foundation
3 # SPDX-License-Identifier: GPL-2.0-or-later
5 # based upon the functionality of Mesh to wall by luxuy_BlenderCN
6 # thanks to meta-androcto
9 "name": "Edge Floor Plan",
10 "author": "lijenstina",
12 "blender": (2, 78, 0),
13 "location": "View3D > EditMode > Mesh",
14 "description": "Make a Floor Plan from Edges",
21 from bpy
.types
import Operator
22 from bpy
.props
import (
31 # Handle error notifications
32 def error_handlers(self
, error
, reports
="ERROR"):
34 self
.report({'WARNING'}, reports
+ " (See Console for more info)")
36 print("\n[mesh.edges_floor_plan]\nError: {}\n".format(error
))
39 class MESH_OT_edges_floor_plan(Operator
):
40 bl_idname
= "mesh.edges_floor_plan"
41 bl_label
= "Edges Floor Plan"
42 bl_description
= "Top View, Extrude Flat Along Edges"
43 bl_options
= {'REGISTER', 'UNDO'}
47 description
="Set the width of the generated walls\n",
53 description
="Set the height of the inner wall edges",
57 connect_ends
: BoolProperty(
59 description
="Connect the ends of the boundary Edge loops",
62 repeat_cleanup
: IntProperty(
63 name
="Recursive Prepare",
64 description
="Number of times that the preparation phase runs\n"
65 "at the start of the script\n"
66 "If parts of the mesh are not modified, increase this value",
71 ('EDGE_NET', "Edge Net",
72 "Edge Net Method for mesh preparation - Initial Fill\n"
73 "The filled in faces will be Inset individually\n"
74 "Supports simple 3D objects"),
75 ('SINGLE_FACE', "Single Face",
76 "Single Face Method for mesh preparation - Initial Fill\n"
77 "The produced face will be Triangulated before Inset Region\n"
78 "Good for edges forming a circle, avoid 3D objects"),
79 ('SOLIDIFY', "Solidify",
80 "Extrude and Solidify Method\n"
81 "Useful for complex meshes, however works best on flat surfaces\n"
82 "as the extrude direction has to be defined")
84 fill_type
: EnumProperty(
87 description
="Choose the method for creating geometry",
90 keep_faces
: BoolProperty(
92 description
="Keep or not the fill faces\n"
93 "Can depend on Remove Ngons state",
96 tri_faces
: BoolProperty(
97 name
="Triangulate Faces",
98 description
="Triangulate the created fill faces\n"
99 "Sometimes can lead to unsatisfactory results",
102 initial_extrude
: FloatVectorProperty(
103 name
="Initial Extrude",
105 default
=(0.0, 0.0, 0.1),
111 remove_ngons
: BoolProperty(
113 description
="Keep or not the Ngon Faces\n"
114 "Note about limitations:\n"
115 "Sometimes the kept Faces could be Ngons\n"
116 "Removing the Ngons can lead to no geometry created",
119 offset
: FloatProperty(
121 description
="Set the offset for the Solidify modifier",
125 only_rim
: BoolProperty(
127 description
="Solidify Fill Rim only option",
132 def poll(cls
, context
):
133 ob
= context
.active_object
134 return (ob
and ob
.type == 'MESH' and context
.mode
== 'EDIT_MESH')
136 def check_edge(self
, context
):
137 bpy
.ops
.object.mode_set(mode
='OBJECT')
138 bpy
.ops
.object.mode_set(mode
='EDIT')
139 obj
= bpy
.context
.object
141 if len(me_check
.edges
) < 1:
149 bm
.verts
.ensure_lookup_table()
150 bm
.edges
.ensure_lookup_table()
151 bm
.faces
.ensure_lookup_table()
153 def solidify_mod(self
, context
, ob
, wid
, offset
, only_rim
):
155 mods
= ob
.modifiers
.new(
156 name
="_Mesh_Solidify_Wall", type='SOLIDIFY'
159 mods
.use_quality_normals
= True
161 mods
.use_even_offset
= True
163 mods
.use_rim_only
= only_rim
164 mods
.show_on_cage
= True
166 bpy
.ops
.object.modifier_apply(
167 modifier
="_Mesh_Solidify_Wall"
169 except Exception as e
:
170 error_handlers(self
, e
,
171 reports
="Adding a Solidify Modifier failed")
174 def draw(self
, context
):
178 box
.label(text
="Choose Method:", icon
="NONE")
179 box
.prop(self
, "fill_type")
181 col
= box
.column(align
=True)
183 if self
.fill_type
== 'EDGE_NET':
184 col
.prop(self
, "repeat_cleanup")
185 col
.prop(self
, "remove_ngons", toggle
=True)
187 elif self
.fill_type
== 'SOLIDIFY':
188 col
.prop(self
, "offset", slider
=True)
189 col
.prop(self
, "initial_extrude")
192 col
.prop(self
, "remove_ngons", toggle
=True)
193 col
.prop(self
, "tri_faces", toggle
=True)
196 box
.label(text
="Settings:", icon
="NONE")
198 col
= box
.column(align
=True)
199 col
.prop(self
, "wid")
201 if self
.fill_type
!= 'SOLIDIFY':
202 col
.prop(self
, "depth")
203 col
.prop(self
, "connect_ends", toggle
=True)
204 col
.prop(self
, "keep_faces", toggle
=True)
206 col
.prop(self
, "only_rim", toggle
=True)
208 def execute(self
, context
):
209 if not self
.check_edge(context
):
210 self
.report({'WARNING'},
211 "Operation Cancelled. Needs a Mesh with at least one edge")
215 depth
= self
.depth
* 0.1
216 offset
= self
.offset
* 0.1
217 store_selection_mode
= context
.tool_settings
.mesh_select_mode
218 # Note: the remove_doubles called after bmesh creation would make
219 # blender crash with certain meshes - keep it in mind for the future
220 bpy
.ops
.mesh
.remove_doubles(threshold
=0.003)
221 bpy
.ops
.object.mode_set(mode
='OBJECT')
222 bpy
.ops
.object.mode_set(mode
='EDIT')
223 ob
= bpy
.context
.object
226 bm
= bmesh
.from_edit_mesh(me
)
228 bmesh
.ops
.delete(bm
, geom
=bm
.faces
, context
='FACES_ONLY')
230 context
.tool_settings
.mesh_select_mode
= (False, True, False)
231 original_edges
= [edge
.index
for edge
in bm
.edges
]
232 original_verts
= [vert
.index
for vert
in bm
.verts
]
234 bpy
.ops
.mesh
.select_all(action
='DESELECT')
236 if self
.fill_type
== 'EDGE_NET':
237 for i
in range(self
.repeat_cleanup
):
238 bmesh
.ops
.edgenet_prepare(bm
, edges
=bm
.edges
)
240 bmesh
.ops
.edgenet_fill(bm
, edges
=bm
.edges
, mat_nr
=0, use_smooth
=True, sides
=0)
242 if self
.remove_ngons
:
243 ngons
= [face
for face
in bm
.faces
if len(face
.edges
) > 4]
245 bmesh
.ops
.delete(bm
, geom
=ngons
, context
='FACES') # 5 - delete faces
249 elif self
.fill_type
== 'SOLIDIFY':
250 for vert
in bm
.verts
:
253 bmesh
.ops
.extrude_edge_only(
254 bm
, edges
=bm
.edges
, use_select_history
=False
257 verts_extrude
= [vert
for vert
in bm
.verts
if vert
.index
in original_verts
]
262 vec
=(self
.initial_extrude
)
268 for edge
in bm
.edges
:
272 bm
= bmesh
.update_edit_mesh(ob
.data
, loop_triangles
=True, destructive
=True)
274 bpy
.ops
.object.mode_set(mode
='OBJECT')
275 self
.solidify_mod(context
, ob
, wid
, offset
, self
.only_rim
)
277 bpy
.ops
.object.mode_set(mode
='EDIT')
279 context
.tool_settings
.mesh_select_mode
= store_selection_mode
284 bm
.faces
.new(bm
.verts
)
288 bmesh
.ops
.triangle_fill(
289 bm
, use_beauty
=True, use_dissolve
=False, edges
=bm
.edges
293 if self
.remove_ngons
and self
.fill_type
!= 'EDGE_NET':
294 ngons
= [face
for face
in bm
.faces
if len(face
.edges
) > 4]
296 bmesh
.ops
.delete(bm
, geom
=ngons
, context
='FACES') # 5 - delete faces
300 del_boundary
= [edge
for edge
in bm
.edges
if edge
.index
not in original_edges
]
306 if self
.fill_type
== 'EDGE_NET':
307 extrude_inner
= bmesh
.ops
.inset_individual(
308 bm
, faces
=bm
.faces
, thickness
=wid
, depth
=depth
,
309 use_even_offset
=True, use_interpolate
=False,
310 use_relative_offset
=False
313 extrude_inner
= bmesh
.ops
.inset_region(
314 bm
, faces
=bm
.faces
, faces_exclude
=[], use_boundary
=True,
315 use_even_offset
=True, use_interpolate
=False,
316 use_relative_offset
=False, use_edge_rail
=False,
317 thickness
=wid
, depth
=depth
, use_outset
=False
321 del_faces
= [faces
for faces
in bm
.faces
if faces
not in extrude_inner
["faces"]]
326 if not self
.keep_faces
:
327 bmesh
.ops
.delete(bm
, geom
=del_faces
, context
='FACES') # 5 delete faces
332 for face
in bm
.faces
:
333 for edge
in del_boundary
:
334 if isinstance(edge
, bmesh
.types
.BMEdge
):
335 if edge
in face
.edges
:
338 face_del
= list(face_del
)
344 if not self
.connect_ends
:
345 bmesh
.ops
.delete(bm
, geom
=face_del
, context
='FACES')
351 for edge
in bm
.edges
:
355 bm
= bmesh
.update_edit_mesh(ob
.data
, loop_triangles
=True, destructive
=True)
357 context
.tool_settings
.mesh_select_mode
= store_selection_mode
363 bpy
.utils
.register_class(MESH_OT_edges_floor_plan
)
367 bpy
.utils
.unregister_class(MESH_OT_edges_floor_plan
)
370 if __name__
== "__main__":