我们如何编写模拟时钟的角度表达式?

问题描述

这是我所提到的书中作为解决方案给出的表达,但似乎超出了我的理解。请任何人帮助理解我们如何准确地写出这个角度?为什么要减去 pi / 2 ?我真的不知道

xs=(int)(Math.cos(s*3.14f/30-3.14f/2)*45+xcentre);
ys=(int)(Math.sin(s*3.14f/30-3.14f/2)*45+ycentre);
xm=(int)(Math.cos(m*3.14f/30-3.14f/2)*40+xcentre);
ym=(int)(Math.sin(m*3.14f/30-3.14f/2)*40+ycentre);
xh=(int)(Math.cos((h*30+m/2)*3.14f/180-3.14f/2)*30+xcentre);
yh=(int)(Math.sin((h*30+m/2)*3.14f/180-3.14f/2)*30+ycentre);

解决方法

似乎是在给定以原点为中心的三个时钟指针两端的直角坐标,向上为负y,向右为正x。我可以通过尝试一些值来说明这一点。

我将您的基本代码包装如下:

static class ClockCoords {
    int xs,ys;
    int xm,ym;
    int xh,yh;

    public String toString() {
        return String.format("h: %3d,%3d m: %3d,%3d s: %3d,%3d",xh,yh,xm,ym,xs,ys);
    }
}

public static ClockCoords coords(int h,int m,int s) {
    ClockCoords r = new ClockCoords();
    int xcentre = 0;
    int ycentre = 0;
    r.xs = (int) (Math.cos(s * 3.14f / 30 - 3.14f / 2) * 45 + xcentre);
    r.ys = (int) (Math.sin(s * 3.14f / 30 - 3.14f / 2) * 45 + ycentre);
    r.xm = (int) (Math.cos(m * 3.14f / 30 - 3.14f / 2) * 40 + xcentre);
    r.ym = (int) (Math.sin(m * 3.14f / 30 - 3.14f / 2) * 40 + ycentre);
    r.xh = (int) (Math.cos((h * 30 + m / 2) * 3.14f / 180 - 3.14f / 2) * 30 + xcentre);
    r.yh = (int) (Math.sin((h * 30 + m / 2) * 3.14f / 180 - 3.14f / 2) * 30 + ycentre);
    return r;
}


public static void main(String[] args) throws IOException {
    System.out.println(coords(0,0));
    System.out.println(coords(0,45));
    System.out.println(coords(6,0));
    System.out.println(coords(6,30,0));
}

这给了我这个

h:   0,-29 m:   0,-39 s:   0,-44
h:   0,-39 s: -44,0
h:   0,29 m:   0,-44
h:  -7,28 m:   0,39 s:   0,-44

因此,如果我还没弄错的话,时针长29单位,分针长39单位,秒针长44单位。在一天的开始,第一个条目,他们都直指上。在第二天进入日期45秒后,秒针指向左侧,但在给定整数粒度和不太长的手的情况下,其他两只手仍指向上。第三个条目6:00只是将时针翻转指向下方。第四项很有趣,即6:30,它使分针指向下方,但时针也向左和向上移动了一点……我们的第一只手不在与中心成90度角的位置。有趣的东西。至于所有这些的原因...我认为这都是基本的三角学和笛卡尔数学。

等等,有一件事我不明白...我在公式中看到每只手的长度不同。为什么我从公式中看到的手长只有1?我的猜测是,它与所使用的PI非常近似。我认为这些手并没有指向正上方。这就是我能想到的。也许明天我会尝试为PI插入更高的价值。

,

让我也为您提供一些背景信息。

三角学

enter image description here

都是围绕一个圆,半径为1个单位,以Origo为中心,即点(0,0)。圆上一点的坐标可以计算为x =cosα,y =sinα,其中α是角度。由于圆的所有点到中心的距离相等,因此您只需要知道角度即可计算坐标,即(cos(alpha),sin(alpha))。

原因:看一下Pythagoras's theorem,这是law of cosines的结果,其中三角形的最大角度为90度。

毕达哥拉斯定理指出

enter image description here

其中a,b和c是三角形的边长,三角形是垂直的,并且 a b 为真。因此,cos和sin是由于三角坐标。

弧度,PI / 2

角度单位可以度(360度为整圆)或弧度(2 PI为整圆)进行测量。让我们谈谈弧度。在数学上,0弧度是圆的最高点的角度。 pi / 2是最左侧的。 pi是最低的。 3pi / 2是最右边的。请注意,随着角度的增加,您的点将逆时针移动。

由于这是周期性的,所以确实如此

2 * k * pi + alpha = alpha

在屏幕上,y指向下方,而数学标准中y顶点指向上方。因此,如果您从0弧度开始,那将是在屏幕上显示时的最低点,并且由于倒数y,随着角度的增加,您将朝顺时针方向移动。从0删除pi / 2会将您指向最右边。

变量

  • (xh,yh)是小时坐标
  • (xm,ym)是分钟坐标
  • (xs,ys)是第二个坐标

它们是传统时钟的指针。

  • h是小时角
  • m是分钟角度
  • s是第二个角度

(xcentre,y​​centre)是中心的坐标。它们可能不同于(0,0),因此坐标移动,支持所谓的平移。

步长

一分钟有60秒,一小时有60分钟,所以角度步长必须相同,即使一天中有24小时,步长的频率也不同,因此,小时的角度步长既不应该频繁出现,又要更大(传统时钟上为12个位置)。

手长

时针,分针和秒针(无双关)的长度不同,这就是为什么将三角圆的单位分别乘以45、40和30的原因。

随着时间的流逝,大概角度会发生变化,随后其坐标也会发生变化。

,

一个不那么详尽的解释,只看几秒钟:

xs = (int)(Math.cos(s*3.14f/30-3.14f/2)*45+xcentre);
ys = (int)(Math.sin(s*3.14f/30-3.14f/2)*45+ycentre);

正如Steve's answer所指出的那样,从(xcentre,ycentre)(xs,ys)画一条线将显示秒针,“ up”为“ y = 0”-从而使轴反转比较传统地块。这实际上对我们有利:在正常情况下,随着角度的增加,它们将显示为逆时针方向。由于y轴的屏幕坐标是反向的,因此这对我们来说很好:我们的手将按预期的顺时针方向前进。

让我们使用xs看一下数学:

xs = Math.cos(s*3.14f/30-3.14f/2)*45;          // removed rounding & using xcentre=0
xs = Math.cos((s/30)*PI - PI/2) * sHandLength; // use PI=3.14...,sHandLength=45 

现在,在时间0,我们希望秒针朝上看。但通常是cos(0) = 1。如果我们减去90º(PI/2),它会一直向上看:

xs = Math.cos((s/30)*PI - zeroIsUp) * sHandLength;

我们可以撤消此操作以继续推理其余表达式:

xs = Math.cos((s/30)*PI);                      // sHandlength of 1,time 0 is left

所缺少的只是那个神秘的s/30*PI。我们来做一张小桌子:

seconds(s)        radians(argument to Math.cos())
0                 0
15                PI/2       -- a quarter-turn of the hand
30                PI         -- a half-turn
45                3*PI/2     -- a three-quarters turn
60                2*PI,or 0 -- a full turn

因此,看来(s/30) * PI简直是(s/60) * (2*PI)的缩写。

对于一本有关Java的书,我期望有一种更清晰的书写方式。我个人的偏好是:

xs = (int)(Math.cos(base60toRadians(s) - zeroIsUp) * secondHandLength + xCentre);
ys = (int)(Math.sin(base60toRadians(s) - zeroIsUp) * secondHandLength + yCentre);

// ... somewhere else
private float base60toRadians(int b60) { return (b60 / 60) * (2 * Math.PI); } 
,

时钟中心的坐标为(xcentre,y​​centre)。 通过将几何仿射平移到中心,您可以像从(0,0)进行计算。

指针沿方向顺时针旋转。

现在,钟针的顶部为(0,1),角度为π/ 2(90°)。 (1,0)是角度开始的地方,(-1,0)的中途是角度π(180°)。 360°的完整角度为2π,其1/60 变为:

秒:60秒=2π;角2π/ 60 =π/ 30

double startAngle = Math.PI / 2; // Value 0 has angle π/2
double rs = 45;
xs = (int)(Math.cos(-s * Math.PI / 30 - startAngle) * rs + xcentre);
ys = ...        sin ...

小时:24小时=2π;角2π/ 24 =π/ 12