Search code examples
javascriptgeometry

How to find out where y intersects with x on the surface of a cycloid?


To find out where y intersects with x on the surface of the circle i use Math.sqrt(radius * radius - Math.pow( x, 2)) .

How do I find out where y intersects with x on the surface of a cycloid?

In the snippets bellow I have a circle and a cycloid. For the circle I am able to get the value of y from x with const y = Math.sqrt(radius * radius - Math.pow(radius - x, 2)). I want to be able to do the same for the cycloid and replace const y = 0 with a working solution.

(() => {
            const inputX = document.getElementById('xCircle')
            inputX.addEventListener('input', (e) => {

                const x = e.target.value

                ctx.clearRect(0, 0, radius, radius)
                drawCurve()
                drawIntersection(x)
            })

            const radius = 200

            const canvas = document.getElementById('circle')
            canvas.width = radius
            canvas.height = radius
            const ctx = canvas.getContext('2d')

            const drawCurve = () => {
                ctx.beginPath()
                ctx.arc(radius, 0, radius, 0, Math.PI * 2)
                ctx.stroke()
            }
            drawCurve()

            const drawIntersection = (x) => {

                const y = Math.sqrt(radius * radius - Math.pow(radius - x, 2))                
               
               document.getElementById('yCircle').textContent = Math.round(y)

                ctx.beginPath()
                ctx.moveTo(x, radius)
                ctx.lineTo(x, y)
                ctx.lineTo(radius, y)
                ctx.stroke()
            }
            drawIntersection(inputX.value)

        })();


        (() => {

            const inputX = document.getElementById('xCycloid')
            inputX.addEventListener('input', (e) => {

                const x = e.target.value
                
                ctx.clearRect(0, 0, canvasW, canvasH)
                drawCurve()
                drawIntersection(x)
            })

            const radius = 100

            const canvas = document.getElementById('cycloid')
            const canvasW = radius * Math.PI, canvasH = radius * 2
            canvas.width = canvasW
            canvas.height = canvasH
            const ctx = canvas.getContext('2d')

            const drawCurve = () => {
                ctx.beginPath()
                for (let t = 0; t <= Math.PI; t += 0.01) {
                    const x = radius * (t - Math.sin(t))
                    const y = radius * (1 - Math.cos(t))

                    ctx.lineTo(x, y)
                }
                ctx.stroke()
            }
            drawCurve()

            const drawIntersection = (x) => {

                const y = 0
                
              document.getElementById('yCycloid').textContent = Math.round(y)

                ctx.beginPath()
                ctx.moveTo(x, canvasH)
                ctx.lineTo(x, y)
                ctx.lineTo(canvasW, y)
                ctx.stroke()
            }
            drawIntersection(inputX.value)

        })();
canvas {
            border: 1px solid
        }
<canvas id="circle"></canvas>
    <form>
        <label>x:<input id="xCircle" type="number" value="30"></label>
        y:<span id="yCircle"></span>
    </form>
    <br>
    <canvas id="cycloid"></canvas>
    <form>
        <label>x:<input id="xCycloid" type="number" value="40"></label>
        y:<span id="yCycloid"></span>
    </form>


Solution

  • You have to solve approximatively (numerically) the equation t - Math.sin(t) == x/radius to get t for x, then substitute t in y = radius * (1 - Math.cos(t)).

    Solving the equation can only be done iteratively, here's a possible implementation based on Newton's method

    let t = Math.PI/2; // initial solution (half value)
    let converged = false;
    for(let iter = 0; iter < 100; iter++){
        // tNext = Math.sin(t) + x/radius; // alternative, simpler fixed point iteration
        const tNext = t - (t - Math.sin(t) - x/radius)/(1 - Math.cos(t)); // Newton's method
        const delta = Math.abs(tNext - t);
        t = tNext;
        if(delta < 1e-8){
            converged = true;
            break;
        }
    }
    if(!converged){
        throw new Error('No solution found');
    }
    
    const y = radius * (1- Math.cos(t));
    

    Here's your snippet with that code inserted:

    (() => {
                const inputX = document.getElementById('xCircle')
                inputX.addEventListener('input', (e) => {
    
                    const x = e.target.value
    
                    ctx.clearRect(0, 0, radius, radius)
                    drawCurve()
                    drawIntersection(x)
                })
    
                const radius = 200
    
                const canvas = document.getElementById('circle')
                canvas.width = radius
                canvas.height = radius
                const ctx = canvas.getContext('2d')
    
                const drawCurve = () => {
                    ctx.beginPath()
                    ctx.arc(radius, 0, radius, 0, Math.PI * 2)
                    ctx.stroke()
                }
                drawCurve()
    
                const drawIntersection = (x) => {
    
                    const y = Math.sqrt(radius * radius - Math.pow(radius - x, 2))                
                   
                   document.getElementById('yCircle').textContent = Math.round(y)
    
                    ctx.beginPath()
                    ctx.moveTo(x, radius)
                    ctx.lineTo(x, y)
                    ctx.lineTo(radius, y)
                    ctx.stroke()
                }
                drawIntersection(inputX.value)
    
            })();
    
    
            (() => {
    
                const inputX = document.getElementById('xCycloid')
                inputX.addEventListener('input', (e) => {
    
                    const x = e.target.value
                    
                    ctx.clearRect(0, 0, canvasW, canvasH)
                    drawCurve()
                    drawIntersection(x)
                })
    
                const radius = 100
    
                const canvas = document.getElementById('cycloid')
                const canvasW = radius * Math.PI, canvasH = radius * 2
                canvas.width = canvasW
                canvas.height = canvasH
                const ctx = canvas.getContext('2d')
    
                const drawCurve = () => {
                    ctx.beginPath()
                    for (let t = 0; t <= Math.PI; t += 0.01) {
                        const x = radius * (t - Math.sin(t))
                        const y = radius * (1 - Math.cos(t))
    
                        ctx.lineTo(x, y)
                    }
                    ctx.stroke()
                }
                drawCurve()
    
                const drawIntersection = (x) => {
                    let t = Math.PI/2; 
                    let converged = false;
                    for(let iter = 0; iter < 100; iter++){
                      
                      const tNext = t - (t - Math.sin(t) - x/radius)/(1 - Math.cos(t)); // Newton's method
                      // tNext = Math.sin(t) + x/radius; // simple fixed point iteration
                      const delta = Math.abs(tNext - t); 
                      t = tNext;
                      if(delta < 1e-8){
                          converged = true;
                          break;
                      }
                    }
                    if(!converged){
                      throw new Error('No solution found');
                    }
    
                    const y = radius * (1- Math.cos(t));
                    
                  document.getElementById('yCycloid').textContent = Math.round(y)
    
                    ctx.beginPath()
                    ctx.moveTo(x, canvasH)
                    ctx.lineTo(x, y)
                    ctx.lineTo(canvasW, y)
                    ctx.stroke()
                }
                drawIntersection(inputX.value)
    
            })();
    canvas {
                border: 1px solid
            }
    <canvas id="circle"></canvas>
        <form>
            <label>x:<input id="xCircle" type="number" value="30"></label>
            y:<span id="yCircle"></span>
        </form>
        <br>
        <canvas id="cycloid"></canvas>
        <form>
            <label>x:<input id="xCycloid" type="number" value="40"></label>
            y:<span id="yCycloid"></span>
        </form>