Kapparino
Kapparino

Reputation: 988

reposition object in circle

As you can see on the image, I have a p1 and p2 objects with (x,y) coordinates which I know the values, and I know radius of all these circle objects.

However, I want to calculate new position x,y which would be p3 center point. Basically, as you can see it's p2 position + radius.

I am doing this for java game which is based on libgdx. I would appreciate any math or java language directions/examples.

enter image description here

Upvotes: 0

Views: 299

Answers (3)

Franck
Franck

Reputation: 1455

Now using libgdx for the graphics. Thus no need for polar co-ordinates, on the outside.

I am not doing frame rate relative animation. Therefore, this is no perfect match to your code.

Using the following calculation (if (theta >= 360) { theta = 0.0f; }) at the end of the render method will let the animation restart with its original value.

package org.demo;

import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.utils.ScreenUtils;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;

public class CircleDemo extends ApplicationAdapter {
    ShapeRenderer shapeRenderer;
    float theta = 0.0f;

    @Override
    public void create () {
        shapeRenderer = new ShapeRenderer();
    }

    @Override
    public void render () {
        ScreenUtils.clear(0, 0.4f, 0.4f, 1);

        Vector2 p1 = new Vector2( Gdx.graphics.getWidth() / 2.0f ,  Gdx.graphics.getHeight() / 2.0f);
        Vector2 smallCircleCenter = new Vector2(150.0f, 0.0f);
        smallCircleCenter.add(p1); // translate center by p1

        shapeRenderer.begin(ShapeRenderer.ShapeType.Line);

        // static lines and circles
        for (int angle = 0; angle < 360; angle += 30) {
            Vector2 lineEnd = new Vector2(smallCircleCenter);
            lineEnd.rotateAroundDeg(p1, angle);
            shapeRenderer.line(p1, lineEnd);
            shapeRenderer.circle(lineEnd.x, lineEnd.y, 20);
        }

        // animated line and circle in red
        shapeRenderer.setColor(0.75f, 0, 0, 1);
        Vector2 movingCircleCenter = new Vector2(smallCircleCenter);
        movingCircleCenter.rotateAroundDeg(p1, theta);
        shapeRenderer.line(p1, movingCircleCenter);
        shapeRenderer.circle(movingCircleCenter.x, movingCircleCenter.y, 20);

        shapeRenderer.setColor(1, 1, 1, 1);

        shapeRenderer.end();
        theta++;

        // for the screenshot stop at 90 degrees
        if (theta >= 90) {
            theta = 90.0f;
        }
    }
    
    @Override
    public void dispose () {
        shapeRenderer.dispose();
    }
}

Circle drawn with libgdx

Upvotes: 1

Kapparino
Kapparino

Reputation: 988

So I wrote a test in my project, based on your approach:

package com.bigbang.test.impl;

import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.utils.Array;
import com.bigbang.Game;
import com.bigbang.graphics.g2d.shapes.impl.Ellipse;
import com.bigbang.graphics.g2d.shapes.impl.Line;
import com.bigbang.graphics.gl.Color;
import com.bigbang.math.BBMath;

public class PolarToCartesianTest extends AbstractTest {

    private Array<GraphicalObject> graphicalObjectArray;
    private GraphicalObject dynamicGraphicalObject;
    private float radius, smallCircleRadius;
    private float centerX, centerY;

    public PolarToCartesianTest(Game game) {
        super(game);
    }

    @Override
    public void create() {
        radius = 200f;
        centerX = game.getScreenController().getScreenWidth() / 2;
        centerY = game.getScreenController().getScreenHeight() / 2;
        smallCircleRadius = radius / 4;
        graphicalObjectArray = new Array<>();
        for (int angle = 0; angle < 360; angle += 30) {
            GraphicalObject graphicalObject = new GraphicalObject();
            graphicalObject.angle = angle;
            graphicalObjectArray.add(graphicalObject);
        }
        dynamicGraphicalObject = new GraphicalObject();

        game.getCameraController().getCamera().position.x = game.getScreenController().getScreenWidth() / 2;
        game.getCameraController().getCamera().position.y = game.getScreenController().getScreenHeight() / 2;
    }

    @Override
    public void update(float deltaTime) {
        for (GraphicalObject graphicalObject : graphicalObjectArray) {
            Vector2 polarToCartesianPosition = BBMath.polarToCartesian(radius, graphicalObject.angle);

            graphicalObject.line.x1 = centerX + 0;
            graphicalObject.line.y1 = centerY + 0;
            graphicalObject.line.x2 = centerX + polarToCartesianPosition.x;
            graphicalObject.line.y2 = centerY + polarToCartesianPosition.y;
            graphicalObject.line.color = Color.WHITE_COLOR;

            graphicalObject.ellipse.x = centerX + polarToCartesianPosition.x;
            graphicalObject.ellipse.y = centerY + polarToCartesianPosition.y;
            graphicalObject.ellipse.width = 2 * smallCircleRadius;
            graphicalObject.ellipse.height = 2 * smallCircleRadius;
            graphicalObject.ellipse.color = Color.WHITE_COLOR;
        }

        float shift = 0;
        float theta = (shift * smallCircleRadius) * (centerY / centerX);
        Vector2 pos = BBMath.polarToCartesian(radius, theta);
        dynamicGraphicalObject.line.color = new Color(Color.RED);
        dynamicGraphicalObject.line.x1 = centerX + 0;
        dynamicGraphicalObject.line.y1 = centerY + 0;
        dynamicGraphicalObject.line.x2 = centerX + pos.x;
        dynamicGraphicalObject.line.y2 = centerY + pos.y;

        dynamicGraphicalObject.ellipse.x = centerX + pos.x;
        dynamicGraphicalObject.ellipse.y = centerY + pos.y;
        dynamicGraphicalObject.ellipse.width = 2 * smallCircleRadius;
        dynamicGraphicalObject.ellipse.height = 2 * smallCircleRadius;
        dynamicGraphicalObject.ellipse.color = new Color(Color.RED);
    }

    @Override
    public void draw() {
        game.getShapeRenderer().begin(ShapeRenderer.ShapeType.Line);
        for (GraphicalObject graphicalObject : graphicalObjectArray) {
            graphicalObject.line.draw();
            graphicalObject.ellipse.draw();
        }
        dynamicGraphicalObject.line.draw();
        dynamicGraphicalObject.ellipse.draw();
        game.getShapeRenderer().end();
    }

    class GraphicalObject {
        Ellipse ellipse;
        Line line;
        float angle;

        public GraphicalObject() {
            this.ellipse = new Ellipse(game);
            this.line = new Line(game);
        }
    }
}

Which is same math like in your example, with some modifications:

enter image description here

However, you can notice I have this dynamicGraphicalObject (red circle), which I want to shift position around circle by using theta value calculated as (shift * smallCircleRadius) * (centerY / centerX);. This works perfect for shift=0 value. It's properly positioned/overlapping white. But if I would change shift variable to 1, 2, 3, or 11, you can see that it's not precisely aligned with white circles. Is this floating point issue or am I missing something in calculation of theta ? shift values used: 2,6 and 11 in order by images

enter image description here

enter image description here

enter image description here

--

SOLUTION:

float fixPrecision = 1.1f;
float theta = (shift * fixPrecision) + ((shift * smallCircleRadius) * (centerY / centerX));

Upvotes: 0

Franck
Franck

Reputation: 1455

See code comments for explanation.

import java.awt.*;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import javax.swing.*;

class CenteredCircle extends Ellipse2D.Double {
    CenteredCircle(Point2D.Double p, double radius) {
        super(p.x - radius, p.y - radius, 2 * radius, 2 * radius);
    }   
}

public class CircleDemo extends JFrame {
    public CircleDemo() {
            int width = 640; int height = 480;
            setSize(new Dimension(width, height));
            setLocationRelativeTo(null);
            setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            setVisible(true);
    
            JPanel p = new JPanel() {
                @Override
                public void paintComponent(Graphics g) {
                    Graphics2D g2d = (Graphics2D) g;
                    // center p1
                    Point2D.Double p1 = new Point2D.Double(getSize().width/2, getSize().height/2);
                    double radius = 130.0;

                    // big circle
                    Shape circle2 = new CenteredCircle(p1, radius);
                    g2d.draw(circle2);                    

                    // 12 small circles
                    for (int angle = 0; angle < 360; angle += 30) {
                        // this is the magic part
                        // a polar co-ordinate has a length and an angle
                        // by changing the angle we rotate
                        // the transformed co-ordinate is the center of the small circle
                        Point2D.Double newCenter = polarToCartesian(radius, angle);
                        // draw line just for visualization
                        Line2D line = new Line2D.Double(p1.x, p1.y, p1.x + newCenter.x, p1.y+ newCenter.y);
                        g2d.draw(line);
                        // draw the small circle
                        Shape circle = new CenteredCircle(
                            new Point2D.Double(p1.x + newCenter.x, p1.y + newCenter.y),
                            radius/4);
                        g2d.draw(circle);  
                    }
                }
            };
            setTitle("Circle Demo");
            getContentPane().add(p);
        }
    public static void main(String arg[]) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                new CircleDemo();
            }
        });
    }    
    static Point2D.Double polarToCartesian(double r, double theta) {
        theta = (theta * Math.PI) / 180.0; // multiply first, then divide to keep error small
        return new Point2D.Double(r * Math.cos(theta), r * Math.sin(theta));
    }
    // not needed, just for completeness
    public static Point2D.Double cartesianToPolar(double x, double y) {
        return new Point2D.Double(Math.sqrt(x * x + y * y), (Math.atan2(y, x) * 180) / Math.PI);
    }    
}

Circle with small circle along

Upvotes: 1

Related Questions