We were playing around with bevels this week – it’s pretty straight forward, the API lets you set the parameters you set through the GUI in a bevel modifier.
import bpy
# Clear all existing objects
for obj in list(bpy.data.objects):
bpy.data.objects.remove(obj, do_unlink=True)
# Set Units
scene = bpy.context.scene
scene.unit_settings.system = 'METRIC'
scene.unit_settings.scale_length = 0.001 # 1 BU = 1 mm
# Create rectangular cube
bpy.ops.mesh.primitive_cube_add(location=(0, 0, 0))
block = bpy.context.active_object
block.name = "Block"
# cube default size is 2x2x2, so set absolute dimensions
block.dimensions = (2.0, 20.0, 0.25)
bpy.context.view_layer.objects.active = block
block.select_set(True)
# Apply scale so booleans/bevel behave predictably
bpy.ops.object.transform_apply(location=False, rotation=False, scale=True)
# Create cylinder cutter
hole_diameter = 1.0
hole_radius = hole_diameter / 2.0
# Make it longer than the block thickness so it fully cuts through
cutter_depth = 5.0
bpy.ops.mesh.primitive_cylinder_add(
vertices=64,
radius=hole_radius,
depth=cutter_depth,
location=(0.0, 0.0, 0.0), # center of the block
rotation=(0.0, 0.0, 0.0)
)
cutter = bpy.context.active_object
cutter.name = "HoleCutter"
bpy.ops.object.transform_apply(location=False, rotation=False, scale=True)
# Boolean: cut hole
bpy.context.view_layer.objects.active = block
bool_mod = block.modifiers.new(name="Hole", type='BOOLEAN')
bool_mod.operation = 'DIFFERENCE'
bool_mod.solver = 'EXACT'
bool_mod.object = cutter
# Apply boolean
bpy.ops.object.modifier_apply(modifier=bool_mod.name)
# Hide cutter in viewport + renders
cutter.hide_set(True)
cutter.hide_render = True
# Bevel the block
bevel_width = 0.08
bevel_segments = 5
bevel_mod = block.modifiers.new(name="Bevel", type='BEVEL')
bevel_mod.width = bevel_width
bevel_mod.segments = bevel_segments
bevel_mod.limit_method = 'ANGLE'
bevel_mod.angle_limit = 0.523599 # 30 degrees in radians
# Apply bevel
bpy.ops.object.modifier_apply(modifier=bevel_mod.name)
2 cups gluten free flour (King Arthur Flour’s “Measure-for-measure”)
1 Tbsp baking powder
1 tsp salt
1/2 cup buttermillk powder + 2 cups water (or 2 cups of buttermilk!)
2 eggs
1 tsp psyllium husk fiber (finely ground powder)
Method:
Combine all dry ingredients. Mix in wet ingredients. Allow batter to sit for about ten minutes and add more water as needed – the gluten free flour absorbs a LOT of water. Then cook pancakes.
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)
We had huge wind storms today – 85mph up at the airport. It was gusty all day, but it really picked up in the afternoon. I was working and heard wind noise. I looked out the window and saw a wall of brown that I realized was all of the dead needles blowing off the pine trees along the south side of our back yard. Then the trees all started to lay down – the willow first, but then the pines started reaching for the ground. One popped and flopped into the grass as the gust subsided and all of the other trees stood up.
In case anyone else ever needs to set a windows dynamic port range for magic RPC “stuff” — there’s a minimum range size of 255. If you make the range to small, you get an incredibly vague and not-useful “the parameter is incorrect” error. Increase num to at least the min value, and you don’t be going in circles trying to figure out what in your command doesn’t match the parameters in the documentation!
We’re having another one of those days where it goes from 70F to 20F over the course of a day – so we’ve got about 10 days of sap coming up. Washed and replaced all of the existing buckets, and tapped all of the maple trees.
import bpy
# Clear all existing objects
for obj in list(bpy.data.objects):
bpy.data.objects.remove(obj, do_unlink=True)
# Set Units
scene = bpy.context.scene
scene.unit_settings.system = 'METRIC'
scene.unit_settings.scale_length = 0.001 # 1 BU = 1 mm
# Create cylinder
bpy.ops.mesh.primitive_cylinder_add(
vertices=32, radius=10.0, depth=20.0,
end_fill_type='NGON', calc_uvs=True,
enter_editmode=False, align='WORLD',
location=(0.0, 0.0, -2.0), rotation=(0.0, 0.0, 0.0),
scale=(1, 1, 1)
)
# Name cylinder
obj = bpy.context.active_object
obj.name = "MyCylinder"
# Frame Selected
for area in bpy.context.window.screen.areas:
if area.type == 'VIEW_3D':
for region in area.regions:
if region.type == 'WINDOW':
with bpy.context.temp_override(area=area, region=region):
bpy.ops.view3d.view_selected(use_all_regions=False)
break
break