--- jupyter: description: 'The brachistochrone with planex2d: a straight ramp, a circular arc and a cycloid between the same two points, the time to slide down each integrated along the wire, the cycloid fastest at pi sqrt(r/g) from any height, and a tube swept along it with OpenCASCADE against pi rho^2 L.' --- # The fastest slide Which track between two points does a bead slide down fastest, from rest and without friction? This is the brachistochrone problem, posed by Johann Bernoulli. The straight ramp is the shortest, but the bead starts slowly on it, and a circular arc is faster. The fastest track is a cycloid, the curve that a point on a rolling circle traces. The cycloid is also the tautochrone, the curve that Huygens used for his pendulum clocks. Wherever on it the bead starts from rest, it takes the same time to the bottom. The example slides a bead down three tracks from (0, 0) to $(\pi r, -2r)$, for a rolling circle of radius $r = 1\,\text{m}$. The cycloid must take $\pi \sqrt{r/g}$, less than the others, from any height on it, and its length must be $4r$. Swept with a disc, it is a tube whose volume must be $\pi \rho^2 L$. ![Beads sliding down the ramp, the arc and the cycloid, shown at the same instants](../_static/gallery/fastest-slide.png){.result} ```python import math import numpy as np import netgen.occ as occ import planex as px ``` ```python nbsphinx="hidden" from docs_figures import Row ``` ## Three tracks The cycloid is $x = r\,(\theta - \sin\theta)$, $y = -r\,(1 - \cos\theta)$, from its cusp at $\theta = 0$ to its bottom at $\theta = \pi$. `px.Curve` draws it from this formula, with $\theta$ in degrees, `t*pi/180` where it stands alone. The arc is the circle through both ends that is level at the bottom, as the cycloid is, so its center lies straight above the bottom end, at the height $(\pi^2 - 4)\,r/4$ (Figure 1). The cycloid's length is $4r$, which the print compares: ```python g = 9.81 r = 1.0 def cycloid(theta0): """the cycloid from theta0, in radians, to its bottom at pi""" return px.Curve((f"{r}*(t*pi/180 - sin(t))", f"-{r}*(1 - cos(t))"), (math.degrees(theta0), 180)) top, bottom = (0.0, 0.0), (math.pi * r, -2 * r) tracks = { "ramp": px.Line(top, bottom), "arc": px.Arc(top, bottom, (math.pi * r, (math.pi**2 - 4) * r / 4)), "cycloid": cycloid(0), } print("lengths", {name: track.mass for name, track in tracks.items()}, "| 4r", 4 * r) ``` ```python nbsphinx="hide-input" Row((px.Compound([tracks["ramp"], tracks["arc"]]), "The ramp, the arc and the cycloid, highlighted", {"highlight": list(tracks["cycloid"].edges), "labels": [("ramp", (2.0, -0.95)), ("arc", (1.45, -1.25)), ("cycloid", (1.9, -1.95))], "arcs": 720}), caption="Three tracks between the same two points") ``` ## Sliding down The bead's speed follows from its energy, $v = \sqrt{2 g h}$ after a drop $h$. `slide` follows a track's points, `Polylines` at a tolerance of 10⁻⁷, and adds the time of every straight piece between them. On a straight piece the bead's speed changes linearly in time, so it takes $2L/(v_1 + v_2)$ for its length $L$, exactly. The points follow the curve closely enough for the sum to be the curve's time. `slide` also returns the times at the points, for the picture below. The ramp's time is $L \sqrt{2/(g h)}$, and the cycloid's is $\pi \sqrt{r/g}$, the shortest of the three: ```python def slide(track): """the points along the track and the times the bead, starting there at rest, reaches them""" points = np.asarray(track.Polylines(1e-7)[0])[:, :2] speed = np.sqrt(np.maximum(2 * g * (points[0, 1] - points[:, 1]), 0)) pieces = np.hypot(*np.diff(points, axis=0).T) return points, np.concatenate([[0], np.cumsum(2 * pieces / (speed[:-1] + speed[1:]))]) slides = {name: slide(track) for name, track in tracks.items()} for name, (points, times) in slides.items(): print(name, "|", times[-1], "s") print("ramp", math.dist(top, bottom) * math.sqrt(2 / (g * 2 * r)), "s | cycloid pi sqrt(r/g)", math.pi * math.sqrt(r / g), "s") ``` The next figure shows the beads at the same instants, every 0.2 s. The cycloid's bead drops steeply at first, gains speed early and is ahead all the way. ```python nbsphinx="hide-input" import matplotlib.pyplot as plt fig, ax = plt.subplots(figsize=(10, 6)) colors = {"ramp": "#6fb0cf", "arc": "#d98c2b", "cycloid": "#0f7ea8"} for name, (points, times) in slides.items(): ax.plot(points[:, 0], points[:, 1], color=colors[name], lw=1.6, label=name) instants = [0.2, 0.4, 0.6, 0.8, 1.0] ax.scatter(np.interp(instants, times, points[:, 0]), np.interp(instants, times, points[:, 1]), color=colors[name], s=40, zorder=3) ax.set_aspect("equal") ax.legend(frameon=False) ax.axis("off") plt.show() ``` ```python nbsphinx="hidden" import sys sys.path.insert(0, "..") from gallery import save_figure card, ax = plt.subplots(figsize=(6.4, 4)) for name, (points, times) in slides.items(): ax.plot(points[:, 0], points[:, 1], color=colors[name], lw=3) instants = [0.2, 0.4, 0.6, 0.8, 1.0] ax.scatter(np.interp(instants, times, points[:, 0]), np.interp(instants, times, points[:, 1]), color=colors[name], s=90, zorder=3, edgecolor="white", linewidth=1) ax.set_aspect("equal") ax.axis("off") save_figure(card, "fastest-slide", bare=True) plt.close(card) ``` ## From any height On the tautochrone, the bead reaches the bottom in the same time from any point where it starts from rest. From $\theta = 0^\circ$, $45^\circ$, $90^\circ$ and $135^\circ$, the times to the bottom are compared with $\pi \sqrt{r/g}$: ```python for degrees in (0, 45, 90, 135): points, times = slide(cycloid(math.radians(degrees))) print("from", degrees, "degrees |", times[-1], "s, pi sqrt(r/g)", math.pi * math.sqrt(r / g), "s") ``` ## A track in 3D A tube of radius $\rho = 5\,\text{cm}$ along the cycloid is a disc on the plane square to the curve at its start, swept along it with `occ.Pipe` (Figure 2). Near the cusp the cycloid bends more sharply than the tube is thick, so the tube starts at $\theta = 0.3$, where the curve's radius of curvature, $4r \sin(\theta/2)$, is well above $\rho$. The disc's center follows the curve, so the volume is the disc's area times the length, $\pi \rho^2 \cdot 4r \cos(\theta/2)$: ```python start = 0.3 spine = cycloid(start) x0, y0 = r * (start - math.sin(start)), -r * (1 - math.cos(start)) dx, dy = 1 - math.cos(start), -math.sin(start) square = px.Plane(origin=(x0, y0, 0), normal=(dx / math.hypot(dx, dy), dy / math.hypot(dx, dy), 0)) tube = occ.Pipe(spine.ToOCCWire(), px.QuickSketch(square).Circle(0.05).Face().ToOCCFace()) print("tube", tube.mass, "m³, exact", math.pi * 0.05**2 * 4 * r * math.cos(start / 2)) ``` ```python nbsphinx="hide-input" upright = tube.Rotate(occ.Axis((0, 0, 0), occ.X), 90) # the curve's y up in the picture Row((upright, "The tube along the cycloid", {"views": [(upright, "shade"), (upright, "ink")], "angles": (-80, 0, 10)}), caption="A tube swept along the cycloid") ``` See [Lines and arcs](../core/basics/edges.md) for `px.Arc` and [Splines](../core/basics/splines.md) for splines through points.