Code: Select all
# Creates a curved stair handrail with 180 degree turn
#
from FreeCAD import Vector, Placement
import Draft
import Part
import math
turn_radius = 250
# line of first straight part of bannister
straight1_pnts = [ Vector (0, 0, 0),
Vector (1000, 0, 1000),
]
# line of second straight part of bannister after curved turn
straight2_pnts = [ straight1_pnts[1] + Vector (0, turn_radius, 1200),
straight1_pnts[0] + Vector (0, turn_radius, 1200+1000+1000),
]
# We will create the curved turn using a Bezier curve
# control points for the Bezier curve connecting these straight
# parts. These are points creates on lines parallel to the
# straight parts of the bannister
ctrl_pnt_1 = straight1_pnts[1] + (straight1_pnts[1] - straight1_pnts[0]).normalize().multiply (turn_radius)
ctrl_pnt_2 = straight2_pnts[0] + (straight2_pnts[0] - straight2_pnts[1]).normalize().multiply (turn_radius)
# add the points just to show their locations
#Draft.makePoint (ctrl_pnt_1)
#Draft.makePoint (ctrl_pnt_2)
# make the list of points for the bezier curve
turn1_pnts = [ straight1_pnts[1],
ctrl_pnt_1,
ctrl_pnt_2,
straight2_pnts[0],
]
straight1 = Part.makeLine ((straight1_pnts[0].x, straight1_pnts[0].y, straight1_pnts[0].z),
(straight1_pnts[1].x, straight1_pnts[1].y, straight1_pnts[1].z))
straight2 = Part.makeLine ((straight2_pnts[0].x, straight2_pnts[0].y, straight2_pnts[0].z),
(straight2_pnts[1].x, straight2_pnts[1].y, straight2_pnts[1].z))
curve1 = Draft.makeBezCurve(turn1_pnts)
sweepPath = Part.Wire ([straight1, curve1.Shape, straight2])
Part.show (sweepPath)
# create handrail profile, draw it on the YZ plane, it is then
# rotated to align with the first straight part of the hand rail
# rectangular profile
# --------------------------------------------------------------
#h = 50
#w = 100
#profile_pnts = [ Vector (0,0,0),
# Vector (0,w,0),
# Vector (0,w,h),
# Vector (0,0,h),
# Vector (0,0,0),
# ]
# create the profile face
#profile = Part.Face (Part.makePolygon (profile_pnts))
# triangle profile
# --------------------------------------------------------------
#h = 50
#w = 100
#profile_pnts = [ Vector (0,0,0),
# Vector (0,-w/2.0,h),
# Vector (0,w/2.0,h),
# Vector (0,0,0),
# ]
# create the profile face
#profile = Part.Face (Part.makePolygon (profile_pnts))
# curved profile
# --------------------------------------------------------------
w = 55.0
profile_pnts = [ Vector (0, -w/2, 0),
Vector (0, w/2, 0),
]
curveangle = 45.0
curvepnts = [ profile_pnts[0],
Vector (0, -w*math.cos(math.radians(curveangle))+profile_pnts[0].y, w*math.sin(math.radians(curveangle))),
Vector (0, w*math.cos(math.radians(curveangle))+profile_pnts[1].y, w*math.sin(math.radians(curveangle))),
profile_pnts[1],
]
profcurve = Part.BezierCurve()
profcurve.setPoles (curvepnts)
profline = Part.makeLine ((profile_pnts[1].x, profile_pnts[1].y, profile_pnts[1].z) ,
(profile_pnts[0].x, profile_pnts[0].y, profile_pnts[0].z) )
profile = Part.Face (Part.Wire ([profcurve.toShape(), profline]))
# complex profile, not finished
# --------------------------------------------------------------
#h = 100.0
#w = 100.0
#profile_pnts = [ Vector (0, 0, 0),
# Vector (0, 0.9*w/2, 0),
# Vector (0, 0.9*w/2, -0.15*h),
# Vector (0, w/2, -0.15*h),
# Vector (0, w/2, 0.05*h),
# Vector (0, w/2, 0.1*h),
# Vector (0, w/2, 0.6*h),
# Vector (0, w/2, 0.95*h),
# Vector (0, w/2, h),
# # other side
# Vector (0, -w/2, h),
# Vector (0, -w/2, 0.95*h),
# Vector (0, -w/2, 0.6*h),
# Vector (0, -w/2, 0.1*h),
# Vector (0, -w/2, 0.05*h),
# Vector (0, -w/2, -0.15*h),
# Vector (0, -0.9*w/2, -0.15*h),
# Vector (0, -0.9*w/2, 0),
# Vector (0, 0, 0),
# ]
# create the profile face
#profile = Part.Face (Part.makePolygon (profile_pnts))
#Part.show(profile)
# Align the profile with bannister angle
# --------------------------------------------------------------
# make profile face normal vector align with bannister
# get vector pointing in direction of first straight part
straight1_vec = straight1_pnts[1] - straight1_pnts[0]
# get rotation axis. We want to rotate the profile around the
# vector orthogonal to it's face normal and the bannister
# direction vector
rotAxis = profile.normalAt(0,0).cross(straight1_vec.normalize())
# get rotation angle, angle between the bannister vector
# and the face normal vector
cosA = profile.normalAt(0,0).dot(straight1_vec.normalize())
profile.rotate ( profile.CenterOfMass,
rotAxis,
math.degrees (math.acos (cosA)) )
# now shift the profile so that its centre aligns with the
# start of the sweep path
profile.translate(straight1_pnts[0] - profile.CenterOfMass)
#Part.show(profile)
# Create the sweep of the handrail
# --------------------------------------------------------------
# We use the Part.BRepOffsetAPI.MakePipeShell so we can
# use the binormal mode which ensured
ps = Part.BRepOffsetAPI.MakePipeShell(sweepPath)
# set the binormal vector to point in the Z axis
ps.setBiNormalMode( Vector (0,0,1))
#for pro in Profiles:
# ps.add( pro, False, False) # add(shape, contact, rotate_ortho )
# supply the wire bounding the profile face
ps.add (profile.Wire)
# make the sweep
if ps.isReady():
ps.build()
ps.makeSolid() # optional
Part.show(ps.shape())
From side From above: From front: Some google keywords:
Victorian handrail, stair rail, tangent method, curved handrail