## How to create a handrail scroll/volute shape based on fibbonacci?

Post here for help on using FreeCAD's graphical user interface (GUI).
Forum rules

crobar
Posts: 153
Joined: Fri Aug 29, 2014 1:26 pm

### How to create a handrail scroll/volute shape based on fibbonacci?

Hello,

I would like to create a scroll for a victorian style handrail, something like the picture below (don't know how to change the image size here?): Victorian handrail scroll
Scroll-example-Victorian-banister2013-10-15-12.21.jpg (38.63 KiB) Viewed 1423 times
More detailed description can be found at http://www.thisiscarpentry.com/2009/07/ ... -a-volute/  As a starter for 10 I have worked up a script which can create a fibbonacci (golden ratio) spiral:

Code: Select all

``````from math import sin, cos, pi, sqrt
import Part
import Draft

def fibonacci_spiral_connected ( n=101 ):
"""

FIBONACCI_SPIRAL_CONNECTED draws connected points on a Fibonacci spiral.

Licensing:

Modified:

07 April 2011

Author:

John Burkardt

Parameters:

Input, integer N, the number of points to plot.
Default is 101.
"""
#
#  PHI is the golden ratio, the limit of the ratio of
#  successive Fibonacci numbers:
#
#    PHI = limit ( N->oo ) F(N+1)/F(N)
#
phi = ( 1.0 + sqrt ( 5.0 ) ) / 2.0
#
#  Allocate storage for the point data.
#
#  x = zeros ( n, 1 )
#  y = zeros ( n, 1 )
#
#  Set the angle and radius of the first point.
#
a = 0.0;
r = 0.0;
#
#  Set the increments.
#
da = 2.0 * pi * ( phi - 1.0 ) / phi;
dr = 1.0;
#
#  Create a spiral in which the radius R and angle A both
#  increase by a constant increment,
#
x = []
y = []
for i in range (1,n+1):
x.append (r * cos ( a ));
y.append (r * sin ( a ));
a = (a + da) % (2 * pi);
r = r + dr;

#
#  SCALE controls how many steps we take between the actual points.
#  A value of 5 is enough to see the basic spiral that connects the points.
#  A vale of 10 would make a smoother spiral.
#
scale = 20.0;
#
#  Allocate storage for the intermediate data.
#
n2 = scale * ( n - 1 ) + 1;
#x2 = zeros ( n2, 1 );
#y2 = zeros ( n2, 1 );
#
#  Set the angle and radius of the first point.
#
a = 0.0;
r = 0.0;
#
#  Set the increments.
#
da = 2.0 * pi * ( phi - 1.0 ) / phi;
dr = 1.0;

da = da / scale;
dr = dr / scale;
#
#  Create a spiral in which the radius R and angle A both
#  increase by a constant increment,
#
#x2 = []
#y2 = []
pnts = []
for i in range (1,int(n2)+1):
pnts.append (Vector (r * cos ( a ), r * sin ( a ), 0))
#x2.append (r * cos ( a ));
#y2.append (r * sin ( a ));
a = ( a + da ) % ( 2 * pi );
r = r + dr;

#
#  Display the points,
#  and use the intermediate points to draw lines that display the spiral.
#

return pnts

pnts2 = fibonacci_spiral_connected  (10)

goldenspiral = Part.makePolygon (pnts2)

Part.show (goldenspiral)
``````
But I'm not sure what to do from here. Probably a sweep or loft?
Last edited by crobar on Wed Apr 26, 2017 9:27 pm, edited 2 times in total.
crobar
Posts: 153
Joined: Fri Aug 29, 2014 1:26 pm

### Re: How to create a handrail scroll/volute shape based on fibbonacci?

Made some progress with the attached script, but not sure where to go from here:

Code: Select all

``````
import math
import Part
import Draft

def fibonacci_spiral_connected ( n=101 ):
"""

FIBONACCI_SPIRAL_CONNECTED draws connected points on a Fibonacci spiral.

Licensing:

Modified:

07 April 2011

Author:

John Burkardt

Parameters:

Input, integer N, the number of points to plot.
Default is 101.
"""
#
#  PHI is the golden ratio, the limit of the ratio of
#  successive Fibonacci numbers:
#
#    PHI = limit ( N->oo ) F(N+1)/F(N)
#
phi = ( 1.0 + math.sqrt ( 5.0 ) ) / 2.0
#
#  Allocate storage for the point data.
#
#  x = zeros ( n, 1 )
#  y = zeros ( n, 1 )
#
#  Set the angle and radius of the first point.
#
a = 0.0;
r = 0.0;
#
#  Set the increments.
#
da = 2.0 * math.pi * ( phi - 1.0 ) / phi;
dr = 1.0;
#
#  Create a spiral in which the radius R and angle A both
#  increase by a constant increment,
#
#  x = []
#  y = []
#  for i in range (1,n+1):
#    x.append (r * cos ( a ));
#    y.append (r * sin ( a ));
#    a = (a + da) % (2 * pi);
#    r = r + dr;

#
#  SCALE controls how many steps we take between the actual points.
#  A value of 5 is enough to see the basic spiral that connects the points.
#  A vale of 10 would make a smoother spiral.
#
scale = 20.0;
#
#  Allocate storage for the intermediate data.
#
n2 = scale * ( n - 1 ) + 1;
#x2 = zeros ( n2, 1 );
#y2 = zeros ( n2, 1 );
#
#  Set the angle and radius of the first point.
#
a = 0.0;
r = 0.0;
#
#  Set the increments.
#
da = 2.0 * math.pi * ( phi - 1.0 ) / phi;
dr = 1.0;

da = da / scale;
dr = dr / scale;
#
#  Create a spiral in which the radius R and angle A both
#  increase by a constant increment,
#
#x2 = []
#y2 = []
pnts = []
for i in range (1,int(n2)+1):
pnts.append (Vector (r * math.cos ( a ), r * math.sin ( a ), 0))
#x2.append (r * cos ( a ));
#y2.append (r * sin ( a ));
a = ( a + da ) % ( 2 * math.pi );
r = r + dr;

#
#  Display the points,
#  and use the intermediate points to draw lines that display the spiral.
#

return pnts

#def makeWire (pnts):
#
#    ind = 0
#    edgelist = []
#
#    for p in pnts:
#
#        if ind > 0:
#            edgelist.append (Part.makeLine (pnts[ind-1], pnts[ind]))
#
#        nd = ind + 1
#
#    return Part.Wire (edgelist)

#n = 4 #eval(input())
#
#phi = (1+5**.5)/2
#
#w = int(phi**(4*(n//4)))
#
#k = pi/180
#
#pnts = [Vector (phi**(j/90)*cos(j*k)+w/2, phi**(j/90)*sin(j*k)+w/2, 0) for j in range(n*90)]

pnts2 = fibonacci_spiral_connected  (5)

#goldenspiral = Part.makePolygon (pnts2)

#gs2 = Part.Wire (goldenspiral.Edges)

Draft.makeBSpline (pnts2)

#Part.show (goldenspiral)

#Part.show (gs2)

# curved profile
# --------------------------------------------------------------
w = 2.0
profile_pnts = [ Vector (0, -w/2, 0),
Vector (0, w/2, 0),
]

curveangle = 45.0
curvepnts = [ profile_pnts,
profile_pnts,
]

profcurve = Part.BezierCurve()
profcurve.setPoles (curvepnts)
profline = Part.makeLine ((profile_pnts.x, profile_pnts.y, profile_pnts.z) ,
(profile_pnts.x, profile_pnts.y, profile_pnts.z) )

profile = Part.Face (Part.Wire ([profcurve.toShape(), profline]))

#profile.rotate ( profile.CenterOfMass,
#                 Vector (0,0,1),
#                 90.0 )

Part.show (profile)

``````
if I sweep the profile along the curve I get this: scroll_attempt_1.png (35.02 KiB) Viewed 1386 times
renatorivo
Posts: 2332
Joined: Tue Feb 21, 2012 8:07 pm
Location: Torino - Italy

### Re: How to create a handrail scroll/volute shape based on fibbonacci?

Fibonacci spiral from Sketch and Sweep fibo.png (31.6 KiB) Viewed 1376 times
fibonacci sweep.FCStd
Renato
hammax
Posts: 1006
Joined: Thu Jan 19, 2017 5:03 pm
Location: Ammersee

### Re: How to create a handrail scroll/volute shape based on fibbonacci?

...Fibonacci-Spiral placing the sweep-profiles with one side on the sweepingspiral.
It's mor like the snailshell. Only Problem is, there are the little curls at the inside of the shell,
because the fibonacci-spiral is merely an approach to the real logarithmic spiral. Fibonacchi_2.PNG (45.1 KiB) Viewed 1348 times
Last edited by hammax on Thu Apr 27, 2017 9:01 am, edited 1 time in total.
renatorivo
Posts: 2332
Joined: Tue Feb 21, 2012 8:07 pm
Location: Torino - Italy

### Re: How to create a handrail scroll/volute shape based on fibbonacci?

If I'm not mistaken, to have a flat side, it looks like this works best fibo1.png (34.43 KiB) Viewed 1328 times
hammax
Posts: 1006
Joined: Thu Jan 19, 2017 5:03 pm
Location: Ammersee

### Re: How to create a handrail scroll/volute shape based on fibbonacci?

...you can also do it with a conical Helix... HelixConical.PNG (81.29 KiB) Viewed 1320 times
microelly2
Posts: 4560
Joined: Tue Nov 12, 2013 4:06 pm
Contact:

### Re: How to create a handrail scroll/volute shape based on fibbonacci?

I would calculate more ribs and interpolate it all as a bspline surface

this is not the fibonacci curve but with the modification of the trafo and scaler pts -> pts1 ist should work too.

Code: Select all

``````

import Draft
import numpy as np
anz=12

r=50
#Draft.makeBSpline(pts)

pps=[]
for k in range(40):
pts1=[FreeCAD.Vector((-200+ p.x)*0.5**(k*1.0/anz),p.y*0.5**(k*1.0/anz),0) for p in pts]
pts2=[trafo.multiply(p).Base for p in ps]
pts2 += [pts2]

Draft.makeBSpline(pts2)
pps += [pts2]

bs=Part.BSplineSurface()
bs.interpolate(pps)
Part.show(bs.toShape())
``````
crobar
Posts: 153
Joined: Fri Aug 29, 2014 1:26 pm

### Re: How to create a handrail scroll/volute shape based on fibbonacci?

Thanks for the suggestions so far, the problem is that none of these crete the kind of shape needed, because the sweep actually needs to self-intersect and keep the profile shape. The closest I've come is the following script which makes it in parts and tries to fuse them at the end, but the fuse fails, or at least the resulting geometry has errors. I think this is an OCC limitation or bug.

Code: Select all

``````
import math
import Part
import Draft
import sys
sys.stdout.flush()

def fibonacci_spiral_connected ( n=101 ):
"""

FIBONACCI_SPIRAL_CONNECTED draws connected points on a Fibonacci spiral.

Licensing:

Modified:

07 April 2011

Author:

John Burkardt

Parameters:

Input, integer N, the number of points to plot.
Default is 101.
"""
#
#  PHI is the golden ratio, the limit of the ratio of
#  successive Fibonacci numbers:
#
#    PHI = limit ( N->oo ) F(N+1)/F(N)
#
phi = ( 1.0 + math.sqrt ( 5.0 ) ) / 2.0
#
#  Allocate storage for the point data.
#
#  x = zeros ( n, 1 )
#  y = zeros ( n, 1 )
#
#  Set the angle and radius of the first point.
#
#a = 0.0;
#r = 0.0;
#
#  Set the increments.
#
#da = 2.0 * math.pi * ( phi - 1.0 ) / phi;
#dr = 1.0;
#
#  Create a spiral in which the radius R and angle A both
#  increase by a constant increment,
#
#  x = []
#  y = []
#  for i in range (1,n+1):
#    x.append (r * cos ( a ));
#    y.append (r * sin ( a ));
#    a = (a + da) % (2 * pi);
#    r = r + dr;

#
#  SCALE controls how many steps we take between the actual points.
#  A value of 5 is enough to see the basic spiral that connects the points.
#  A vale of 10 would make a smoother spiral.
#
scale = 50.0;
#
#  Allocate storage for the intermediate data.
#
n2 = scale * ( n - 1 ) + 1;
#x2 = zeros ( n2, 1 );
#y2 = zeros ( n2, 1 );
#
#  Set the angle and radius of the first point.
#
a = 0.0;
r = 0.0;
#
#  Set the increments.
#
da = 2.0 * math.pi * ( phi - 1.0 ) / phi;
dr = 1.0;

da = da / scale;
dr = dr / scale;
#
#  Create a spiral in which the radius R and angle A both
#  increase by a constant increment,
#
#x2 = []
#y2 = []
pnts = []
for i in range (1,int(n2)+1):
pnts.append (Vector (r * math.cos ( a ), r * math.sin ( a ), 0))
#x2.append (r * cos ( a ));
#y2.append (r * sin ( a ));
a = ( a + da ) % ( 2 * math.pi );
r = r + dr;

#
#  Display the points,
#  and use the intermediate points to draw lines that display the spiral.
#

return pnts

def FibIter():
a,b = 0,1
yield a
yield b
while True:
a, b = b, a + b
yield b

#def makeWire (pnts):
#
#    ind = 0
#    edgelist = []
#
#    for p in pnts:
#
#        if ind > 0:
#            edgelist.append (Part.makeLine (pnts[ind-1], pnts[ind]))
#
#        nd = ind + 1
#
#    return Part.Wire (edgelist)

#n = 4 #eval(input())
#
#phi = (1+5**.5)/2
#
#profwidth = int(phi**(4*(n//4)))
#
#k = pi/180
#
#pnts = [Vector (phi**(j/90)*cos(j*k)+profwidth/2, phi**(j/90)*sin(j*k)+profwidth/2, 0) for j in range(n*90)]

nquarterturns = 5
pnts2 = fibonacci_spiral_connected  (nquarterturns)

#goldenspiral = Part.makePolygon (pnts2)

#gs2 = Part.Wire (goldenspiral.Edges)

sweeppath = Draft.makeBSpline (pnts2)

#Part.show (goldenspiral)

#Part.show (gs2)

# curved profile
# --------------------------------------------------------------
profwidth = 2.0
profile_pnts = [ Vector (0, -profwidth/2, 0),
Vector (0, profwidth/2, 0),
]

curveangle = 45.0
curvepnts = [ profile_pnts,
profile_pnts,
]

profcurve = Part.BezierCurve()
profcurve.setPoles (curvepnts)
profline = Part.makeLine ((profile_pnts.x, profile_pnts.y, profile_pnts.z) ,
(profile_pnts.x, profile_pnts.y, profile_pnts.z) )

profile = Part.Face (Part.Wire ([profcurve.toShape(), profline]))

#profile.rotate ( profile.CenterOfMass,
#                 Vector (0,0,1),
#                 90.0 )

Part.show (profile)
#
nsections = 5*nquarterturns
pntspersection = len (pnts2) / nsections

fib = FibIter ()
startind = None

fullsectioniterstart = 6
sectioncount = 0
finalpart = Part.makeCylinder (2.5, 0.6)
finalpart.translate(Vector (0,0,-0.55))

finalpart = finalpart.fuse (Part.makeCylinder (1.2, 0.65))
Part.show (finalpart)

while True:
#for n in range (nsections):

done = False

if startind is None:
startind = fib.next ()

thisprofile = profile.copy()

shiftpnt = thisprofile.CenterOfMass

if sectioncount < fullsectioniterstart:

# cut the profile in half

cutbox = Part.makeBox (profwidth, profwidth, 2.0*profwidth)

cutbox.translate (Vector (-profwidth/2.0, 0, -profwidth/2.0))

#Part.show (cutbox)
#Part.show (thisprofile)

thisprofile = thisprofile.cut (cutbox).Faces

#Part.show (thisprofile)

#break

endind = startind + 3*fib.next ()

print ('starting section from points {} to {} of {}'.format (startind, endind, len(pnts2)))

if endind > len(pnts2):

#sweeppath = Draft.makeBSpline (pnts2[startpnt : ])
sweeppath = Draft.makeBSpline (pnts2[(startind-2) : ])
done = True

else:

if startind == 0:
sweeppath = Draft.makeBSpline (pnts2[0 : endind])
else:
#sweeppath = Draft.makeBSpline (pnts2[n*pntspersection : (n+1)*pntspersection+1])
sweeppath = Draft.makeBSpline (pnts2[(startind-2) : endind])

pathtangent = sweeppath.Shape.tangentAt(0)

#startpnt = pnts2[n*pntspersection]
#startpnt = pnts2[(startind-3)]
startpnt = Vector (sweeppath.Shape.Vertexes.X,
sweeppath.Shape.Vertexes.Y,
sweeppath.Shape.Vertexes.Z )

# get rotation axis. We want to rotate the profile around the
# vector orthogonal to it's face normal and the sweep path
# tangent at the start of this sweep section
rotAxis = profile.normalAt(0,0).cross(pathtangent.normalize())
# get rotation angle, angle between the bannister vector
# and the face normal vector
cosA = profile.normalAt(0,0).dot(pathtangent.normalize())

thisprofile.rotate ( shiftpnt,
rotAxis,
math.degrees (math.acos (cosA)) )

# now shift the profile so that its centre aligns with the
# start of the sweep path
thisprofile.translate(startpnt - shiftpnt)

# We use the Part.BRepOffsetAPI.MakePipeShell so we can
# use the binormal mode which ensured
ps = Part.BRepOffsetAPI.MakePipeShell(Part.Wire (sweeppath.Shape))

# set the binormal vector to point in the Z axis
ps.setBiNormalMode( Vector (0,0,1))
#for pro in Profiles:

# supply the wire bounding the profile face

# make the sweep
ps.build()
x = ps.makeSolid() # optional

Part.show(ps.shape().Solids)

if finalpart is None:
finalpart = ps.shape().Solids
else:
finalpart = finalpart.fuse (ps.shape().Solids)

#break

#Part.show (finalpart)

if done:
break

#    else:
#
#        endind = startind + 3

# the next start point should be the end point of this section
startind = endind

sectioncount = sectioncount + 1

Part.show (finalpart)

``````
I've can't attache the resulting project file as it's too large, but here's a screenshot: scroll_attempt_2.png (88.49 KiB) Viewed 1287 times
microelly2
Posts: 4560
Joined: Tue Nov 12, 2013 4:06 pm
Contact:

### Re: How to create a handrail scroll/volute shape based on fibbonacci?

The self intersecting sweep is a problem and I think there is no way to solve this problem using the sweep.
My example can configured that you create only segments of the surface and then connect them with some helper faces.
It's a lot of work, but if you have 4 edges you can create with part tools a filling face.
chrisb
Posts: 21463
Joined: Tue Mar 17, 2015 9:14 am

### Re: How to create a handrail scroll/volute shape based on fibbonacci?

crobar wrote:Made some progress with the attached script, but not sure where to go from here:
There is a german post which might give you an idea how to use Sketcher for kind of "3D-Sketches": https://forum.freecadweb.org/viewtopic. ... 45#p165745. The videos mentioned there show what he has done.