gpt4 book ai didi

javascript - HTML5 Canvas画线点间距离

转载 作者:太空宇宙 更新时间:2023-11-04 13:52:03 25 4
gpt4 key购买 nike

我正在尝试学习 HTML5 并发现了一个非常简单的粒子系统,我对其进行了一些修改。如果粒子之间的距离在 0-20 范围内,我想在粒子之间创建一条线。

我目前的方法是在每个粒子之间画一条线,无论距离多远。

这是我尝试检查距离的地方,但我不知道该怎么做。将不胜感激任何帮助和解释。提前致谢。

           // This particle
var p = particles[t];

// Check position distance to other particles
for (var q = 0; q < particles.length; q++) {

if (particles[q].x - p.x < line_distance || p.x - particles[q].x < line_distance) {
ctx.beginPath();
ctx.lineWidth = .1;
ctx.strokeStyle = '#fff';
ctx.moveTo(p.x, p.y);
ctx.lineTo(particles[q].x, particles[q].y);
ctx.stroke();
}

}

// Request animation frame
var requestAnimationFrame = window.requestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.msRequestAnimationFrame;

// Canvas
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');

// Set fullscreen
canvas.width = document.documentElement.clientWidth;
canvas.height = document.documentElement.clientHeight;

// Options
var num =30; // Number of particles to draw
var size = 3; // Particle size
var color = '#fff'; // Particle color
var min_speed = 1; // Particle min speed
var max_speed = 3; // Particle max speed
var line_distance = 20; // This is the max distance between two particles
// if we want to draw a line between them

// Particles array
var particles = [];
for (var i = 0; i < num; i++) {
particles.push(
new create_particle()
);
}

// Lets animate the particle
function draw() {

// Background
ctx.fillStyle = "#000";
ctx.fillRect(0, 0, canvas.width, canvas.height);

// Lets draw particles from the array now
for (var t = 0; t < particles.length; t++) {

// This particle
var p = particles[t];

for (var q = 0; q < particles.length; q++) {

// Check position distance
if (particles[q].x - p.x < line_distance || p.x - particles[q].x < line_distance) {
ctx.beginPath();
ctx.lineWidth = .1;
ctx.strokeStyle = '#fff';
ctx.moveTo(p.x, p.y);
ctx.lineTo(particles[q].x, particles[q].y);
ctx.stroke();
}

}

// Color
ctx.fillStyle = color;

// Circle path
ctx.beginPath();
ctx.arc(p.x, p.y, p.radius, Math.PI * 2, false);
ctx.fill();

// Lets use the velocity now
p.x += p.vx;
p.y += p.vy;

// If there is only 1 particle
// show X, Y, and velocity
if (num === 1) {
ctx.fillText('Y:'+ p.y, 20, 20);
ctx.fillText('X:'+ p.x, 20, 40);
ctx.fillText('YV:'+ p.vy, 20, 60);
ctx.fillText('XV:'+ p.vx, 20, 80);
}

// To prevent the balls from moving out of the canvas
if (p.x < size) p.vx*= (p.vx / -p.vx);
if (p.y < size) p.vy*= (p.vy / -p.vy);
if (p.x > canvas.width - size) p.vx*= (-p.vx / p.vx);
if (p.y > canvas.height - size) p.vy*= (-p.vy / p.vy);

}

// Loop
requestAnimationFrame(draw);

}

// Function for particle creation
function create_particle() {

// Random position
this.x = Math.random() * canvas.width;
this.y = Math.random() * canvas.height;

// Velocity
this.vx = random_int_between(min_speed, max_speed);
this.vy = random_int_between(min_speed, max_speed);

// Color & Size
this.color = color;
this.radius = size;

}

// Random number between (used for speed)
function random_int_between(min, max) {
return Math.floor(Math.random() * max) + min;
}

draw();
<canvas id="canvas"></canvas>

最佳答案

N 体粒子系统

因为这是一个 N 机身案例,没有人说任何关于 CPU 负载的事情。

CPU 负载

粒子系统会在处理过载时迅速使 CPU 陷入困境。当您将每个粒子与另一个粒子进行测试时,尤其如此。由于粒子系统几乎总是用于实时图形,无效的编码可能会破坏整个动画。

不需要做什么

首先,由于您只是在寻找阈值距离,因此您可以通过在知道测试失败后不再继续计算来优化计算。

因此设置阈值距离

var dist = 20;
var distSq = dist * dist; // No need to square this inside loops

然后在循环中计算测试并继续。假设p1和p2是粒子

x = p2.x-p1.x;  // do x first
if((x *= x) < distSq){ // does it pass?? if not you have saved calculating y
y = p2.y-p1.y; // now do y as you know x is within distance
if(x + (y * y) < distSq){ // now you know you are within 20
// draw the line

假设只有 1/6 会通过并且 1/3 接近,您可以节省一半以上的 CPU 负载。您还会注意到我没有使用距离的 CPU 重 sqrt。没有必要,因为数字和数字的平方之间存在一对一的匹配。如果一个数的平方根小于距离,则该数的平方将小于距离的平方。

N 体平方

永远不要像这样用两个 for 循环来模拟 N body 。

for(i = 0; i < particles.length; i ++){
for(j = 0; j < particles.length; j ++){
// you will test all i for j and all j for i but half of them are identical
// and the square root of the number are self to self

这让我很伤心,因为解决方案是如此简单。

假设您有 100 个粒子,每秒 60 帧,您每秒对 100 个粒子进行 60 * 100 * 100 次比较 (600,000)。这完全是在浪费 CPU 时间。

永远不要做某事两次,或者您知道答案的事情。

改进 for 循环并避免测试已知距离和测试每个粒子与自身的距离

var len =  particles.length;  // move the length out as it can be expensive
// and pointless as the value does not change;
for(i = 0; i < len; i ++){
for(j = i + 1; j < len; j ++){
// Now you only test each particle against each other once rather than twice

因此,仅需几个简单的字符(for(j = 0 变为 for(j = i + 1),您就可以从 600,000 次比较中减少一半以上的 CPU 负载减少到不到 300,000

人眼很容易被骗

欺骗眼睛是从动画中获得额外性能的最佳方式。

这是一种视觉效果,人眼看不到像素,也看不到 1/60 秒的单个帧,但它确实看到帧速率下降。创建一个复杂的粒子系统可以获得出色的 FX,但如果它降低了帧速率,那么好处就会丢失。利用像素太小和 1/20 秒远远超出人类发现错误的能力这一事实是优化 FX 并在每个 CPU 滴答中添加更多 bang 的最佳方法。

下面的演示有两个粒子模拟。每个100分。 49 像素以内的任何点之间都画有一条线。一个做我上面演示的所有事情,另一个牺牲一点内存和很多准确性,并且每帧只计算 1/3 点之间的距离。由于最大速度可以接近一帧线长度的一半,跳过 2 帧可以使线长两倍或两点太近而没有线。这样做可以节省大量 CPU,但您无法选择哪个是哪个。

单击您认为跳过点的 sim 以找出哪个是哪个。

var canvas = document.createElement("canvas"); 
canvas.width= 540;
canvas.height = 270;
var ctx = canvas.getContext("2d");
document.body.appendChild(canvas);
mouseX = 0;
mouseB = false;
function clickedFun(event){
mouseX = event.clientX
mouseB = true;
}


canvas.addEventListener("click",clickedFun);

var w = 250;
var h = 250;
var wh = w/2;
var hh = h/2;
var speedMax = 5;
var partSize = 2;
var count = 100
var grav = 1;
var pA1 = []; // particle arrays
var pA2 = [];
var PI2 = Math.PI * 2;

// populate particle arrays
for(var i = 0; i < count; i += 1){
// dumb list
pA1.push({
x : Math.random() * w,
y : Math.random() * h,
dx : (Math.random() -0.5)*speedMax,
dy : (Math.random() -0.5)*speedMax,

})
// smart list
pA2.push({
x : Math.random() * w,
y : Math.random() * h,
dx : (Math.random() -0.5)*speedMax,
dy : (Math.random() -0.5)*speedMax,
links : [], // add some memory
})
for(var j = 0; j < count; j += 1){
pA2[i].links[i] = false; // set memory to no links
}
}


// move and draw the dots. Just a simple gravity sim
function drawAll(parts){
var x,y,d;
var i = 0;
var len = parts.length;
var p;
ctx.beginPath();
for(;i < len; i++){
p = parts[i];
x = wh-p.x;
y = hh-p.y;
d = x*x + y*y;
x *= grav / d;
y *= grav / d;
p.dx += x;
p.dy += y;
p.x += p.dx;
p.y += p.dy;
if(p.x <= 0){
p.dx -= p.dx/2;
p.x = 1;
}else
if(p.x >= w){
p.dx -= p.dx/2;
p.x = w-1;
}
if(p.y <= 0){
p.dy -= p.dy/2;
p.y = 1;
}else
if(p.y >= h){
p.dy -= p.dy/2;
p.y = w-1;
}
ctx.moveTo(p.x+partSize,p.y)
ctx.arc(p.x,p.y,partSize,0,PI2)
}
ctx.fill();
}
//Old style line test. If two particles are less than dist apart
// draw a line between them
function linesBetween(parts,dist){
var distSq = dist*dist;
var x,y,d,j;
var i = 0;
var len = parts.length;
var p,p1;
ctx.beginPath();
for(; i < len; i ++){
p = parts[i];
for(j = i + 1; j < len; j ++){
p1 = parts[j];
x = p1.x-p.x;
if((x *= x) < distSq){
y = p1.y-p.y;
if(x + (y*y) < distSq){
ctx.moveTo(p.x,p.y);
ctx.lineTo(p1.x,p1.y)
}
}
}

}
ctx.stroke();
}

var counter = 0;// counter for multyplexing
// Fast version. As the eye can not posible see the differance of
// of 4 pixels over 1/30th of a second only caculate evey third
// particls
function linesBetweenFast(parts,dist){
var distSq = dist*dist;
var x,y,d,j,l;
var i = 0;
counter += 1;
var cc = counter % 3;
var wr,re;
var len = parts.length;
var p,p1;
var lineSet
ctx.beginPath();
for(; i < len; i ++){
p = parts[i];
l = p.links;
for(j = i + 1; j < len; j += 1){
p1 = parts[j];
if((j + cc)%3 === 0){ // only every third particle
lineSet = false; // test for diferance default to fail
x = p1.x-p.x;
if((x *= x) < distSq){
y = p1.y-p.y;
if(x + (y*y) < distSq){
lineSet = true; // yes this needs a line
}
}
l[j] = lineSet; // flag it as needing a line
}
if(l[j]){ // draw the line if needed
ctx.moveTo(p.x,p.y);
ctx.lineTo(p1.x,p1.y);
}
}
}
ctx.stroke();
}


var drawLines; // to hold the function that draws lines
// set where the screens are drawn
var left = 10;
var right = 10 * 2 + w;
// Now to not cheat swap half the time
if(Math.random() < 0.5){
right = 10;
left = 10 * 2 + w;
}


// draws a screem
var doScreen = function(parts){
ctx.fillStyle = "red"
drawAll(parts);
ctx.strokeStyle = "black";
ctx.lineWidth = 1;
drawLines(parts,49);
}
var guess = ""
var guessPos;
var gueesCol;
ctx.font = "40px Arial Black";
ctx.textAlign = "center";
ctx.textBasline = "middle"
var timer = 0;
function update(){
ctx.setTransform(1,0,0,1,0,0);
ctx.clearRect(0,0,canvas.width,canvas.height);
ctx.setTransform(1,0,0,1,left,10);
ctx.strokeStyle = "red";
ctx.lineWidth = 4;
ctx.strokeRect(0,0,w,h);
drawLines = linesBetween;
doScreen(pA1)
ctx.setTransform(1,0,0,1,right,10);
ctx.strokeStyle = "red";
ctx.lineWidth = 4;
ctx.strokeRect(0,0,w,h);
drawLines = linesBetweenFast
doScreen(pA2)




if(mouseB){
if((mouseX > 270 && right >250) ||
(mouseX < 250 && right < 250)){
guess = "CORRECT!"
guessPos = right;
guessCol = "Green";
}else{
guess = "WRONG"
guessPos = left
guessCol = "Red";
}
timer = 120;
mouseB = false;
}else
if(timer > 0){
timer -= 1;
if(timer > 30){
ctx.setTransform(1,0,0,1,guessPos,10);
ctx.font = "40px Arial Black";
ctx.fillStyle = guessCol;
ctx.fillText(guess,w/2,h/2);
}else{
if(Math.random() < 0.5){
right = 10;
left = 10 * 2 + w;
}else{
left = 10;
right = 10 * 2 + w;
}
}

}else{
ctx.setTransform(1,0,0,1,0,0);
ctx.font = "16px Arial Black";
var tw = ctx.measureText("Click which sim skips 2/3rd of").width +30;

ctx.beginPath();
ctx.fillStyle = "#DDD";
ctx.strokeStyle = "Red";
ctx.rect(270-tw/2,-5,tw,40);
ctx.stroke();
ctx.fill();

ctx.fillStyle = "blue";
ctx.fillText("Click which sim skips 2/3rd of",270,15) ;
ctx.fillText("particle tests every frame",270,30) ;
}

requestAnimationFrame(update);
}
update();

关于javascript - HTML5 Canvas画线点间距离,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36745286/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com