英文:
How to calculate an optical axis line for planetary orbits around the sun to earth in HTML5
问题
我正在从地球的角度创建一个内部太阳系模型,以用于制作占星术的出生图参考。我能够绘制地球-太阳轴和地球-月球轴的光学轴线,但我对剩余的内部行星的三角法一无所知。
以下是一个工作示例。
https://codepen.io/pjdroopypants/pen/wvEKeOm
window.onload = function(){
var canvas = document.getElementById("canvas"),
ctx = canvas.getContext("2d"),
cw = canvas.width,
ch = canvas.height,
time = 1;
function circle(radius,color,x,y){
ctx.beginPath();
ctx.fillStyle = color;
ctx.arc(x,y,radius,0,2*Math.PI,true);
ctx.fill();
ctx.closePath();
}
function line(color,ax,ay,bx,by){
ctx.beginPath();
ctx.moveTo(ax*2,ay);
ctx.lineTo(bx+0.5,by+0.5);
ctx.strokeStyle = color;
ctx.stroke();
ctx.closePath();
}
var sunDegree = Math.floor(Math.random() * 359) + 1; // 临时的,将使用来自实时行星数据的函数
var merDegree = Math.floor(Math.random() * 359) + 1; // 临时的,将使用来自实时行星数据的函数
var venDegree = Math.floor(Math.random() * 359) + 1; // 临时的,将使用来自实时行星数据的函数
var marDegree = Math.floor(Math.random() * 359) + 1; // 临时的,将使用来自实时行星数据的函数
var mooDegree = Math.floor(Math.random() * 359) + 1; // 临时的,将使用来自实时行星数据的函数
var interval = 570.0930061551268;
function animate(){
ctx.save();
ctx.clearRect(0, 0, 1100, 1100);
ctx.translate(cw/2,ch/2);
//地球
ctx.rotate(-(time / interval)+ Math.PI);
circle(15,"blue",0,0);
ctx.translate(480,0);
line("blue",-240,0,0,0);
ctx.translate(-480,0);
ctx.rotate((time / interval)+ Math.PI);
//月球
var moontime = (time / (interval / 13.36996336996337)+mooDegree);
ctx.rotate(-moontime);
ctx.translate(23,0);
circle(3,"black",0,0);
ctx.translate(457,0);
line("#6a6a6a",-230,0,0,0);
ctx.translate(-480,0);
ctx.rotate(moontime);
//太阳
var suntime = time / interval;
ctx.rotate(-suntime);
ctx.translate(120,0);
circle(15,"yellow",0,0);
ctx.translate(360,0);
line("yellow",-182,0,0,0);
ctx translate(-360,0);
ctx.rotate(suntime);
//水星
var mertime = (time / (interval / 4.150568181818182))+merDegree;
ctx.rotate(-(time / (interval / 4.150568181818182))+merDegree-suntime);
ctx.translate(40,0);
circle(15,"#898989",0,0);
ctx.translate(-40,0);
ctx.rotate((time / (interval / 4.150568181818182))+merDegree+suntime);
//金星
ctx.rotate(-(time / (interval / 1.625500667556742))+venDegree-suntime);
ctx.translate(80,0);
circle(15,"#b9955b",0,0);
ctx.translate(-80,0);
ctx.rotate((time / (interval / 1.625500667556742))+venDegree);
//火星
ctx.rotate(-(time / (interval / 0.5316593886462882))+marDegree);
ctx.translate(160,0);
circle(15,"#9f5e13",0,0);
ctx.translate(-160,0);
ctx.rotate((time / (interval / 0.5316593886462882))+marDegree);
ctx.restore();
time++;
window.requestAnimationFrame(animate);
}
window.requestAnimationFrame(animate);
}
我尝试在每次动画迭代中反向旋转,通过减去太阳的旋转角度,但这只会使轴线水平,而不会回到中心。
英文:
I am creating an inner solar sytem model from the percpective of Earth for creating astology birthcharts references. I am able to draw an optical axis line for the Earth - Sun axis, and the Earth - Moon axis but I can not for the life of me figure the trigonometry for the remaining inner planets.
Here is a working pen.
https://codepen.io/pjdroopypants/pen/wvEKeOm
window.onload = function(){
var canvas = document.getElementById("canvas"),
ctx = canvas.getContext("2d"),
cw = canvas.width,
ch = canvas.height,
time = 1;
function circle(radius,color,x,y){
ctx.beginPath();
ctx.fillStyle = color;
ctx.arc(x,y,radius,0,2*Math.PI,true);
ctx.fill();
ctx.closePath();
}
function line(color,ax,ay,bx,by){
ctx.beginPath();
ctx.moveTo(ax*2,ay);
ctx.lineTo(bx+0.5,by+0.5);
ctx.strokeStyle = color;
ctx.stroke();
ctx.closePath();
}
var sunDegree = Math.floor(Math.random() * 359) + 1; //temporary, will use function from live planetary data
var merDegree = Math.floor(Math.random() * 359) + 1; //temporary, will use function from live planetary data
var venDegree = Math.floor(Math.random() * 359) + 1; //temporary, will use function from live planetary data
var marDegree = Math.floor(Math.random() * 359) + 1; //temporary, will use function from live planetary data
var mooDegree = Math.floor(Math.random() * 359) + 1; //temporary, will use function from live planetary data
var interval = 570.0930061551268;
function animate(){
ctx.save();
ctx.clearRect(0, 0, 1100, 1100);
ctx.translate(cw/2,ch/2);
//Earth
ctx.rotate(-(time / interval)+ Math.PI);
circle(15,"blue",0,0);
ctx.translate(480,0);
line("blue",-240,0,0,0);
ctx.translate(-480,0);
ctx.rotate((time / interval)+ Math.PI);
//Moon
var moontime = (time / (interval / 13.36996336996337)+mooDegree);
ctx.rotate(-moontime);
ctx.translate(23,0);
circle(3,"black",0,0);
ctx.translate(457,0);
line("#6a6a6a",-230,0,0,0);
ctx.translate(-480,0);
ctx.rotate(moontime);
//Sun
var suntime = time / interval;
ctx.rotate(-suntime);
ctx.translate(120,0);
circle(15,"yellow",0,0);
ctx.translate(360,0);
line("yellow",-182,0,0,0);
ctx.translate(-360,0);
ctx.rotate(suntime);
//Mercury
var mertime = (time / (interval / 4.150568181818182))+merDegree;
ctx.rotate(-(time / (interval / 4.150568181818182))+merDegree-suntime);
ctx.translate(40,0);
circle(15,"#898989",0,0);
ctx.translate(-40,0);
ctx.rotate((time / (interval / 4.150568181818182))+merDegree+suntime);
//Venus
ctx.rotate(-(time / (interval / 1.625500667556742))+venDegree-suntime);
ctx.translate(80,0);
circle(15,"#b9955b",0,0);
ctx.translate(-80,0);
ctx.rotate((time / (interval / 1.625500667556742))+venDegree);
//Mars
ctx.rotate(-(time / (interval / 0.5316593886462882))+marDegree);
ctx.translate(160,0);
circle(15,"#9f5e13",0,0);
ctx.translate(-160,0);
ctx.rotate((time / (interval / 0.5316593886462882))+marDegree);
ctx.restore();
time++;
window.requestAnimationFrame(animate);
}
window.requestAnimationFrame(animate);
}
I attempted to reverse rotate each animation iteration by subtracting the suns rotating angle but that just makes the axis line horizontal and not back to center.
答案1
得分: 0
## 使用对象封装
你的代码很混乱
你需要封装各个部分,以便可以单独处理它们。
下面的示例使用工厂模式定义了行星,以创建行星对象。
每个行星都有轨道角度、速率和与其轨道的距离。例如,水星、金星、地球、火星都绕太阳运动,月球绕地球运动。
行星按照轨道顺序创建。也就是说,在创建行星之前,不能创建其轨道上的行星。
在每一帧动画中,每个行星的位置都相对于其轨道上的行星按照创建顺序计算。
在渲染行星之前,我们想要将视图居中在所选行星上(在本例中为地球)。居中的行星位置用于创建一个全局矩阵,将所有行星移动到正确的位置。
然后在绘制每个行星时,将全局矩阵与行星的矩阵相乘 `ctx.setTransform(...globalMat); ctx.transform(this.cos, this.sin, -this.sin, this.cos, this.x, this.y);` 并绘制结果
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-js -->
requestAnimationFrame(mainLoop);
const ctx = canvas.getContext("2d");
const TAU = Math.PI * 2;
const centerOnPlanetName = "Earth"; // 要居中视图的行星名称
const globalMatrix = [1,0,0,1,0,0]; // 保存行星位置以跟踪
const solSystem = {}; // 按名称保存行星
const planets = []; // 按创建顺序保存行星
const planetCommon = { // 行星的通用属性
x: 0, y: 0, cos: 1, sin: 0,
update(time) {
this.ang = this.startAng + this.orbitalSpeed * time;
this.cos = Math.cos(this.ang);
this.sin = Math.sin(this.ang);
if (this.orbits) { // 检查是否绕着某物体运动
this.x = this.orbits.x + this.cos * this.dist;
this.y = this.orbits.y + this.sin * this.dist;
}
},
draw(ctx, globalMat) {
ctx.setTransform(...globalMat);
ctx.transform(this.cos, this.sin, -this.sin, this.cos, this.x, this.y);
ctx.fillStyle = this.color;
ctx.beginPath();
ctx.arc(0, 0, this.radius, 0, TAU);
ctx.fill();
if (this.dirTo !== undefined) {
ctx.beginPath();
const ax = Math.cos(this.dirTo);
const ay = Math.sin(this.dirTo);
ctx.lineTo(ax * this.radius * 1.5, ay * this.radius * 1.5);
ctx.lineTo(ax * this.distTo, ay * this.distTo);
ctx.stroke();
}
},
directionTo(planet) {
if (planet !== this) {
this.dirTo = Math.atan2(planet.y - this.y, planet.x - this.x) - this.ang;
this.distTo = Math.hypot(planet.y - this.y, planet.x - this.x);
} else {
this.dirTo = undefined;
}
}
};
const planet = (name, orbitsName, dist, orbitalSpeed, startAng, radius, color) => {
planets.push(solSystem[name] = {
orbits: solSystem[orbitsName], // 设置新行星绕着的行星
dist, // 离轨道物体的距离
orbitalSpeed, // 每个时间单位的弧度
startAng, // 弧度中的起始角度
radius,
color,
...planetCommon,
});
};
planet("Sun", undefined, 0, 0.8, - Math.PI, 20, "yellow");
planet("Mercury", "Sun", 45, 2, 0, 6, "#888");
planet("Venus", "Sun", 70, 1.2, 0, 15, "#CC8");
planet("Earth", "Sun", 120, 0.8, 0, 16, "#39B");
planet("Mars", "Sun", 175, 0.4, 0, 10, "#B43");
planet("Moon", "Earth", 30, 5, 0, 3, "#444");
function centerOn(cx, cy, planet) {
globalMatrix[4] = cx - planet.x;
globalMatrix[5] = cy - planet.y;
}
function mainLoop(time) {
time /= 1000;
ctx.setTransform(1, 0, 0, 1, 0, 0);
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
planets.forEach(planet => planet.update(time));
planets.forEach(planet => planet.directionTo(solSystem[centerOnPlanetName]));
centerOn(ctx.canvas.width * 0.5, ctx.canvas.height * 0.5, solSystem[centerOnPlanetName]);
planets.forEach(planet => planet.draw(ctx, globalMatrix));
requestAnimationFrame(mainLoop);
}
<!-- language: lang-css -->
canvas {
border: 1px solid black;
}
<!-- language: lang-html -->
<canvas id="canvas" width="600" height="600"></canvas>
<!-- end snippet -->
英文:
Encapsulate using objects
Your code is a mess
You need to encapsulate the various parts so you can work with them individually.
The example below defines planets using a factory pattern to create planets as objects.
Each planet gets orbital ang, rate, and distance from the planet they orbit. Eg Mercury, Venus, Earth, Mars all orbit the Sun, and the Moon orbits the Earth.
Planets are created in orbital order. IE can not create planet before you create the planet it orbits.
On each animation frame each planet's position is calculated relative to the planet it orbits in the same order as they were created.
Before rendering the planet we want to center the view on a selected planet (in this case the Earth). The centered planets position is used to create a global matrix that will move all planets to the correct position.
Then when drawing each planet we multiply the global matrix by the planet's matrix ctx.setTransform(...globalMat); ctx.transform(this.cos, this.sin, -this.sin, this.cos, this.x, this.y);
and draw the result
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-js -->
requestAnimationFrame(mainLoop);
const ctx = canvas.getContext("2d");
const TAU = Math.PI * 2;
const centerOnPlanetName = "Earth"; // Planet name to center view on
const globalMatrix = [1,0,0,1,0,0]; // holds position of planet to track
const solSystem = {}; // holds planets by name
const planets = []; // holds planets in order of created
const planetCommon = { // common properties of planets
x: 0, y: 0, cos: 1, sin: 0,
update(time) {
this.ang = this.startAng + this.orbitalSpeed * time;
this.cos = Math.cos(this.ang);
this.sin = Math.sin(this.ang);
if (this.orbits) { // Check if orbiting something
this.x = this.orbits.x + this.cos * this.dist;
this.y = this.orbits.y + this.sin * this.dist;
}
},
draw(ctx, globalMat) {
ctx.setTransform(...globalMat);
ctx.transform(this.cos, this.sin, -this.sin, this.cos, this.x, this.y);
ctx.fillStyle = this.color;
ctx.beginPath();
ctx.arc(0, 0, this.radius, 0, TAU);
ctx.fill();
if (this.dirTo !== undefined) {
ctx.beginPath();
const ax = Math.cos(this.dirTo);
const ay = Math.sin(this.dirTo);
ctx.lineTo(ax * this.radius * 1.5, ay * this.radius * 1.5);
ctx.lineTo(ax * this.distTo, ay * this.distTo);
ctx.stroke();
}
},
directionTo(planet) {
if (planet !== this) {
this.dirTo = Math.atan2(planet.y - this.y, planet.x - this.x) - this.ang;
this.distTo = Math.hypot(planet.y - this.y, planet.x - this.x);
} else {
this.dirTo = undefined;
}
}
};
const planet = (name, orbitsName, dist, orbitalSpeed, startAng, radius, color) => {
planets.push(solSystem[name] = {
orbits: solSystem[orbitsName], // Set planet new planet orbits
dist, // dist from orbiting body
orbitalSpeed, // in radians per time unit
startAng, // starting angle in radians
radius,
color,
...planetCommon,
});
};
planet("Sun", undefined, 0, 0.8, - Math.PI, 20, "yellow");
planet("Mercury", "Sun", 45, 2, 0, 6, "#888");
planet("Venus", "Sun", 70, 1.2, 0, 15, "#CC8");
planet("Earth", "Sun", 120, 0.8, 0, 16, "#39B");
planet("Mars", "Sun", 175, 0.4, 0, 10, "#B43");
planet("Moon", "Earth", 30, 5, 0, 3, "#444");
function centerOn(cx, cy, planet) {
globalMatrix[4] = cx - planet.x;
globalMatrix[5] = cy - planet.y;
}
function mainLoop(time) {
time /= 1000;
ctx.setTransform(1, 0, 0, 1, 0, 0);
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
planets.forEach(planet => planet.update(time));
planets.forEach(planet => planet.directionTo(solSystem[centerOnPlanetName]));
centerOn(ctx.canvas.width * 0.5, ctx.canvas.height * 0.5, solSystem[centerOnPlanetName]);
planets.forEach(planet => planet.draw(ctx, globalMatrix));
requestAnimationFrame(mainLoop);
}
<!-- language: lang-css -->
canvas {
border: 1px solid black;
}
<!-- language: lang-html -->
<canvas id="canvas" width="600" height="600"></canvas>
<!-- end snippet -->
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论