We spent a lot of the day trying to modify 3D models that we found online to work as a sign holder. Something like the bent metal plates you can buy at the tractor store. Since these are simple polygons, I thought it might be easier to script the build (plus making changes to the dimensions would just require tweaking variables).
Voila – hopefully it’s a T-post sign holder! It at least looks like one.

import bpy
import bmesh
import math
from mathutils import Vector
# -----------------------------
# Scene units (mm)
# -----------------------------
scene = bpy.context.scene
scene.unit_settings.system = 'METRIC'
scene.unit_settings.scale_length = 0.001 # 1 Blender unit = 1 mm
INCH = 25.4
def inch(x): return x * INCH
# -----------------------------
# PARAMETERS (mm)
# -----------------------------
thk = 10.0 # sheet thickness
width_x = 50.0 # bracket width (across the post)
# Leg lengths (side profile)
L_top = 15.0 # top flange length
L_web = 48.0 # drop/web height between bends
L_leg = 50.0 # long leg length (down the post)
# Bend included angles
bend1_included = 200.0 # top flange
bend2_included = 90.0 # web -> long leg
# If the long leg goes the wrong direction, flip this
flip_second_bend = False
# -----------------------------
# Punch hole on TOP FLANGE
# -----------------------------
do_punch = True
# T-post references
t_bar_w = inch(1.375) # crossbar width
t_bar_h = 12.0 # crossbar height (mm) <-- tune
t_stem_w = 12.0 # stem width (mm) <-- tune
t_stem_h = inch(1.625) # stem height
punch_clear = 1.0 # clearance added around each rectangle (mm)
# Position on top flange (Z from p0 end, and X across width)
punch_center_z = L_top * 0.55
punch_center_x = width_x * 0.50
# Vertical placement on top flange (Y=0 plane)
punch_center_y = 0.0
# -----------------------------
# Optional bevel to make edges look more formed
# -----------------------------
do_bevel = True
bevel_width = 0.6
bevel_segments = 2
# -----------------------------
# Cleanup
# -----------------------------
for n in ["BracketShape", "PunchBar", "PunchStem"]:
o = bpy.data.objects.get(n)
if o:
bpy.data.objects.remove(o, do_unlink=True)
# -----------------------------
# Helpers (YZ plane directions)
# Define 0° as +Z. +90° is +Y. -90° is -Y.
# -----------------------------
def unit_from_angle(deg_from_posZ):
a = math.radians(deg_from_posZ)
return Vector((0.0, math.sin(a), math.cos(a)))
def boolean_diff(target, cutter):
mod = target.modifiers.new(name=f"BOOL_{cutter.name}", type="BOOLEAN")
mod.operation = 'DIFFERENCE'
mod.solver = 'EXACT'
mod.object = cutter
bpy.context.view_layer.objects.active = target
bpy.ops.object.modifier_apply(modifier=mod.name)
cutter.hide_set(True)
def add_cube(name, size_xyz, location_xyz):
bpy.ops.mesh.primitive_cube_add(size=1, location=location_xyz)
obj = bpy.context.active_object
obj.name = name
obj.scale = (size_xyz[0]/2, size_xyz[1]/2, size_xyz[2]/2)
bpy.ops.object.transform_apply(scale=True)
return obj
# Convert included bend angles to turn angles
turn1 = 180.0 - bend1_included
turn2 = 180.0 - bend2_included
# Start along +Z (top flange)
theta0 = 0.0
d0 = unit_from_angle(theta0)
# After bend1, go "down" (toward -Y) by turning negative
theta1 = theta0 - turn1
d1 = unit_from_angle(theta1)
# After bend2, go toward +Z again (or flip if needed)
theta2 = theta1 + (turn2 if not flip_second_bend else -turn2)
d2 = unit_from_angle(theta2)
# Profile points (center surface)
p0 = Vector((0.0, 0.0, 0.0)) # free end of top flange
p1 = p0 + d0 * L_top # bend1 line
p2 = p1 + d1 * L_web # bend2 line
p3 = p2 + d2 * L_leg # end of long leg
# -----------------------------
# Build a single connected sheet surface:
# Create two polylines separated in X, then make quads between them.
# -----------------------------
mesh = bpy.data.meshes.new("BracketShapeMesh")
bracket = bpy.data.objects.new("BracketShape", mesh)
bpy.context.collection.objects.link(bracket)
bpy.context.view_layer.objects.active = bracket
bracket.select_set(True)
bm = bmesh.new()
x0, x1 = 0.0, width_x
# Left side (x0)
v0a = bm.verts.new((x0, p0.y, p0.z))
v1a = bm.verts.new((x0, p1.y, p1.z))
v2a = bm.verts.new((x0, p2.y, p2.z))
v3a = bm.verts.new((x0, p3.y, p3.z))
# Right side (x1)
v0b = bm.verts.new((x1, p0.y, p0.z))
v1b = bm.verts.new((x1, p1.y, p1.z))
v2b = bm.verts.new((x1, p2.y, p2.z))
v3b = bm.verts.new((x1, p3.y, p3.z))
# Faces (one per segment)
bm.faces.new((v0a, v0b, v1b, v1a)) # top flange
bm.faces.new((v1a, v1b, v2b, v2a)) # web
bm.faces.new((v2a, v2b, v3b, v3a)) # long leg
bm.normal_update()
bm.to_mesh(mesh)
bm.free()
# -----------------------------
# Solidify to thickness (sheet metal look)
# -----------------------------
solid = bracket.modifiers.new("Solidify", type="SOLIDIFY")
solid.thickness = thk
solid.offset = 0.0
bpy.ops.object.modifier_apply(modifier=solid.name)
# -----------------------------
# Punch the lowercase "t" on the top flange
# (Top flange is flat at Y=0; punch straight through Y)
# -----------------------------
if do_punch:
cutter_depth_y = thk * 6.0 # ensure it fully cuts through
# Crossbar rectangle
bar = add_cube(
"PunchBar",
size_xyz=(t_bar_w + 2*punch_clear, cutter_depth_y, t_bar_h + 2*punch_clear),
location_xyz=(punch_center_x, punch_center_y, punch_center_z)
)
# Stem rectangle (placed under the bar like a lowercase "t")
stem_center_z = punch_center_z - (t_bar_h*0.35) - (t_stem_h*0.5)
stem = add_cube(
"PunchStem",
size_xyz=(t_stem_w + 2*punch_clear, cutter_depth_y, t_stem_h + 2*punch_clear),
location_xyz=(punch_center_x, punch_center_y, stem_center_z)
)
#boolean_diff(bracket, bar)
#boolean_diff(bracket, stem)
# -----------------------------
# Optional bevel
# -----------------------------
if do_bevel:
bev = bracket.modifiers.new("Bevel", type="BEVEL")
bev.width = bevel_width
bev.segments = bevel_segments
bev.limit_method = 'ANGLE'
bev.angle_limit = math.radians(35)
bpy.context.view_layer.objects.active = bracket
bpy.ops.object.modifier_apply(modifier=bev.name)












