I have been trying to write a simulator using the HTML5 Canvas. I have the following code to generate equipotential lines for electric fields.
function drawEqLines(charges) {
// don't bother if there aren't any charges
if (charges.length == 0) return;
// the Charge class contains the charge of the charge as well as its x and y coordinates
var fieldFilled = [];
for (var i = 0; i < 10; i++) {
fieldFilled.push([]);
for (var j = 0; j < 10; j++) {
fieldFilled[i].push(false);
}
}
var calculatedFields = [];
var maxForce = 0;
for (var i = 0; i < fieldFilled.length; i++) {
var direction = 1;
for (var jj=0; jj < fieldFilled[i].length; jj++) {
if (!fieldFilled[i][jj]) {
//create a path here
//Iterate at most 2 times in case the surface gets out of the area
for (var circleTimes = 0; circleTimes < 3; circleTimes+=2) {
//Define the center of the current block as a starting point of the surface
/*
horizontalBlock and verticalBlock are the width and height of the canvas divided into 10 different sections.
_BlockHalf is half of one of the blocks
*/
var curX = i * this.horizontalBlock + this.horizontalBlockHalf;
var curY = jj * this.verticalBlock + this.verticalBlockHalf;
// Point is a class that contains an x and y value
var curPt = new Point(curX, curY);
var direction = 1 - circleTimes;
var dots = [];
dots.push(curPt);
//Superposition the fields from all charges, and get the resulting force vector
var dirX = 0;
var dirY = 0;
var totalForce = 0;
for (var j = 0; j < charges.length; j++) {
var distX = curPt.x - charges[j].x;
var distY = curPt.y - charges[j].y;
var distanceSq = distX * distX + distY * distY;
var force = charges[j].charge / distanceSq;
var distanceFactor = force / Math.sqrt(distanceSq);
//Measure the initial force in order to match the equipotential surface points
totalForce += force;
dirX += distX * distanceFactor;
dirY += distY * distanceFactor;
}
//Maximum 2000 dots per surface line
var times = 2000;
while (times-- > 0) {
var dirTotal = Math.sqrt(dirX * dirX + dirY * dirY);
var stepX = dirX / dirTotal;
var stepY = dirY / dirTotal;
//The equipotential surface moves normal to the force vector
curPt.x = curPt.x + direction * 6 * stepY;
curPt.y = curPt.y - direction * 6 * stepX;
/*
*
* ***********************LOG LINE HERE***********************************
*
*/
// this prints out unique values
console.log(curPt.x + ", " + curPt.y);
//Correct the exact point a bit to match the initial force as near it can
var minForceIndex = -1;
var minForceDiff = 0;
var minDirX = 0;
var minDirY = 0;
var minCurX = 0;
var minCurY = 0;
curPt.x -= 3 * stepX;
curPt.y -= 3 * stepY;
for (var pointIndex = 0; pointIndex < 7; pointIndex++, curPt.x += stepX, curPt.y += stepY) {
dirX = 0;
dirY = 0;
var forceSum = 0;
for (var j = 0; j < charges.length; j++) {
var distX = curPt.x - charges[j].x;
var distY = curPt.y - charges[j].y;
var distanceSq = distX ** 2 + distY ** 2;
var force = charges[j].charge / distanceSq;
var distanceFactor = force / Math.sqrt(distanceSq);
//Measure the initial force in order to match the equipotential surface points
forceSum += force;
dirX += distX * distanceFactor;
dirY += distY * distanceFactor;
}
var forceDiff = Math.abs(forceSum - totalForce);
if (minForceIndex == -1 || forceDiff < minForceDiff) {
minForceIndex = pointIndex;
minForceDiff = forceDiff;
minDirX = dirX;
minDirY = dirY;
minCurX = curPt.x;
minCurY = curPt.y;
} else {
break;
}
}
//Set the corrected equipotential point
curPt.x = minCurX;
curPt.y = minCurY;
dirX = minDirX;
dirY = minDirY;
//Mark the containing block as filled with a surface line.
var indI = parseInt(curPt.x / this.horizontalBlock);
var indJ = parseInt(curPt.y / this.verticalBlock);
if (indI >= 0 && indI < fieldFilled.length) {
if (indJ >= 0 && indJ < fieldFilled[indI].length) {
fieldFilled[indI][indJ] = true;
}
}
//Add the dot to the line (was commented out when I added the other log)
dots.push(curPt);
if (dots.length > 5) {
//If got to the begining, a full circle has been made, terminate further iterations
if (indI == i && indJ == jj) {
distX = dots[0].x - curPt.x;
distY = dots[0].y - curPt.y;
if (distX * distX + distY * distY <= 49) {
dots.push(new Point(dots[0].x, dots[0].y));
times = 0;
circleTimes = 3;
}
}
//If got out of the area, terminate further iterations for this turn.
if (curPt.x < 0 || curPt.x > this.canvas.width || curPt.y < 0 || curPt.y > this.canvas.height) {
times = 0;
}
}
}
calculatedFields.push([totalForce, dots]);
maxForce = Math.max(maxForce, Math.abs(totalForce));
}
}
}
}
console.log(calculatedFields);
// draw the lines from the arrays of dots here...
}
The result of this code is an array that looks like this:
[
[
0.000007927962725256215,
[
// as you can see, these points are all the same
{x: 114.16365557137308, y: 402.6147544331975},
{x: 114.16365557137308, y: 402.6147544331975},
{x: 114.16365557137308, y: 402.6147544331975},
{x: 114.16365557137308, y: 402.6147544331975},
// etc...
]
],
[
0.000006140401131528559,
[
{x: -1.0201768243546043, y: 323.28955989370445},
{x: -1.0201768243546043, y: 323.28955989370445},
{x: -1.0201768243546043, y: 323.28955989370445},
{x: -1.0201768243546043, y: 323.28955989370445},
// etc...
]
]
]
These points should be unique, however, it seems to just generate the same point over and over again. For some reason, it does decide to stop after a reasonable number of iterations, as if it is doing the calculations correctly but just putting the same point into the array anyway.
Why is this happening? How can I fix it?
Update: Here are the classes that I am using:
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
}
class Charge {
constructor(charge, x, y) {
this.charge = charge;
this.x = x;
this.y = y;
}
}
Also, the charges
array is fine, because I use it in a similar method which does work correctly. An example for this would be:
[
{charge: 6, x: 50, y: 50},
{charge: -4, x: 70, y: 90.5},
// etc...
]
Update 2:
I have tried adding the log line indicated in the above code, which prints out only unique values. However, even if I try pushing the point to the array here it still results in the same issue.
Update 3:
Added this runnable snippet:
const height = 400; // for example
const width = 600; // also for example
const horizontalBlock = width / 10;
const verticalBlock = height / 10;
const horizontalBlockHalf = horizontalBlock / 2;
const verticalBlockHalf = verticalBlock / 2;
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
}
function drawEqLines(charges) {
// don't bother if there aren't any charges
if (charges.length == 0) return;
// the Charge class contains the charge of the charge as well as its x and y coordinates
var fieldFilled = [];
for (var i = 0; i < 10; i++) {
fieldFilled.push([]);
for (var j = 0; j < 10; j++) {
fieldFilled[i].push(false);
}
}
var calculatedFields = [];
var maxForce = 0;
for (var i = 0; i < fieldFilled.length; i++) {
var direction = 1;
for (var jj=0; jj < fieldFilled[i].length; jj++) {
if (!fieldFilled[i][jj]) {
//create a path here
//Iterate at most 2 times in case the surface gets out of the area
for (var circleTimes = 0; circleTimes < 3; circleTimes+=2) {
//Define the center of the current block as a starting point of the surface
/*
horizontalBlock and verticalBlock are the width and height of the canvas divided into 10 different sections.
_BlockHalf is half of one of the blocks
*/
var curX = i * horizontalBlock + horizontalBlockHalf;
var curY = jj * verticalBlock + verticalBlockHalf;
// Point is a class that contains an x and y value
var curPt = new Point(curX, curY);
var direction = 1 - circleTimes;
var dots = [];
dots.push(curPt);
//Superposition the fields from all charges, and get the resulting force vector
var dirX = 0;
var dirY = 0;
var totalForce = 0;
for (var j = 0; j < charges.length; j++) {
var distX = curPt.x - charges[j].x;
var distY = curPt.y - charges[j].y;
var distanceSq = distX * distX + distY * distY;
var force = charges[j].charge / distanceSq;
var distanceFactor = force / Math.sqrt(distanceSq);
//Measure the initial force in order to match the equipotential surface points
totalForce += force;
dirX += distX * distanceFactor;
dirY += distY * distanceFactor;
}
//Maximum 2000 dots per surface line
var times = 2000;
while (times-- > 0) {
var dirTotal = Math.sqrt(dirX * dirX + dirY * dirY);
var stepX = dirX / dirTotal;
var stepY = dirY / dirTotal;
//The equipotential surface moves normal to the force vector
curPt.x = curPt.x + direction * 6 * stepY;
curPt.y = curPt.y - direction * 6 * stepX;
/*
*
* ***********************LOG LINE HERE***********************************
*
*/
// this prints out unique values
console.log(curPt.x + ", " + curPt.y);
//Correct the exact point a bit to match the initial force as near it can
var minForceIndex = -1;
var minForceDiff = 0;
var minDirX = 0;
var minDirY = 0;
var minCurX = 0;
var minCurY = 0;
curPt.x -= 3 * stepX;
curPt.y -= 3 * stepY;
for (var pointIndex = 0; pointIndex < 7; pointIndex++, curPt.x += stepX, curPt.y += stepY) {
dirX = 0;
dirY = 0;
var forceSum = 0;
for (var j = 0; j < charges.length; j++) {
var distX = curPt.x - charges[j].x;
var distY = curPt.y - charges[j].y;
var distanceSq = distX ** 2 + distY ** 2;
var force = charges[j].charge / distanceSq;
var distanceFactor = force / Math.sqrt(distanceSq);
//Measure the initial force in order to match the equipotential surface points
forceSum += force;
dirX += distX * distanceFactor;
dirY += distY * distanceFactor;
}
var forceDiff = Math.abs(forceSum - totalForce);
if (minForceIndex == -1 || forceDiff < minForceDiff) {
minForceIndex = pointIndex;
minForceDiff = forceDiff;
minDirX = dirX;
minDirY = dirY;
minCurX = curPt.x;
minCurY = curPt.y;
} else {
break;
}
}
//Set the corrected equipotential point
curPt.x = minCurX;
curPt.y = minCurY;
dirX = minDirX;
dirY = minDirY;
//Mark the containing block as filled with a surface line.
var indI = parseInt(curPt.x / this.horizontalBlock);
var indJ = parseInt(curPt.y / this.verticalBlock);
if (indI >= 0 && indI < fieldFilled.length) {
if (indJ >= 0 && indJ < fieldFilled[indI].length) {
fieldFilled[indI][indJ] = true;
}
}
//Add the dot to the line (was commented out when I added the other log)
dots.push(curPt);
if (dots.length > 5) {
//If got to the begining, a full circle has been made, terminate further iterations
if (indI == i && indJ == jj) {
distX = dots[0].x - curPt.x;
distY = dots[0].y - curPt.y;
if (distX * distX + distY * distY <= 49) {
dots.push(new Point(dots[0].x, dots[0].y));
times = 0;
circleTimes = 3;
}
}
//If got out of the area, terminate further iterations for this turn.
if (curPt.x < 0 || curPt.x > width || curPt.y < 0 || curPt.y > height) {
times = 0;
}
}
}
calculatedFields.push([totalForce, dots]);
maxForce = Math.max(maxForce, Math.abs(totalForce));
}
}
}
}
console.log(calculatedFields);
// draw the lines from the arrays of dots here...
}
var charges = [
{charge: 6, x: 50, y: 75},
{charge: -5, x: 60, y: 254.5}
]
drawEqLines(charges);
You have a loop with code that says:
dots.push(curPt)
In that loop, you reassign the
x
andy
properties of thecurPt
object, but it’s the same object that you keep pushing on each iteration.You will have to create a new
Point
object inside the loop if you want to push different points into the array.free slots games https://casinorealmoneyai.com/ – casino bonus codes slots games free free casino games online