问题描述
我是JavaFX的新手,在三角函数和数学方面确实很弱,并且一直在尝试寻找两点之间的直线角度。这两个点围绕着两个不同的理想圆形轨道的共同中心点旋转。这些点代表地球与木星之间的距离,确切地说,我需要找到地球与木星之间的角度。如我们的太阳系一样,木星在一个半径更大的轨道上旋转。我试过了'atan'和'atan2',但是它并没有给出所有角度的预期答案,我太笨了,无法正确使用它。经过进一步研究,我发现类似“它仅计算相对于X轴+ ve侧的角度”,但我明白了。在“斜率计算器”(https://www.calculator.net/slope-calculator.html)的进一步阅读和帮助下,我设法像他们一样解决了这个问题。通过向atan / atan2结果中添加适当的度数(例如180,360),并获得正确和预期的结果(对我而言)。我不完全了解这种90/180/360度的加法(我还没有看到他们加90度,或者为什么要加0度)。我较差的数学技能说,它的测量角度(旧的+ ve x轴,360/180-测量角度?)与360度之间的差,或者简而言之,就是未测量的角度(共180度或360度)。 ,结果并非总是预期的,很少出错,差异很大,完全错误。这是由于对atan / atan2结果添加了错误的度数。计算器站点使用的方法给出正确的结果,除了180度的直线(3'O时钟到9'O时钟线)外,它给出0度。 (不是180度吗?但是,我需要180度这样的直线)。该站点将结果加180度或360度以获得最终结果,并且是正确的,并且按照我的要求,期望使用3'O Clock ----> 9'O Clock线路盒。根据我的要求,9'O ---> 3'O的角度是正确的。为了弄清楚要添加到atan / atan2结果多少度,我目前正在寻找直线的斜率,并将0/90/180/360度添加到atan / atan2结果中,即使对于3'O时钟也要获得预期结果----> 9'O时钟线。还是有问题。
//PANE_HEIGHT is 960,the height and width of pane. PositionX is subtracted from it to get the //Cartesian plane coordinates instead of screen coordinates
currentJupiterangleRetroRough = (Math.todegrees(Math.atan((((PANE_HEIGHT - jupiterPositionY) - ( PANE_HEIGHT-earthPositionY))) / ((jupiterPositionX) - (earthPositionX)))));
//Finding the slope of the line
slope = (((PANE_HEIGHT-jupiterPositionY) - (PANE_HEIGHT-earthPositionY)) / ((jupiterPositionX) - (earthPositionX)));
//Adding required angles to output of atan to get final degrees,based on the slope
currentJupiterangleRetro = (Math.todegrees( Math.atan((((PANE_HEIGHT - jupiterPositionY) - ( PANE_HEIGHT-earthPositionY))) / ((jupiterPositionX) - (earthPositionX) )))) +
(slope<0 ? 360:(slope==0?0:(slope>0 & slope<1 ? 0:(slope>1 & slope<2 ? 90:180 ))));
//VarIoUs approaches to find the appropriate degrees to add to atan result
(slope<0 ? 360:180);
(slope<0 ? 360:(slope==0?0:180 ));
// Different One
// Another one
// and so on
(slope<0 ? 360:(slope==0?0:(slope>0 & slope<1 ? 0:(slope>1 & slope<2 ? 90:180 )))); //Improved one,still not fully correct
该应用程序可在任何日期/时间连续模拟行星的位置,并连续更新图形和文本中的位置和度数。因此,要花时间去发现计算是否错误,我确定是正确的,但是很难找到。除此之外,要计算的度数是正在退化的行星之间的角度(向后移动的错觉),因此很难通过与任何事物进行比较来发现计算误差。必须记录所有原始角度,位置,复古角度以进行控制台操作,并逐行准备好以查看大跳动,或者将手表原始角度记录下来并大致计算后视角度并进行验证。
花了整整两天的时间才能找到几乎完全正确的工作解决方案,并且再也无法拉扯我的头发了。已经阅读了关于StackOverflow的类似问题,甚至有人遇到了几乎相同的问题,但我相信没有答案,并且讨论继续在聊天中进行,但找不到更多信息。 我希望对数学精通的人会很容易。请提供完善的解决方案。预先感谢。
解决方法
首先,我可能不确定这是否是正确的解决方案。根据我对您的问题的理解,我试图解释如何计算点之间的角度。
要获得两个点之间的角度,您需要一个用于测量角度的第三个顶点。第一步是确定相对于特定坐标系的所有三个点的位置。可以说是根据窗格确定点的位置。
现在,通过选择所需的顶点,使用Point2D API计算三个点之间的角度。这将始终使您产生锐角。
下面是一个示例演示,它以太阳为顶点来计算两个行星之间的角度。
请注意,顶点不必是中心点。它可以是参考坐标系中的任何点。
我希望这可以帮助您入门:)
import javafx.animation.*;
import javafx.application.Application;
import javafx.beans.binding.DoubleBinding;
import javafx.geometry.Insets;
import javafx.geometry.Point2D;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.ToggleButton;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Line;
import javafx.stage.Stage;
import javafx.util.Duration;
public class EarthJupiterAngle_Demo extends Application {
@Override
public void start(Stage primaryStage) throws Exception {
VBox root = new VBox();
root.setSpacing(10);
root.setPadding(new Insets(10));
Scene scene = new Scene(root,600,600);
primaryStage.setScene(scene);
primaryStage.setTitle("Angle between Earth & Jupiter");
primaryStage.show();
Pane pane = new Pane();
pane.setPadding(new Insets(10));
pane.setStyle("-fx-border-width:1px;-fx-border-color:black;-fx-background-color:white;");
VBox.setVgrow(pane,Priority.ALWAYS);
Circle sun = new Circle(8,Color.ORANGE);
sun.centerXProperty().bind(pane.widthProperty().divide(2));
sun.centerYProperty().bind(pane.heightProperty().divide(2));
double earthAU = 100;
Circle earthOrbit = new Circle(earthAU);
earthOrbit.setFill(null);
earthOrbit.setStroke(Color.LIGHTBLUE);
earthOrbit.setStrokeWidth(1);
earthOrbit.getStrokeDashArray().addAll(10d,5d);
earthOrbit.centerXProperty().bind(sun.centerXProperty());
earthOrbit.centerYProperty().bind(sun.centerYProperty());
Circle earth = new Circle(5,Color.BLUE);
earth.layoutXProperty().bind(sun.centerXProperty());
earth.layoutYProperty().bind(sun.centerYProperty());
PathTransition earthRotate = new PathTransition();
earthRotate.setDuration(Duration.millis(10000));
earthRotate.setNode(earth);
earthRotate.setPath(earthOrbit);
earthRotate.setCycleCount(Animation.INDEFINITE);
earthRotate.setOrientation(PathTransition.OrientationType.ORTHOGONAL_TO_TANGENT);
earthRotate.setInterpolator(Interpolator.LINEAR);
earthRotate.play();
Line earthLine = new Line();
earthLine.startXProperty().bind(sun.centerXProperty());
earthLine.startYProperty().bind(sun.centerYProperty());
earthLine.endXProperty().bind(earth.layoutXProperty().add(earth.translateXProperty()));
earthLine.endYProperty().bind(earth.layoutYProperty().add(earth.translateYProperty()));
earthLine.setStroke(Color.GRAY);
earthLine.setStrokeWidth(1);
earthLine.getStrokeDashArray().addAll(10d,5d);
double jupiterAU = 180;
Circle jupiterOrbit = new Circle(jupiterAU);
jupiterOrbit.setFill(null);
jupiterOrbit.setStroke(Color.SANDYBROWN);
jupiterOrbit.setStrokeWidth(1);
jupiterOrbit.getStrokeDashArray().addAll(10d,5d);
jupiterOrbit.centerXProperty().bind(sun.centerXProperty());
jupiterOrbit.centerYProperty().bind(sun.centerYProperty());
Circle jupiter = new Circle(7,Color.BROWN);
jupiter.layoutXProperty().bind(sun.centerXProperty());
jupiter.layoutYProperty().bind(sun.centerYProperty());
PathTransition jupiterRotate = new PathTransition();
jupiterRotate.setDuration(Duration.millis(18000));
jupiterRotate.setNode(jupiter);
jupiterRotate.setPath(jupiterOrbit);
jupiterRotate.setCycleCount(Animation.INDEFINITE);
jupiterRotate.setOrientation(PathTransition.OrientationType.ORTHOGONAL_TO_TANGENT);
jupiterRotate.setInterpolator(Interpolator.LINEAR);
jupiterRotate.play();
Line jupiterLine = new Line();
jupiterLine.startXProperty().bind(sun.centerXProperty());
jupiterLine.startYProperty().bind(sun.centerYProperty());
jupiterLine.endXProperty().bind(jupiter.layoutXProperty().add(jupiter.translateXProperty()));
jupiterLine.endYProperty().bind(jupiter.layoutYProperty().add(jupiter.translateYProperty()));
jupiterLine.setStroke(Color.GRAY);
jupiterLine.setStrokeWidth(1);
jupiterLine.getStrokeDashArray().addAll(10d,5d);
DoubleBinding angle = new DoubleBinding() {
{
bind(earth.translateXProperty(),earth.layoutXProperty(),earth.translateYProperty(),earth.layoutYProperty(),jupiter.translateXProperty(),jupiter.layoutXProperty(),jupiter.translateYProperty(),jupiter.layoutYProperty());
}
@Override
protected double computeValue() {
// Sun position in pane
double sX = sun.getCenterX();
double sY = sun.getCenterY();
// Earth position in pane
double eX = earth.getLayoutX() + earth.getTranslateX();
double eY = earth.getLayoutY() + earth.getTranslateY();
// Jupiter position in pane
double jX = jupiter.getLayoutX() + jupiter.getTranslateX();
double jY = jupiter.getLayoutY() + jupiter.getTranslateY();
// Use Point2D API to calculate angle between three points
return Math.round(new Point2D(sX,sY).angle(new Point2D(eX,eY),new Point2D(jX,jY)));
}
};
Label angleLabel = new Label("Angle : ");
Label valLabel = new Label("");
Timeline angleTimeline = new Timeline(new KeyFrame(Duration.millis(100),e -> valLabel.setText(angle.get() + " deg")));
angleTimeline.setCycleCount(Animation.INDEFINITE);
angleTimeline.play();
pane.getChildren().addAll(earthLine,jupiterLine,sun,earthOrbit,earth,jupiterOrbit,jupiter);
ToggleButton button = new ToggleButton("Pause");
button.setPrefWidth(120);
button.selectedProperty().addListener((obs,old,selected) -> {
if (selected) {
button.setText("Play");
earthRotate.pause();
jupiterRotate.pause();
angleTimeline.pause();
} else {
button.setText("Pause");
earthRotate.play();
jupiterRotate.play();
angleTimeline.play();
}
});
HBox hb = new HBox(button,angleLabel,valLabel);
hb.setStyle("-fx-font-size:18px;");
hb.setAlignment(Pos.CENTER_LEFT);
hb.setSpacing(10);
root.getChildren().addAll(hb,pane);
}
}
,
您实际上有一个从地球到木星的向量,并且您想找到该向量的角度(即方向)。您还希望从X轴正方向逆时针测量角度。这意味着您可以使用两个矢量来测量同一角度,这两个矢量是您的矢量和正x方向的单位矢量。这很重要,因为它可以简化实现,因为:
- JavaFX具有
Point2D
类,该类可以表示向量并提供方便的方法(例如angle
)。 - 两个向量之间的角度使用反余弦。这将使我们得到一个介于
0
和180
度之间的值,我个人觉得它比-90
到90
度的反正切范围要容易得多。
例如,如果您有两个点,则可以使用以下方法计算隐式矢量和x轴正方向之间的角度:
/**
* Computes the angle (in degrees) of the vector from {@code p1} to {@code p2}. The angle
* will be in the range {@code 0} (inclusive) to {@code 360} (exclusive) as measured
* counterclockwise from the positive x-axis.
*
* @param p1 the start point of the vector
* @param p2 the end point of the vector
* @return the angle,in degrees,of the vector from {@code p1} to {@code p2} measured
* counterclockwise from the positive x-axis
*/
public static double computeAngleOfVector(Point2D p1,Point2D p2) {
Point2D vector = new Point2D(p2.getX() - p1.getX(),p2.getY() - p1.getY());
double angle = vector.angle(1.0,0.0);
if (vector.getY() > 0) {
// vector pointing downwards and thus is in the 3rd or 4th quadrant
return 360.0 - angle;
}
// vector pointing upwards and thus is in the 1st or 2nd quadrant
return angle;
}
请注意,我之所以使用vector.getY() > 0
而不是vector.getY() < 0
的原因是,与大多数(?)GUI框架一样,JavaFX的正y方向指向向下。根据您在模型中表示坐标系的方式,您可能需要稍微修改代码。
以下是我认为与您想要的方式相符的应用程序:
import javafx.animation.Animation;
import javafx.animation.Interpolator;
import javafx.animation.PathTransition;
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.DoubleBinding;
import javafx.beans.value.ObservableDoubleValue;
import javafx.geometry.Insets;
import javafx.geometry.Point2D;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Arc;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Line;
import javafx.scene.text.Font;
import javafx.scene.text.FontWeight;
import javafx.stage.Stage;
import javafx.util.Duration;
public class Main extends Application {
private static final double SCENE_WIDTH = 1000;
private static final double SCENE_HEIGHT = 700;
@Override
public void start(Stage primaryStage) {
Circle sun = createCelestialBody(50,Color.YELLOW);
Circle earth = createCelestialBody(20,Color.BLUE);
Circle earthOrbitIndicator = createOrbitIndicator(150);
Circle jupiter = createCelestialBody(35,Color.BROWN);
Circle jupiterOrbitIndicator = createOrbitIndicator(300);
Line earthJupiterVector = createBodyToBodyVector(earth,jupiter);
DoubleBinding angleObservable = createAngleBinding(earthJupiterVector);
Line xAxisIndicator = createXAxisIndicator(earth);
Arc angleIndicator = createAngleIndicator(earth,angleObservable);
Pane root =
new Pane(
createAngleLabel(angleObservable),earthOrbitIndicator,jupiterOrbitIndicator,jupiter,earthJupiterVector,xAxisIndicator,angleIndicator);
primaryStage.setScene(new Scene(root,SCENE_WIDTH,SCENE_HEIGHT));
primaryStage.setTitle("Earth-Jupiter Vector Angle");
primaryStage.setResizable(false);
primaryStage.show();
animateOrbit(Duration.seconds(7),earthOrbitIndicator.getRadius());
animateOrbit(Duration.seconds(16),jupiterOrbitIndicator.getRadius());
}
private Label createAngleLabel(ObservableDoubleValue angleObservable) {
Label label = new Label();
label.setPadding(new Insets(10));
label.setUnderline(true);
label.setFont(Font.font("Monospaced",FontWeight.BOLD,18));
label
.textProperty()
.bind(
Bindings.createStringBinding(
() -> String.format("Angle: %06.2f",angleObservable.get()),angleObservable));
return label;
}
private Circle createCelestialBody(double radius,Color fill) {
Circle body = new Circle(radius,fill);
body.setCenterX(SCENE_WIDTH / 2);
body.setCenterY(SCENE_HEIGHT / 2);
return body;
}
private Circle createOrbitIndicator(double radius) {
Circle indicator = new Circle(radius,Color.TRANSPARENT);
indicator.setStroke(Color.DARKGRAY);
indicator.getStrokeDashArray().add(5.0);
indicator.setCenterX(SCENE_WIDTH / 2);
indicator.setCenterY(SCENE_HEIGHT / 2);
return indicator;
}
private void animateOrbit(Duration duration,Circle celestialBody,double orbitRadius) {
Circle path = new Circle(SCENE_WIDTH / 2,SCENE_HEIGHT / 2,orbitRadius);
PathTransition animation = new PathTransition(duration,path,celestialBody);
animation.setCycleCount(Animation.INDEFINITE);
animation.setInterpolator(Interpolator.LINEAR);
animation.playFromStart();
}
private Line createBodyToBodyVector(Circle firstBody,Circle secondBody) {
Line vectorLine = new Line();
vectorLine.setStroke(Color.BLACK);
vectorLine.setStrokeWidth(2);
vectorLine.getStrokeDashArray().add(5.0);
vectorLine.startXProperty().bind(centerXInParentOf(firstBody));
vectorLine.startYProperty().bind(centerYInParentOf(firstBody));
vectorLine.endXProperty().bind(centerXInParentOf(secondBody));
vectorLine.endYProperty().bind(centerYInParentOf(secondBody));
return vectorLine;
}
private Line createXAxisIndicator(Circle anchor) {
Line xAxisIndicator = new Line();
xAxisIndicator.setStroke(Color.GREEN);
xAxisIndicator.setStrokeWidth(2);
xAxisIndicator.startXProperty().bind(centerXInParentOf(anchor));
xAxisIndicator.startYProperty().bind(centerYInParentOf(anchor));
xAxisIndicator.endXProperty().bind(xAxisIndicator.startXProperty().add(75));
xAxisIndicator.endYProperty().bind(xAxisIndicator.startYProperty());
return xAxisIndicator;
}
private Arc createAngleIndicator(Circle anchor,ObservableDoubleValue angleObservable) {
Arc arc = new Arc();
arc.setFill(Color.TRANSPARENT);
arc.setStroke(Color.RED);
arc.setStrokeWidth(2);
arc.getStrokeDashArray().add(5.0);
arc.centerXProperty().bind(centerXInParentOf(anchor));
arc.centerYProperty().bind(centerYInParentOf(anchor));
arc.setRadiusX(50);
arc.setRadiusY(50);
arc.setStartAngle(0);
arc.lengthProperty().bind(angleObservable);
return arc;
}
// NOTE: getCenterX() and getCenterY() were added in JavaFX 11. The calculations
// are simple,however. It's just (minX + maxX) / 2 and similar for y.
private DoubleBinding centerXInParentOf(Node node) {
return Bindings.createDoubleBinding(
() -> node.getBoundsInParent().getCenterX(),node.boundsInParentProperty());
}
private DoubleBinding centerYInParentOf(Node node) {
return Bindings.createDoubleBinding(
() -> node.getBoundsInParent().getCenterY(),node.boundsInParentProperty());
}
private DoubleBinding createAngleBinding(Line line) {
return Bindings.createDoubleBinding(
() -> {
Point2D vector =
new Point2D(line.getEndX() - line.getStartX(),line.getEndY() - line.getStartY());
double angle = vector.angle(1,0);
if (vector.getY() > 0) {
return 360 - angle;
}
return angle;
},line.startXProperty(),line.endXProperty(),line.startYProperty(),line.endYProperty());
}
}
示例如下: