重新定位圆圈中的对象

reposition object in circle

正如您在图片上看到的,我有一个 p1 和 p2 对象,其坐标为 (x,y),我知道这些值,并且我知道所有这些圆形对象的半径。

但是,我想计算新的位置 x,y,这将是 p3 中心点。基本上,如您所见,它是 p2 位置 + 半径。

我正在为基于 libgdx 的 java 游戏执行此操作。我将不胜感激任何数学或 java 语言 directions/examples.

请参阅代码注释以获取解释。

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);
    }    
}

所以我在我的项目中写了一个测试,基于你的方法:

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);
        }
    }
}

这与您的示例中的数学相同,但有一些修改:

但是,您可以注意到我有这个 dynamicGraphicalObject(红色圆圈),我想通过使用计算为 (shift * smallCircleRadius) * (centerY / centerX); 的 theta 值围绕圆圈移动位置。这非常适合 shift=0 值。它是正确的 positioned/overlapping 白色。但是,如果我将 shift 变量更改为 1、2、3 或 11,您会看到它并没有与白色圆圈精确对齐。这是浮点问题还是我在计算 theta 时遗漏了什么? 使用的偏移值:2,6 和 11 按图像排序

--

解决方案:

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

现在对图形使用 libgdx。因此不需要极地co-ordinates,在外面。

我不是在做帧率相关的动画。因此,这与您的代码并不完全匹配。

在 render 方法的末尾使用以下计算 (if (theta >= 360) { theta = 0.0f; }) 将使动画以其原始值重新开始。

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();
    }
}