Android 用三角函数基础

5,059 阅读7分钟

不管你是想用 canvas 自己随心画,还是因为看不懂别人 canvas 操作的小伙伴,今天你都来对地方了,这里有最基础的教学,带你重温恐怖的初中生活,另外后面有时间我会总结些三角函数在 canvas 中的实际应用范例

自定义 view 中最挠头的就是明明我看了 canvas 所有 API,但我还是依然看不懂别人是怎么绘制的图形,别人 Demo 中传的参数、+- 总是和我想的不一样,如果你有这种疑惑,那么对了,因为你差的就是三角函数基础了,这些初中的知识再一次折磨了我们 (~o ̄3 ̄)~ 搞定三角函数,自定义 view 不再是梦了

学习资料

坐标系

1. 直角坐标系

这个我们应该最清楚不过,区别是 Android 中坐标原点在:左上角,但是这依然有意义。我们把不想要的画在 -值坐标上那么就不会显示,做动画时这很有必要。或者我们在 canvas 中 moveto 到一个新的点,那么就和下图很像了

d50735fae6cd7b8945d1f1b5042442a7d9330e43.webp

2. 象限

直角坐标系的两个坐标轴将平面分成了四个部分,称为:象限,象限的编号是按照 逆时针方向 排列的

f603918fa0ec08fa76d2d89152ee3d6d55fbda32.webp

3. 空间直角坐标系

三条数轴互相垂直的笛卡尔坐标系被称为空间笛卡尔直角坐标系,否则被称为空间笛卡尔斜角坐标系

在x轴和y轴的基础上,增加了指向空间属性的z轴,z轴要符合右手规则:即以右手握住z轴,当右手的四指从正向x轴以π/2角度转向正向y轴时,大拇指的指向就是z轴的正向,这样的三条坐标轴就组成了一个空间直角坐标系点O叫做坐标原点,这样就构成了一个笛卡尔坐标

a8ec8a13632762d0d57d6e89abec08fa513dc641.webp

正角、负角

  • 正角 -- 逆时针旋转
  • 负角 -- 顺时针旋转

canvas 里角度的 +- 我懵逼了很久 ヽ(*。>Д<)o゜

Snip20210811_4.png

角度、弧度

角度、弧度都是为了描述圆的

  • 角度 -->
    • 单词:degree
    • 符号:°
    • 这个大家都知道,把圆分成 360 分,每一分对应的圆心角就是 1°
  • 弧度 -->
    • 单词:radian
    • 符号:rad
    • 当弧长=半径时,把对应的圆心角称为 1rad

Snip20210827_1.png

所以大家看啊,弧度其实还是角度

  • 关于角度和弧度,有个公式:
1rad * π = 180°
  • 角度和弧度相互转换
1(弧度) = 180 / π (角度)
1(角度) = π / 180 (弧度)

弧度60,求角度 --> 60 * 180 / π
角度45,求弧度 --> 45 * π / 180

为什么要讲弧度和角度呢,有个非常现实的需要,Math 三角函数中,sin、cos、tan 函数参数使用的都是弧度,所以弧度转角度,角度转弧度,算弧长就必须我们了解清楚这里

我们来看看 Math 中角度、弧度换转的函数:

public static double toDegrees(double angrad) {
    return angrad * 180.0 / PI;
}

public static double toRadians(double angdeg) {
    return angdeg / 180.0 * PI;
}

三角函数

1. 直角三角形

有一点必须明确:所有三角形的计算,都源于直角三角形,直角三角形中的定理,就叫做:勾股定理

借用掘金上笔友的一张图 --> 721aff8740a3418ba55526fbbb7fbcf6_tplv-k3u1fbpfcp-zoom-crop-mark_1304_1304_1304_734.webp

这张图很形象,很实际,canvas 中我们绘制图形,最离不开的就是圆了,我们需要借助圆来实现各种弧线、path

1.1 正函数

先看图:

1785445-9d8ceec8a63815f5-1.webp

勾股定理就是下图中的,有没有的我都加上了

image.png

所谓的 正函数 就是借助角度来算各种边,大家下面的公式熟悉了,在知道角度的情况下就可以计算各个边了,甚至我们在不知道角度时,借助多个相互关联的三角形也是可以把边都计算出来,三角形中的边就是坐标系中点的 x/y

  • 2个直角边的平方和 = 斜边的平方和
  • sin -- 正玄,对角边 / 斜边
  • cos -- 余玄,临角边 / 斜边
  • tan -- 正切,直角边 / 临角边

1.2 反函数

反函数的定义就是正函数的反面,大家能猜的到吗?其实很简单,我们来看一个 sin 函数:

sinA = 2/5

那么我问大家,现在你知道了 sinA 的数值,那么你能算出 A 这个角度的大小吗,不能吧,所以反函数的意思就在这里

反函数:一旦知道 sin、cos、tan 的大小,通过反函数就能计算出对应角的大小,计算出来的角的大小是弧度值表示的,取值范围:[-π/2,π/2],换算成角度就是 [-90°,90°],因为三角函数都是在直接三角形中计算的,所以参与计算的角的大小都不会超过90°,注意反函数实在正函数前加 a 来表示的:asin、acos、atan

那么上面的例子,反函数应该是:

A = asin(2/5)

2. 普通三角形

详细请看:初中数学常考题型,已知三角形三边长,求角度

勾股定理只能用在直角三角形中,那么一般的三角形怎么计算呢,思路就是延长三角形的2个斜边,形成新的直角三角形,看图

  • 目标三角形 image.png
  • 延长斜边新城新的三角形 image.png image.png
  • 所以最后: image.png

这算一个思路吧,别到时候遇到了大家不会算

Math 函数

Math 这个类,以前我也是很陌生的,除了 random 随机数之外就没用过,但是一来到 canvas 自定义绘制环节,Math 的主战场就到了。很久以前我看大牛们自定义 view 中各种操作就是看不懂,不光 Math 看不懂,就是百度之后,知道这个方法干啥的之后我也是看不懂。根结就在于我们把三角函数全忘光了,再学一边三角函数,再把 Math 搞定,那我们就不会囧了

1. E、PI

Math 类里有2个常量:E、PI,PI=3.1415926,大家知道就行

2. 最大值、最小值、绝对值

image.png

System.out.println("10 和 20 的较大值:" + Math.max(10, 20));
System.out.println("15.6 和 15 的较小值:" + Math.min(15.6, 15));
System.out.println("-12 的绝对值:" + Math.abs(-12));
10和20的较大值:20
15.6和15的较小值:15.0
-12的绝对值:12

3. 求整数

static double ceil(double a)返回大于或等于 a 的最小整数,理解为向上取整
static double floor(double a)返回小于或等于 a 的最大整数,理解为向下取整
static double rint(double a)返回最接近 a 的整数值,如果有两个同样接近的整数,则结果取偶数
static int round(float a)四舍五入
static long round(double a)四舍五入
  • rint -- 四舍五入,但是在0.5时,取偶数,比如 rint(24.5)=24
  • round -- 真*四舍五入,round(24.5)=25
Scanner input = new Scanner(System.in);
System.outprintln("请输入一个数字:");
double num = input.nextDouble();

System.out.println("大于或等于 "+ num +" 的最小整数:" + Math.ceil(num));
System.out.println("小于或等于 "+ num +" 的最大整数:" + Math.floor(num));
请输入一个数字:
99.01
大于或等于 99.01 的最小整数:100.0
小于或等于 99.01 的最大整数:99.0

4. 三角函数

sin、cos,tan 大家要熟悉,+a 的是求反值的,toDegree() 弧度转角度,toRndian() 角度转弧度

image.png

System.out.println{"90 度的正弦值:" + Math.sin(Math.PI/2));
System.out.println("0 度的余弦值:" + Math.cos(0));
System.out.println("1 的反正切值:" + Math.atan(l));
System.out.println("120 度的弧度值:" + Math.toRadians(120.0));
90 度的正弦值:1.0
0 的余弦值:1.0
1 的反正切值:0.7853981633974483
120 度的弧度值:2.0943951023931953

5. 指数运算

image.png

System.out.println("4 的立方值:" + Math.pow(4, 3));
System.out.println("16 的平方根:" + Math.sqrt(16));
System.out.println("10 为底 2 的对数:" + Math.log1O(2));
4 的立方值:64.0
16 的平方根:4.0
10 为底 2 的对数:0.3010299956639812

6. atan/atan2

这一组函数是 canvas 中的应用难点,好多人搞不清楚,看别人的 Demo 也搞不懂这个函数求的啥,为啥用 atan2,所以大家请注意,下面的代码自己跑一下,画画图体会下,我也是自己瞎琢磨

atan/atan2 是一组反函数,反的是 tan 函数,那么反过来求的是:该切角的角度,但是为啥有2个函数呢

注意 atan/atan2 返回的是 斜线 与 正 x 轴 之间的圆心角角度

  • atan:
    • tant(1),使用 tan 值
    • 函数取值【-90,90】之间,若你的角是大于 90 的话,这个值就不准了,比如你的角是 135度
  • atan2:
    • tant2(y,x) --> tant2(7,7)
    • 可以直接使用点的横纵坐标
    • 函数取值【-180,180】之间,第一、第二象限返回的是:0-180,第一、第二象限返回的是:0-(-180)

详细请看下图:

image.png

没画好,有什么好的在线画图网站吗?

假设点 A(7,7)

  • 使用 Math.toDegrees(Math.atan(7/7)) 计算出来的是45度角
  • 使用 Math.toDegrees(Math.atan2(7,7)) 计算出来的是45度角

相比较起来,还是 atan2 更方便使用一些,直接使用点的坐标就可以了,不用对像 tan 一样还得想一下谁除谁

我们选择一些点,大家看一下 atan2 取值范围,包含的点:(7,7),(7,-7),(-7,7),(-7,-7),(7,0)

Log.d("AAA", "atan2(7:7) --> ${Math.toDegrees(Math.atan2(7.0, 7.0))}")
Log.d("AAA", "atan2(-7:7) --> ${Math.toDegrees(Math.atan2(7.0, -7.0))}")
Log.d("AAA", "atan2(7:-7) --> ${Math.toDegrees(Math.atan2(-7.0, 7.0))}")
Log.d("AAA", "atan2(-7:-7) --> ${Math.toDegrees(Math.atan2(-7.0, -7.0))}")
Log.d("AAA", "atan2(7:0) --> ${Math.toDegrees(Math.atan2(0.0, 7.0))}")
D/AAA: atan2(7:7) --> 45.0
D/AAA: atan2(-7:7) --> 135.0
D/AAA: atan2(7:-7) --> -45.0
D/AAA: atan2(-7:-7) --> -135.0
D/AAA: atan2(7:0) --> 0.0

上面 tant2 我们算的是 点到坐标原点之间的斜线的角度,那要是不是坐标原点呢?

只需要将两点x,y坐标分别相减得到一个新的点(x2-x1,y2-y1).然后利用他求出角度就可以了

一定要注意:要大值减小值,要不用 atan2 会出现方向相反的问题,不确定的你就用 abs 绝对值,看例子:

Log.d("AAA", "atan2(7:7):(3,3) --> ${Math.toDegrees(Math.atan2(-4.0, -4.0))}")
Log.d("AAA", "atan2(3:3):(7,7) --> ${Math.toDegrees(Math.atan2(4.0, 4.0))}")
D/AAA: atan2(7:7):(3,3) --> -135.0
D/AAA: atan2(3:3):(7,7) --> 45.0

关于 atan/atan2 取的是哪个角的问题,还有不明白的同学请看这个测试:

Log.d("AAA", "atan2(7,5) --> ${Math.toDegrees(Math.atan2(5.0, 7.0))}")
Log.d("AAA", "atan2(-7,5) --> ${Math.toDegrees(Math.atan2(5.0, -7.0))}")
D/AAA: atan2(7,5) --> 35.53767779197438
D/AAA: atan2(-7,5) --> 144.46232220802563
  • (7,5) 这个点在第一象限形成的切角应该 < 45度
  • (-7,5) 这个点在第一象限形成的切角应该 > 135度

大家看上面的测试,第一象限是 35度角,第二象限是 145度角,大家自己画图对比下