背景
在某短视频上刷到一个万花轮的视频,优美的线条一下把老夫拉回了2005年,小时候的女神小花拿着她的新玩具跟我说,看我给你画一朵超级漂亮的花
不得不说,这东西虽小但对于当时的我来说却是非常震撼,不愧是我的女神能拥有这么神奇的玩意。那个时候的我一定万万没想到,时隔多年后,老夫竟然会用程序去破解女神的“魔法”。
不记得哪个伟人说过,看似复杂的表面背后的原理往往很简单。
好的,仔细观察一下,发现确实很简单只有两点需要解决就能实现这个神奇的效果:
- 推导出笔尖的点和旋转角的公式,即可根据角度的旋转获得所有点,连接成线即可。
- 手指在屏幕上滑动时,根据上一个点和当前点的坐标计算出旋转的角度。
知识储备
内旋轮
内旋轮线(hypotrochoid)是追踪附着在围绕半径为 R 的固定的圆内侧滚转的半径为 r 的圆上的一个点得到的转迹线,这个点到内部滚动的圆的中心的距离是d。
公式推导视频: 【数学】美妙的万花尺与旋轮线(下) | 使用manim 自制的3b1b风格数学视频_哔哩哔哩_bilibili
至于怎么推导出来的大家看这个视频就行了,up主讲的非常明白。直接贴公式:
这里我们得到坐标和θ角度的参数方程为:
x=(R-r)cos(θ)+dcos((R-r)/r*θ)
y=(R-r)sin(θ)-dsin((R-r)/r*θ)
夹角计算
万花轮的转动都靠他来计算转动角度
第一个参数传入中心点,第二个参数是开始点,第二个参数是结束点。
double angle(Offset cen, Offset first, Offset second) {
double dx1, dx2, dy1, dy2;
dx1 = first.dx - cen.dx;
dy1 = first.dy - cen.dy;
dx2 = second.dx - cen.dx;
dy2 = second.dy - cen.dy;
// 计算三边的平方
double ab2 = (second.dx - first.dx) * (second.dx - first.dx) +
(second.dy - first.dy) * (second.dy - first.dy);
double oa2 = dx1 * dx1 + dy1 * dy1;
double ob2 = dx2 * dx2 + dy2 * dy2;
// 根据两向量的叉乘来判断顺逆时针
bool isClockwise = ((first.dx - cen.dx) * (second.dy - cen.dy) -
(first.dy - cen.dy) * (second.dx - cen.dx)) >
0;
// 根据余弦定理计算旋转角的余弦值
double cosDegree =
(oa2 + ob2 - ab2) / (2 * Math.sqrt(oa2) * Math.sqrt(ob2));
// 异常处理,因为算出来会有误差绝对值可能会超过一,所以需要处理一下
if (cosDegree > 1) {
cosDegree = 1;
} else if (cosDegree < -1) {
cosDegree = -1;
}
// 计算弧度
double radian = Math.acos(cosDegree);
// 计算旋转过的角度,顺时针为正,逆时针为负
return isClockwise ? radian : -radian;
}
接下来就可以为所欲为了