proton-engine
Version:
Proton is a simple and powerful javascript particle animation engine.
178 lines (154 loc) • 5.17 kB
JavaScript
import Zone from "./Zone";
import Util from "../utils/Util";
import MathUtil from "../math/MathUtil";
import Vector2D from "../math/Vector2D";
/**
* Represents a line zone for particle systems.
* @extends Zone
*/
export default class LineZone extends Zone {
/**
* Creates a new LineZone.
* @param {number} x1 - The x-coordinate of the first point.
* @param {number} y1 - The y-coordinate of the first point.
* @param {number} [x2] - The x-coordinate of the second point.
* @param {number} [y2] - The y-coordinate of the second point.
* @param {string} [direction=">"] - The direction of the line.
*/
constructor(x1, y1, x2, y2, direction = ">") {
super();
if (x2 - x1 >= 0) {
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
} else {
this.x1 = x2;
this.y1 = y2;
this.x2 = x1;
this.y2 = y1;
}
this.dx = this.x2 - this.x1;
this.dy = this.y2 - this.y1;
this.minx = Math.min(this.x1, this.x2);
this.miny = Math.min(this.y1, this.y2);
this.maxx = Math.max(this.x1, this.x2);
this.maxy = Math.max(this.y1, this.y2);
this.dot = this.x2 * this.y1 - this.x1 * this.y2;
this.xxyy = this.dx * this.dx + this.dy * this.dy;
this.gradient = this.getGradient();
this.length = this.getLength();
this.direction = Util.initValue(direction, ">");
}
/**
* Gets a random position on the line.
* @returns {Vector2D} A vector representing the random position.
*/
getPosition() {
this.random = Math.random();
this.vector.x = this.x1 + this.random * this.length * Math.cos(this.gradient);
this.vector.y = this.y1 + this.random * this.length * Math.sin(this.gradient);
return this.vector;
}
/**
* Determines which side of the line a point is on.
* @param {number} x - The x-coordinate of the point.
* @param {number} y - The y-coordinate of the point.
* @returns {boolean} True if the point is on the positive side of the line, false otherwise.
*/
getDirection(x, y) {
const A = this.dy;
const B = -this.dx;
const C = this.dot;
const D = B === 0 ? 1 : B;
if ((A * x + B * y + C) * D > 0) return true;
else return false;
}
/**
* Calculates the distance of a point from the line.
* @param {number} x - The x-coordinate of the point.
* @param {number} y - The y-coordinate of the point.
* @returns {number} The distance from the point to the line.
*/
getDistance(x, y) {
const A = this.dy;
const B = -this.dx;
const C = this.dot;
const D = A * x + B * y + C;
return D / Math.sqrt(this.xxyy);
}
/**
* Calculates the symmetric vector of a given vector with respect to the line.
* @param {Vector2D} v - The vector to reflect.
* @returns {Vector2D} The reflected vector.
*/
getSymmetric(v) {
const tha2 = v.getGradient();
const tha1 = this.getGradient();
const tha = 2 * (tha1 - tha2);
const oldx = v.x;
const oldy = v.y;
v.x = oldx * Math.cos(tha) - oldy * Math.sin(tha);
v.y = oldx * Math.sin(tha) + oldy * Math.cos(tha);
return v;
}
/**
* Gets the gradient (angle) of the line.
* @returns {number} The gradient of the line in radians.
*/
getGradient() {
return Math.atan2(this.dy, this.dx);
}
/**
* Checks if a particle is outside the range of the line.
* @param {Particle} particle - The particle to check.
* @returns {boolean} True if the particle is within range, false otherwise.
*/
rangeOut(particle) {
const angle = Math.abs(this.getGradient());
if (angle <= MathUtil.PI / 4) {
if (particle.p.x <= this.maxx && particle.p.x >= this.minx) return true;
} else {
if (particle.p.y <= this.maxy && particle.p.y >= this.miny) return true;
}
return false;
}
/**
* Gets the length of the line.
* @returns {number} The length of the line.
*/
getLength() {
return Math.sqrt(this.dx * this.dx + this.dy * this.dy);
}
/**
* Handles particle crossing behavior based on the crossType.
* @param {Particle} particle - The particle to check for crossing.
*/
crossing(particle) {
if (this.crossType === "dead") {
if (this.direction === ">" || this.direction === "R" || this.direction === "right" || this.direction === "down") {
if (!this.rangeOut(particle)) return;
if (this.getDirection(particle.p.x, particle.p.y)) particle.dead = true;
} else {
if (!this.rangeOut(particle)) return;
if (!this.getDirection(particle.p.x, particle.p.y)) particle.dead = true;
}
} else if (this.crossType === "bound") {
if (!this.rangeOut(particle)) return;
if (this.getDistance(particle.p.x, particle.p.y) <= particle.radius) {
if (this.dx === 0) {
particle.v.x *= -1;
} else if (this.dy === 0) {
particle.v.y *= -1;
} else {
this.getSymmetric(particle.v);
}
}
} else if (this.crossType === "cross") {
if (this.alert) {
console.error("Sorry, LineZone does not support cross method!");
this.alert = false;
}
}
}
}