Parameterised plan for a mini-ramp¶
When building and planning a mini-ramp for skateboarding dimensions and proportions play a central role for riding behaviour, safety and usability in the desired space. Classic technical drawings are often static - they describe a fixed ramp size. But, requirements often change in practice:
- How long and high can the ramp be so that it still fits into the space?
- What effect does a larger radius have?
- What is the resulting flat space between transitions?
The functional model has the following parameters to control the layout:
- b (width)
- l (length)
tll/tlr (tablelengths left/right)
h (height)
- r (radius)
Length which is left gets added to the flat part in between the transitions. Because a too short flat sucks!
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
b = 5.5 # Width
l = 8 # Length
tll = 0.8 # Table length left
tlr = 1 # Table length right
h = 1.3 # Height
# Radius should be h +0.4 (Source: Jan Hirt (50/50 Skatepodcast))
r = 2 # Radius
Transitions¶
Ready for some basic trigonometry? Miniramp transitions are not vertical at the top. Usually the radius is bigger than the height. Therefore we must calculate the length of $dx$:
$$ a² + b² = c² \\ (r-h)² + dx² = r² \\ dx = \sqrt{r² - (r - h)^2} $$It is helpful to calculate the $\theta$ angle as well:
$$ \theta = \arcsin{\left(\frac{r-h}{r}\right)} $$and the arc length $al$:
$$ al = r * \left(\frac{\pi}{2}-\theta\right) $$There is a plot at the end of following code cell which sketches everything!
# dx length
dx = np.sqrt(r**2 - (r - h)**2)
# Angle
theta_rad = np.arcsin((r-h)/r)
# Arc length
arc_l = r * (0.5*np.pi-theta_rad)
# Circle center points
m1x = tll + dx
m1y = r
m2x = l - tlr - dx
m2y = r
# Circle parts (radian)
theta_leftover = np.linspace(np.pi, np.pi+theta_rad, 200)
theta_transition = np.linspace(np.pi+theta_rad,1.5*np.pi, 200)
# Transition coords
tran_x = m1x + r * np.cos(theta_transition)
tran_y = m1y + r * np.sin(theta_transition)
# Leftover coords
leftover_x = m1x + r * np.cos(theta_leftover)
leftover_y = m1y + r * np.sin(theta_leftover)
fig, ax = plt.subplots(1,1,figsize=(6, 6))
# Transition
ax.scatter(m1x,m1y,color='blue') # circle center point
ax.plot(tran_x, tran_y, color='blue') # tran circle part
ax.plot(leftover_x, leftover_y, color='blue', linestyle=':') # lefover circle part
ax.plot([tll,tll],[0,h], color='blue') # h
ax.text(tll-0.1,h/2,'h='+str(h), color='blue', fontsize=12, rotation='vertical')
ax.text(tll+dx/2,h/3,'al='+str(round(arc_l,2)), color='blue', fontsize=12)
# Triangle
ax.plot([m1x,m1x-dx],[m1y,m1y-(r-h)],color='green') # r
ax.text(tll+dx/2,h+0.1+(r-h)/2,'r='+str(r),fontsize=12, color='green')
ax.plot([m1x,m1x],[m1y,m1y-(r-h)], color='black') # r-h
ax.text(tll+dx-0.1,h+(r-h)/3,'r-h='+str(r-h),fontsize=12, color='black',rotation='vertical')
ax.plot([m1x,m1x-dx],[m1y-(r-h),m1y-(r-h)], color='red') # dx
ax.text(tll+dx/2,h-0.2,'dx='+str(round(dx,2)),fontsize=12, color='red')
ax.text(tll+0.4,h+0.05,r'$\theta$='+str(round(theta_rad,2)),fontsize=12) # theta
ax.set_title('Trigonometry of a transition (m)')
ax.set_aspect('equal')
2D Plans¶
Now we are ready to draw the plans. We start with the Side View followed by the Top View.
The total length where you ride is calculated with:
$$ total = flat + 2*arclength $$and marked with violet in the side view
fig, ax = plt.subplots(1, 2, figsize=(18, 12))
ax[0].set_aspect('equal')
ax[1].set_aspect('equal')
# Side view
# 1. Tables
# left
ax[0].plot([tll, 0], [h, h], 'black')
ax[0].text(0.1, h-0.3, "tl="+str(tll), fontsize=12, color='black')
# right
ax[0].plot([l, l - tlr], [h, h], 'black')
ax[0].text((l - tlr) + tlr/4, h-0.3, "tr="+str(tlr), fontsize=12, color='black')
# height edges
ax[0].plot([tll, tll], [0, h], 'black')
ax[0].plot([l - tlr, l - tlr], [0, h], 'black')
# 2. Transitions
# Replot the first transition from above
ax[0].plot(tran_x, tran_y, color='blue') # tran circle part
ax[0].plot(leftover_x, leftover_y, color='blue', linestyle=':') # lefover circle part
# Center of the circles
ax[0].scatter(m1x, m1y, color='blue')
ax[0].scatter(m2x, m2y, color='blue')
ax[0].text(m1x-1, h, "r="+str(r), fontsize=12, color='blue')
# Plot second theta
theta_transition_2 = np.linspace(1.5*np.pi,2*np.pi-theta_rad, 200)
theta_leftover_2 = np.linspace(2*np.pi-theta_rad,2*np.pi, 200)
# Second transition coords
tran_x_sec = m2x + r * np.cos(theta_transition_2)
tran_y_sec = m2y + r * np.sin(theta_transition_2)
# Second leftover coords
leftover_x_sec = m2x + r * np.cos(theta_leftover_2)
leftover_y_sec = m2y + r * np.sin(theta_leftover_2)
# Plot second transition
ax[0].plot(tran_x_sec, tran_y_sec, color='blue') # tran circle part
ax[0].plot(leftover_x_sec, leftover_y_sec, color='blue', linestyle=':') # lefover circle part
# 5. Flat
ax[0].plot([m1x, m2x], [0, 0], 'red')
ax[0].text(l/2, 0.2, "flat="+str(round(m2x - m1x,2)), fontsize=12, color='red')
# Total l
total_l = (m2x - m1x) + 2 * arc_l
ax[0].text(l/3, h/2, "total="+str(round(total_l,2)), fontsize=12, color='violet')
# Top view
# Dimensions
ax[1].plot([0, l], [0, 0], 'black')
ax[1].plot([0, l], [b, b], 'black')
ax[1].plot([0, 0], [0, b], 'black')
ax[1].plot([l, l], [0, b], 'black')
# Tables
ax[1].plot([tll, tll], [0, b], 'black')
ax[1].plot([l-tlr, l-tlr], [0, b], 'black')
# Transitions
ax[1].plot([m1x, m1x], [0, b], 'blue')
ax[1].plot([m2x, m2x], [0, b], 'blue')
# Settings
ax[0].set_title("Side view (m)")
ax[0].set_xlim(0, l)
ax[0].set_ylim(0, r + 0.1)
ax[0].set_xlabel("l="+str(l))
ax[0].set_ylabel("h="+str(h))
ax[1].set_title("Top view (m)")
ax[1].set_xlim(0, l)
ax[1].set_ylim(0, b)
ax[1].set_xlabel("l="+str(l))
ax[1].set_ylabel("b="+str(b))
plt.show()
3D View¶
It's a lot of code ...
fig
To conclude the advantages of this parametric approach are:
- Flexible customisation: changes to values immediately generate new variants.
- Consistent geometry: transitions, circles and dimensions remain correct in relation to each other.
- Visualisation: Side and top views, as well as 3D perspectives.
- Experimentation & optimisation: Measure your favorite ramp and compare it to your plan!
- Trigonometry Refreshing Course: Personal advantage:)