This scripts orders the vertexes of selected planes in Blender, and exports the shape to dxf format.
import bpy
import numpy as np
import ezdxf
def cross_product(v1, v2):
return np.cross(v1, v2)
def sort_vertices_ccw(vertices):
# Calculate the geometric center of the polygon
center = np.mean(vertices, axis=0)
# Calculate the angle of each vertex relative to the geometric center
def angle_from_center(vertex):
vector = vertex - center
return np.arctan2(vector[1], vector[0]) # arctan2 to get the angle
# Sort vertices based on their angle from the geometric center
sorted_vertices = sorted(vertices, key=angle_from_center)
# Check the orientation using the cross product
v1 = sorted_vertices[1] - sorted_vertices[0]
v2 = sorted_vertices[2] - sorted_vertices[0]
cross_prod = cross_product(v1, v2)
# If the normal is oriented negatively (on z-axis), it means the vertices are clockwise
if cross_prod[2] < 0:
sorted_vertices.reverse()
return sorted_vertices
def add_aligned_dimension(msp, start, end, text_height=0.25, decimals=2):
# Calculate the midpoint for placing the dimension text
midpoint = [(s + e) / 2 for s, e in zip(start, end)]
# Offset for the dimension line
offset = np.array([0.2, 0.2])
dim = msp.add_linear_dim(
base=start,
p1=start,
p2=end,
location=(midpoint[0] + offset[0], midpoint[1] + offset[1]),
)
# Set text height and decimal precision
dim.dxf.dimtxt = text_height
dim.dxf.dimdec = decimals
dim.render() # Render the dimension to the modelspace
def export_to_dxf(vertices_list, filename):
# Create a new DXF document
doc = ezdxf.new(dxfversion='R2010')
msp = doc.modelspace()
for vertices in vertices_list:
# Convert to 2D by discarding the Z-coordinate, but retain the actual 3D location
vertices_2d = [(v[0], v[1], v[2]) for v in vertices]
# Add the LWPolyline (Lightweight Polyline) to the DXF
polyline = msp.add_lwpolyline([(v[0], v[1]) for v in vertices_2d])
polyline.closed = True # Close the polyline
polyline.dxf.lineweight = 35 # Line weight in hundredths of a millimeter
# Add a hatch with diagonal pattern
hatch = msp.add_hatch(color=7) # Color 7 (white/black depending on background)
hatch.paths.add_polyline_path([(v[0], v[1]) for v in vertices_2d], is_closed=True)
hatch.set_pattern_fill("ANSI31", scale=0.1, angle=0) # Set hatch scale to 0.2
# Add aligned dimensions for each edge of the polygon (i to i+1)
for i in range(len(vertices_2d)):
start = vertices_2d[i]
end = vertices_2d[(i + 1) % len(vertices_2d)] # Loop back to the first vertex
dim = msp.add_aligned_dim(p1=start, p2=end, distance=-1,
override={
"dimtxsty": "Standard",
"dimtxt": 0.35,
"dimclrt": 1,
"dimtsz": 0, # set tick size to 0 to enable arrow usage
"dimasz": 0.25, # arrow size in drawing units
}
).render()
# Save the DXF file
doc.saveas(filename)
def main():
# Get selected objects (assuming they are planar meshes)
selected_objects = [obj for obj in bpy.context.selected_objects if obj.type == 'MESH']
if not selected_objects:
print("No planar mesh objects selected.")
return
vertices_list = []
for obj in selected_objects:
mesh = obj.data
# Transform the vertex coordinates to world space
world_matrix = obj.matrix_world
if len(mesh.polygons) > 0:
# Get vertices of the first polygon (face) in world coordinates
vertices = np.array([world_matrix @ mesh.vertices[vert_idx].co for vert_idx in mesh.polygons[0].vertices])
sorted_vertices = sort_vertices_ccw(vertices)
vertices_list.append(sorted_vertices)
# Export to DXF
export_to_dxf(vertices_list, "C:\#BIMChess\output.dxf")
print("Export completed successfully.")
# Run the main function
main()