在 p5js 中创建星星背后的数学是什么
What is the math behind creation of a star in p5js
我正在执行网站 p5js.org - https://p5js.org/examples/form-star.html 中的示例代码之一。我理解除以下几行之外的所有代码。
function star(x, y, radius1, radius2, npoints) {
let angle = TWO_PI / npoints;
let halfAngle = angle / 2.0;
beginShape();
for (let a = 0; a < TWO_PI; a += angle) {
let sx = x + cos(a) * radius2;
let sy = y + sin(a) * radius2;
vertex(sx, sy);
sx = x + cos(a + halfAngle) * radius1;
sy = y + sin(a + halfAngle) * radius1;
vertex(sx, sy);
}
endShape(CLOSE);
}
取两个角是什么概念。我知道这是使用极坐标到笛卡尔坐标的映射。但是我无法想象这个计算是如何工作的。作者是如何在代码中想出这个逻辑的?
作者正在画线。为此,他们需要 2 分。
为了得到这些点的坐标,他们“想象”了一个圆,因为如果星星是规则的,每个点到中心的距离应该相同(因此围绕星星的中点画一个假想的圆).像这样:
现在,一个圆是 360 度,或者以弧度表示,2 * PI。像这样:
注意:零弧度在右边。如果你逆时针阅读,当你在圆圈的“顶部”时,你会得到 1/2 PI,当你在左边时,你会得到 1 * PI,在底部时会得到 1,5 * PI,而在后面时会得到 2 * PI在右侧。
通过将圆除以星星的点数,作者现在可以使用三角学得到绘制星星所需的坐标:
就是这个主意。玩得开心!
我的回答只是对 laancelot 的出色回答 (+1) 的补充视觉解释:
看看这张图:
你可能习惯了笛卡尔坐标系,其中X轴为水平轴,Y为垂直于其的垂直轴,均以0,0为中心。
还有另一种方法可以从另一个角度查看相同的 x,y 笛卡尔坐标位置。
想象一条从中心到点 x,y 的直线,没有正方形网格。
如果该点在时钟上,您可以使用时间来描述该点
假设小时指向 x,y(指向它的角度)和时钟句柄
是到 x,y 的距离。
这是在极坐标系中查看相同坐标的示意图,
其中坐标不是 x,y 而是角度和半径(从中心到 x,y 的距离)。
在图表上您可以看到 AB 是从中心到光标的点。
回想一下旧的三角助记符:SOH-CAH-TOA(sin = opposite / hypothenuse,cos = adjacent / hypothenuse)。
如果我们知道一个点的角度和半径,我们就可以求解 x,y。
sin(angle) = BC (y) / AB (radius)
与
相同
sin(angle) / 1 = y / radius
我们可以从中提取:
y = sin(angle) * radius
同样
cos(angle) = AC (x) / AB (radius)
与
相同
cos(angle) / 1 = x / radius
我们可以从中提取:
x = cos(angle) * radius
因此极坐标(角度,半径)到笛卡尔(x,y)的转换公式:
x = cos(angle) * radius
y = sin(angle) * radius
奖励积分:现在您可以直观地了解 dist() 函数的工作原理。
用毕达哥拉斯定理求解圆心与鼠标位置构成的直角三角形的斜边很简单
AC = mouseX - centerX
BC = mouseY - centerY
dist = sqrt( (AB * AB) + (BC * BC) )
你可以在这里实际玩一下插图:
let showCartesian = true;
let showPolar = true;
let explanation = "cos(angle) = AC (x) / AB (radius)\n" +
"cos(angle) / 1 = x / radius\n" +
"x = cos(angle) * radius\n\n" +
"sin(angle) = BC (y) / AB (radius)\n" +
"sin(angle) / 1 = y / radius\n" +
"y = sin(angle) * radius\n\n";
function setup() {
createCanvas(600, 600);
}
function draw() {
background(255);
if(showCartesian) drawCartesianGrid(20,20,30);
if(showPolar) drawPolarGrid(300, 300, 30);
stroke(0);
// instructions
text("press 'c' to toggle cartesian grid\n" +
"press 'p' to toggle polar grid\n\n" + explanation, 10, 15);
stroke(0);
// center
let cx = width * 0.5;
let cy = height * 0.5;
// mouse
let x = mouseX;
let y = mouseY;
// cartesian to polar conversion (e.g. x,y to angle, radius )
let angle = atan2(y - cy, x - cx);
let radius = dist(cx, cy, x, y);
// polar to cartesian conversion
let px = cos(angle) * radius;
let py = sin(angle) * radius;
// visualise triangle
strokeWeight(3);
line(cx, cy, x, y);
strokeWeight(1);
line(cx, cy, x, cx);
line(x, cy, x, y);
text("x = " + nfc(x, 0) + ", y = " + nfc(y, 0), x, y - 12);
// visualise angle
noFill();
arc(cx, cy, radius * 0.25, radius * 0.25, angle < 0 ? angle : 0, angle < 0 ? 0 : angle);
text("angle: " + nfc(degrees(angle),2), cx + 12, cy - 12);
// visualise radius / hypothenuse / AB
push();
translate(cx, cy);
rotate(angle);
text("radius / AB / hypo.: " + nfc(radius, 2), radius * 0.25, -12);
pop();
// triangle corner labels
text("A", cx - 12, cy);
text("B", x + 12, y);
text("C", x + 12, cy);
// visualise cartesian coordinate point (offset from centre = same as x,y)
stroke(0,192,0);
ellipse(cx + px, cy + py, 30, 30);
}
function drawCartesianGrid(segsW, segsH, spacing){
stroke(198);
for(let y = 0; y < segsH; y++){
for(let x = 0; x < segsW; x++){
line(x * spacing, y * spacing,
(x+1) * spacing, y * spacing);
line(x * spacing, y * spacing,
x * spacing, (y+1) * spacing);
}
}
}
function drawPolarGrid(x,y,spacing){
let count = width / spacing;
let cx = width * 0.5;
let cy = height * 0.5;
stroke(192);
for(let i = 1 ; i <= count; i++){
ellipse(x, y, (spacing * 2) * i);
}
stroke(127);
line(cx, 0, cx, height);
line(0, cy, width, cy);
line(0, 0, width, height);
line(0, height, width, 0);
}
function keyPressed(){
if(key == 'c'){
showCartesian = !showCartesian;
}
if(key == 'p'){
showPolar = !showPolar;
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.0.0/p5.min.js"></script>
(注意 而不是数学 class 其中笛卡尔系统原点位于中心,Y 轴正向上,在 p5.js 0,0是左上角,y 向下增加。同样,注意从 -180 (-PI) 到 180 (PI) 的角度,指向右,而不是 0, 360 (0 - TWO_PI) 范围)
为了好玩,您可以注释掉在 star()
函数中绘制的顶点,以了解哪个点是哪个点,angle
/halfAngle
如何变化以及radius1
、radius2
对于硬核彻底检查,您可以使用 JS Debugger 在每个 vertex(sx, sy);
处放置一个断点,然后查看 angle/halfAngle
的变化。
我正在执行网站 p5js.org - https://p5js.org/examples/form-star.html 中的示例代码之一。我理解除以下几行之外的所有代码。
function star(x, y, radius1, radius2, npoints) {
let angle = TWO_PI / npoints;
let halfAngle = angle / 2.0;
beginShape();
for (let a = 0; a < TWO_PI; a += angle) {
let sx = x + cos(a) * radius2;
let sy = y + sin(a) * radius2;
vertex(sx, sy);
sx = x + cos(a + halfAngle) * radius1;
sy = y + sin(a + halfAngle) * radius1;
vertex(sx, sy);
}
endShape(CLOSE);
}
取两个角是什么概念。我知道这是使用极坐标到笛卡尔坐标的映射。但是我无法想象这个计算是如何工作的。作者是如何在代码中想出这个逻辑的?
作者正在画线。为此,他们需要 2 分。
为了得到这些点的坐标,他们“想象”了一个圆,因为如果星星是规则的,每个点到中心的距离应该相同(因此围绕星星的中点画一个假想的圆).像这样:
现在,一个圆是 360 度,或者以弧度表示,2 * PI。像这样:
注意:零弧度在右边。如果你逆时针阅读,当你在圆圈的“顶部”时,你会得到 1/2 PI,当你在左边时,你会得到 1 * PI,在底部时会得到 1,5 * PI,而在后面时会得到 2 * PI在右侧。
通过将圆除以星星的点数,作者现在可以使用三角学得到绘制星星所需的坐标:
就是这个主意。玩得开心!
我的回答只是对 laancelot 的出色回答 (+1) 的补充视觉解释:
看看这张图:
你可能习惯了笛卡尔坐标系,其中X轴为水平轴,Y为垂直于其的垂直轴,均以0,0为中心。
还有另一种方法可以从另一个角度查看相同的 x,y 笛卡尔坐标位置。
想象一条从中心到点 x,y 的直线,没有正方形网格。
如果该点在时钟上,您可以使用时间来描述该点 假设小时指向 x,y(指向它的角度)和时钟句柄 是到 x,y 的距离。
这是在极坐标系中查看相同坐标的示意图, 其中坐标不是 x,y 而是角度和半径(从中心到 x,y 的距离)。
在图表上您可以看到 AB 是从中心到光标的点。 回想一下旧的三角助记符:SOH-CAH-TOA(sin = opposite / hypothenuse,cos = adjacent / hypothenuse)。
如果我们知道一个点的角度和半径,我们就可以求解 x,y。
sin(angle) = BC (y) / AB (radius)
与
相同sin(angle) / 1 = y / radius
我们可以从中提取:
y = sin(angle) * radius
同样
cos(angle) = AC (x) / AB (radius)
与
相同cos(angle) / 1 = x / radius
我们可以从中提取:
x = cos(angle) * radius
因此极坐标(角度,半径)到笛卡尔(x,y)的转换公式:
x = cos(angle) * radius
y = sin(angle) * radius
奖励积分:现在您可以直观地了解 dist() 函数的工作原理。
用毕达哥拉斯定理求解圆心与鼠标位置构成的直角三角形的斜边很简单
AC = mouseX - centerX
BC = mouseY - centerY
dist = sqrt( (AB * AB) + (BC * BC) )
你可以在这里实际玩一下插图:
let showCartesian = true;
let showPolar = true;
let explanation = "cos(angle) = AC (x) / AB (radius)\n" +
"cos(angle) / 1 = x / radius\n" +
"x = cos(angle) * radius\n\n" +
"sin(angle) = BC (y) / AB (radius)\n" +
"sin(angle) / 1 = y / radius\n" +
"y = sin(angle) * radius\n\n";
function setup() {
createCanvas(600, 600);
}
function draw() {
background(255);
if(showCartesian) drawCartesianGrid(20,20,30);
if(showPolar) drawPolarGrid(300, 300, 30);
stroke(0);
// instructions
text("press 'c' to toggle cartesian grid\n" +
"press 'p' to toggle polar grid\n\n" + explanation, 10, 15);
stroke(0);
// center
let cx = width * 0.5;
let cy = height * 0.5;
// mouse
let x = mouseX;
let y = mouseY;
// cartesian to polar conversion (e.g. x,y to angle, radius )
let angle = atan2(y - cy, x - cx);
let radius = dist(cx, cy, x, y);
// polar to cartesian conversion
let px = cos(angle) * radius;
let py = sin(angle) * radius;
// visualise triangle
strokeWeight(3);
line(cx, cy, x, y);
strokeWeight(1);
line(cx, cy, x, cx);
line(x, cy, x, y);
text("x = " + nfc(x, 0) + ", y = " + nfc(y, 0), x, y - 12);
// visualise angle
noFill();
arc(cx, cy, radius * 0.25, radius * 0.25, angle < 0 ? angle : 0, angle < 0 ? 0 : angle);
text("angle: " + nfc(degrees(angle),2), cx + 12, cy - 12);
// visualise radius / hypothenuse / AB
push();
translate(cx, cy);
rotate(angle);
text("radius / AB / hypo.: " + nfc(radius, 2), radius * 0.25, -12);
pop();
// triangle corner labels
text("A", cx - 12, cy);
text("B", x + 12, y);
text("C", x + 12, cy);
// visualise cartesian coordinate point (offset from centre = same as x,y)
stroke(0,192,0);
ellipse(cx + px, cy + py, 30, 30);
}
function drawCartesianGrid(segsW, segsH, spacing){
stroke(198);
for(let y = 0; y < segsH; y++){
for(let x = 0; x < segsW; x++){
line(x * spacing, y * spacing,
(x+1) * spacing, y * spacing);
line(x * spacing, y * spacing,
x * spacing, (y+1) * spacing);
}
}
}
function drawPolarGrid(x,y,spacing){
let count = width / spacing;
let cx = width * 0.5;
let cy = height * 0.5;
stroke(192);
for(let i = 1 ; i <= count; i++){
ellipse(x, y, (spacing * 2) * i);
}
stroke(127);
line(cx, 0, cx, height);
line(0, cy, width, cy);
line(0, 0, width, height);
line(0, height, width, 0);
}
function keyPressed(){
if(key == 'c'){
showCartesian = !showCartesian;
}
if(key == 'p'){
showPolar = !showPolar;
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.0.0/p5.min.js"></script>
(注意 而不是数学 class 其中笛卡尔系统原点位于中心,Y 轴正向上,在 p5.js 0,0是左上角,y 向下增加。同样,注意从 -180 (-PI) 到 180 (PI) 的角度,指向右,而不是 0, 360 (0 - TWO_PI) 范围)
为了好玩,您可以注释掉在 star()
函数中绘制的顶点,以了解哪个点是哪个点,angle
/halfAngle
如何变化以及radius1
、radius2
对于硬核彻底检查,您可以使用 JS Debugger 在每个 vertex(sx, sy);
处放置一个断点,然后查看 angle/halfAngle
的变化。