javascriptrotation2d

How to rotate a 2d circle around another?


I'm trying to rotate a circle around another circle in 2d.

I have the following code implemented, which rotates the circle on its axis, however, I want to rotate the circle around the other one.

var w = window.innerWidth;
var h = window.innerHeight;

var canvas = document.createElement('canvas');

document.body.appendChild(canvas);

canvas.width = w;
canvas.height = h;

var ctx = canvas.getContext('2d');

var point = function(x,y,z,rgb) {this.x=x;this.y=y;this.z=z;this.rgb=rgb;}

class Circle {
    constructor(x,y,z,radius) {
        this.x=x;
        this.y=y;
        this.z=z;
        this.radius=radius;
        this.points = [];

         for(let i =0; i<360; i++) {
            let angle = Math.PI / 180 * i;
            let cos = this.x + Math.cos(angle) * this.radius;
            let sin = this.y + Math.sin(angle) * this.radius;
            this.points.push(new point(cos, sin, this.z, `rgb(${i}, ${i}, ${i})`));
    
    }
    }

    rotate() {
    for(let i =0; i<this.points.length; i++) {
        let p = this.points[i];
        let dx = Math.cos(Math.PI / 180) * (p.x - this.x) - Math.sin(Math.PI / 180) * (p.y - this.y);
        let dy = Math.sin(Math.PI / 180) * (p.x - this.x) + Math.cos(Math.PI / 180) * (p.y - this.y);

        p.x = dx + this.x;
        p.y = dy + this.y;
    }
}


drawX(points, i) {
    let p = points[i];
    ctx.beginPath();
    ctx.strokeStyle = p.rgb; 
    ctx.moveTo(p.x, p.y);
    ctx.lineTo(p.x + 1, p.y + 1);
    ctx.stroke();
}

draw(ctx) {
    for(let i =0; i<this.points.length; i++) {
       this.drawX(this.points, i);
    }
}

}

circle1 = new Circle(w/2, h/2, 10, 50); 
circle2 = new Circle(w/2+200, h/2, 10, 50);

function rotateAround(p1, p2) {
    for(let i =0; i<p1.length; i++) {
        for(let j =0; j<p2.length; j++) {
            let dx = Math.cos(Math.PI / 180) * (p1[i].x - p2[j].x) - Math.sin(Math.PI / 180) * (p1[i].y - p2[j].y);
            let dy = Math.sin(Math.PI / 180) * (p1[i].x - p2[j].x) + Math.cos(Math.PI / 180) * (p1[i].y - p2[j].y);

            p1[i].x = dx + p2[j].x;
            p1[i].y = dy + p2[j].y;
        }   
    }
}

function render(now) {
    //now *= 0.001;

    circle1.draw(ctx);
    circle1.rotate();

    circle2.draw(ctx);
    circle2.rotate();

    //rotateAround(circle1.points, circle2.points);       
    requestAnimationFrame(render);
}
render();

Unfortunately, the circles rotate but not around one another. What I have tried can be seen in the rotateAround method.

How do I go about solving this?

Any help would be muchly appreciated


Solution

  • Some remarks on your code:

    Here is how your code evolved to when I tackled the above remarks:

    class Point { // Use class syntax
        constructor(x, y, z, color) {
            this.x = x;
            this.y = y;
            this.z = z;
            this.color = color;
        }
        // Give a Point a draw method of its own
        draw(ctx) {
            ctx.beginPath();
            ctx.strokeStyle = this.color;
            ctx.moveTo(this.x, this.y);
            ctx.lineTo(this.x + 1, this.y + 1);
            ctx.stroke();
        }
    }
    
    class Circle extends Point { // Use inheritance
        constructor(x, y, z, radius, rotationSpeed=1) {
            super(x, y, z, "black");
            this.radius = radius;
            this.rotation = 0;
            this.rotationSpeed = rotationSpeed;
        }
        
        rotate() {
            // No need to store the points, only the current angle
            this.rotation += this.rotationSpeed; 
        }
        
        draw(ctx) {
            for (let i = 0; i < 360; i++) {
                const angle = (Math.PI / 180 * (i + this.rotation)) % 360;
                const cos = this.x + Math.cos(angle) * this.radius;
                const sin = this.y + Math.sin(angle) * this.radius;
                // Draw the point as it is created
                new Point(cos, sin, this.z, `rgb(${i}, ${i}, ${i})`).draw(ctx);
            }
        }
    }
    
    class SlaveCircle extends Circle {
        constructor(master, radius, rotationSpeed, distance, revolutionSpeed) {
            super(master.x + distance, master.y, master.z, radius, rotationSpeed);
            this.master = master;
            this.distance = distance;
            this.revolutionSpeed = revolutionSpeed;
            // Update this slave's location based on an angle.
            this.setAngle(0); 
        }
    
        // Distinguish "rotation" from "revolution":
        revolve() {
            this.setAngle((this.angle + this.revolutionSpeed + 360) % 360);
        }
    
        setAngle(angle) {
            // Keep track of current angle, and update circle-coordinates accordingly
            this.angle = angle;
            const radians = angle / 180 * Math.PI;
            this.x = this.master.x + Math.cos(radians) * this.distance;
            this.y = this.master.y + Math.sin(radians) * this.distance;
        }
    }
    
    function render(ctx, ...circles) {
        // As (some of) the circles move, we need to clear the canvas first:
        ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
        for (const circle of circles) {
            circle.rotate(); // self-rotation (which results in colored-boundary animation)
            circle.revolve?.(); // Only "slave" circle(s) will revolve
            circle.draw(ctx);
        }
        requestAnimationFrame(() => render(ctx, ...circles));
    }
    
    function main(canvas, width, height) { // Limit the scope of variables
        Object.assign(canvas, { width, height });
        document.body.appendChild(canvas);
        const circle1 = new Circle(width/2, height/2, 10, height/6, 3); 
        // Set up dependency between circles:
        const circle2 = new SlaveCircle(circle1, height/20, 5, height/3, 1); 
        render(canvas.getContext('2d'), circle1, circle2);
    }
    
    main(document.createElement('canvas'), innerWidth, innerHeight);
    body { margin: 0; overflow: hidden; }

    Revolve around revolving circles

    With this design you can add a third circle that revolves around the one that is already revolving. Here is a third one added to the constellation which revolves in opposite direction with a greater revolution speed:

    class Point { // Use class syntax
        constructor(x, y, z, color) {
            this.x = x;
            this.y = y;
            this.z = z;
            this.color = color;
        }
        // Give a Point a draw method of its own
        draw(ctx) {
            ctx.beginPath();
            ctx.strokeStyle = this.color;
            ctx.moveTo(this.x, this.y);
            ctx.lineTo(this.x + 1, this.y + 1);
            ctx.stroke();
        }
    }
    
    class Circle extends Point { // Use inheritance
        constructor(x, y, z, radius, rotationSpeed=1) {
            super(x, y, z, "black");
            this.radius = radius;
            this.rotation = 0;
            this.rotationSpeed = rotationSpeed;
        }
    
        rotate() {
            // No need to store the points, only the current angle
            this.rotation += this.rotationSpeed; 
        }
    
        draw(ctx) {
            for (let i = 0; i < 360; i++) {
                const angle = (Math.PI / 180 * (i + this.rotation)) % 360;
                const cos = this.x + Math.cos(angle) * this.radius;
                const sin = this.y + Math.sin(angle) * this.radius;
                // Draw the point as it is created
                new Point(cos, sin, this.z, `rgb(${i}, ${i}, ${i})`).draw(ctx);
            }
        }
    }
    
    class SlaveCircle extends Circle {
        constructor(master, radius, rotationSpeed, distance, revolutionSpeed) {
            super(master.x + distance, master.y, master.z, radius, rotationSpeed);
            this.master = master;
            this.distance = distance;
            this.revolutionSpeed = revolutionSpeed;
            // Update this slave's location based on an angle.
            this.setAngle(0); 
        }
    
        // Distinguish "rotation" from "revolution":
        revolve() {
            this.setAngle((this.angle + this.revolutionSpeed + 360) % 360);
        }
    
        setAngle(angle) {
            // Keep track of current angle, and update circle-coordinates accordingly
            this.angle = angle;
            const radians = angle / 180 * Math.PI;
            this.x = this.master.x + Math.cos(radians) * this.distance;
            this.y = this.master.y + Math.sin(radians) * this.distance;
        }
    }
    
    function render(ctx, ...circles) {
        // As (some of) the circles move, we need to clear the canvas first:
        ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
        for (const circle of circles) {
            circle.rotate(); // self-rotation (which results in colored-boundary animation)
            circle.revolve?.(); // Only "slave" circle(s) will revolve
            circle.draw(ctx);
        }
        requestAnimationFrame(() => render(ctx, ...circles));
    }
    
    function main(canvas, width, height) { // Limit the scope of variables
        Object.assign(canvas, { width, height });
        document.body.appendChild(canvas);
        const circle1 = new Circle(width/2, height/2, 10, height/6, 3); 
        // Set up dependency between circles:
        const circle2 = new SlaveCircle(circle1, height/20, 5, height/3, 1); 
        // Add a third circle that revolves around the second one
        //    and make it revolve faster in opposite direction
        const circle3 = new SlaveCircle(circle2, height/40, 7, height/10, -4); 
        render(canvas.getContext('2d'), circle1, circle2, circle3);
    }
    main(document.createElement('canvas'), innerWidth, innerHeight);
    body { margin: 0; overflow: hidden; }