Skip to content

Commit

Permalink
fix: working on issues #1269 and #1256
Browse files Browse the repository at this point in the history
  • Loading branch information
matteobruni committed May 4, 2021
1 parent 14cd117 commit ecd2170
Show file tree
Hide file tree
Showing 3 changed files with 133 additions and 84 deletions.
158 changes: 85 additions & 73 deletions core/main/src/Plugins/PolygonMask/PolygonMaskInstance.ts
Expand Up @@ -11,6 +11,9 @@ import type { IOptions } from "../../Options/Interfaces/IOptions";
import type { RecursivePartial } from "../../Types";
import type { IPolygonMask } from "./Options/Interfaces/IPolygonMask";
import { PolygonMask } from "./Options/Classes/PolygonMask";
import { Vector } from "../../Core/Particle/Vector";
import { IDelta } from "../../Core/Interfaces/IDelta";
import { OutModeDirection } from "../../Enums/Directions/OutModeDirection";

type SvgAbsoluteCoordinatesTypes =
| SVGPathSegArcAbs
Expand Down Expand Up @@ -152,6 +155,56 @@ function parsePaths(paths: ISvgPath[], scale: number, offset: ICoordinates): ICo
return res;
}

function calcClosestPtOnSegment(
s1: ICoordinates,
s2: ICoordinates,
pos: ICoordinates
): ICoordinates & { isOnSegment: boolean } {
// calc delta distance: source point to line start
const { dx, dy } = NumberUtils.getDistances(pos, s1);

// calc delta distance: line start to end
const { dx: dxx, dy: dyy } = NumberUtils.getDistances(s2, s1);

// Calc position on line normalized between 0.00 & 1.00
// == dot product divided by delta line distances squared
const t = (dx * dxx + dy * dyy) / (dxx ** 2 + dyy ** 2);

// calc nearest pt on line
let x = s1.x + dxx * t;
let y = s1.y + dyy * t;

// clamp results to being on the segment
if (t < 0) {
x = s1.x;
y = s1.y;
} else if (t > 1) {
x = s2.x;
y = s2.y;
}

return { x: x, y: y, isOnSegment: t >= 0 && t <= 1 };
}

function segmentBounce(start: ICoordinates, stop: ICoordinates, velocity: Vector): void {
const { dx, dy } = NumberUtils.getDistances(start, stop);
const wallAngle = Math.atan2(dy, dx); // + Math.PI / 2;
const wallNormalX = Math.sin(wallAngle);
const wallNormalY = -Math.cos(wallAngle);

console.log(">-----");
console.log("wall angle", ((wallAngle * 180) / Math.PI + 360) % 360);
console.log("particle angle", ((velocity.angle * 180) / Math.PI + 360) % 360);

const d = 2 * (velocity.x * wallNormalX + velocity.y * wallNormalY);

velocity.x -= d * wallNormalX;
velocity.y -= d * wallNormalY;

console.log("res angle", ((velocity.angle * 180) / Math.PI + 360) % 360);
console.log("-----<");
}

/**
* Polygon Mask manager
* @category Polygon Mask Plugin
Expand Down Expand Up @@ -241,27 +294,8 @@ export class PolygonMaskInstance implements IContainerPlugin {
return Utils.deepExtend({}, position ? position : this.randomPoint()) as ICoordinates;
}

particleBounce(particle: Particle): boolean {
const options = this.options;

/* check bounce against polygon boundaries */
if (options.enable && options.type !== Type.none && options.type !== Type.inline) {
if (!this.checkInsidePolygon(particle.getPosition())) {
this.polygonBounce(particle);

return true;
}
} else if (options.enable && options.type === Type.inline && particle.initialPosition) {
const dist = NumberUtils.getDistance(particle.initialPosition, particle.getPosition());

if (dist > this.polygonMaskMoveRadius) {
this.polygonBounce(particle);

return true;
}
}

return false;
particleBounce(particle: Particle, delta: IDelta, direction: OutModeDirection): boolean {
return this.polygonBounce(particle, delta, direction);
}

clickPositionValid(position: ICoordinates): boolean {
Expand Down Expand Up @@ -305,35 +339,40 @@ export class PolygonMaskInstance implements IContainerPlugin {
}
}

private polygonBounce(particle: Particle): void {
/*if (!this.raw) {
return;
private polygonBounce(particle: Particle, delta: IDelta, direction: OutModeDirection): boolean {
const options = this.options;

if (!this.raw || !options.enable || direction !== OutModeDirection.top) {
return false;
}

for (let i = 0, j = this.raw.length - 1; i < this.raw.length; j = i++) {
const { x, y } = particle.getPosition();
const pi = this.raw[i];
const pj = this.raw[j];
const { x: nearestX, y: nearestY } = this.calcClosestPtOnSegment(pi, pj, { x, y });
const dx = x - nearestX;
const dy = y - nearestY;
const radius = particle.getRadius();
const isColliding = dx ** 2 + dy ** 2 <= (radius * 2) ** 2;
if (isColliding) {
const { dx, dy } = NumberUtils.getDistances(pi, pj);
const wallAngle = Math.atan2(dy, dx);
const incidenceAngle = particle.velocity.angle;
const wallNormalAngle = wallAngle - Math.PI / 2; // assuming clockwise angle calculations
const differenceAngle = incidenceAngle - wallNormalAngle;
const reflectionAngle = incidenceAngle + 2 * differenceAngle;
particle.velocity.angle = reflectionAngle;
if (options.type === Type.inside || options.type === Type.outside) {
for (let i = 0, j = this.raw.length - 1; i < this.raw.length; j = i++) {
const pos = particle.getPosition();
const pi = this.raw[i],
pj = this.raw[j];
const closest = calcClosestPtOnSegment(pi, pj, pos);
const distance = NumberUtils.getDistance(pos, closest);
const radius = particle.getRadius();

if (distance < radius) {
segmentBounce(pi, pj, particle.velocity);

return true;
}
}
} else if (options.type === Type.inline && particle.initialPosition) {
const dist = NumberUtils.getDistance(particle.initialPosition, particle.getPosition());

if (dist > this.polygonMaskMoveRadius) {
particle.velocity.x = particle.velocity.y / 2 - particle.velocity.x;
particle.velocity.y = particle.velocity.x / 2 - particle.velocity.y;

return true;
}
}*/
}

particle.velocity.x = particle.velocity.y / 2 - particle.velocity.x;
particle.velocity.y = particle.velocity.x / 2 - particle.velocity.y;
return false;
}

private checkInsidePolygon(position?: ICoordinates): boolean {
Expand Down Expand Up @@ -374,33 +413,6 @@ export class PolygonMaskInstance implements IContainerPlugin {
return options.type === Type.inside ? inside : options.type === Type.outside ? !inside : false;
}

private calcClosestPtOnSegment(s1: ICoordinates, s2: ICoordinates, pos: ICoordinates) {
// calc delta distance: source point to line start
const { dx, dy } = NumberUtils.getDistances(pos, s1);

// calc delta distance: line start to end
const { dx: dxx, dy: dyy } = NumberUtils.getDistances(s2, s1);

// Calc position on line normalized between 0.00 & 1.00
// == dot product divided by delta line distances squared
const t = (dx * dxx + dy * dyy) / (dxx ** 2 + dyy ** 2);

// calc nearest pt on line
let x = s1.x + dxx * t;
let y = s1.y + dyy * t;

// clamp results to being on the segment
if (t < 0) {
x = s1.x;
y = s1.y;
} else if (t > 1) {
x = s2.x;
y = s2.y;
}

return { x: x, y: y, isOnSegment: t >= 0 && t <= 1 };
}

private parseSvgPath(xml: string, force?: boolean): ICoordinates[] | undefined {
const forceDownload = force ?? false;

Expand Down
51 changes: 44 additions & 7 deletions core/main/tests/Utils.ts
Expand Up @@ -5,13 +5,27 @@ import { IParticle } from "../src/Core/Interfaces/IParticle";
import { Particle } from "../src/Core/Particle";
import { MoveDirection } from "../src/Enums";
import { NumberUtils, Utils } from "../src/Utils";
import { ICoordinates } from "../src/Core/Interfaces/ICoordinates";
import { Vector } from "../src/Core/Particle/Vector";

function buildParticleWithDirection(direction: MoveDirection): IParticle {
const container = new Container("someid");
const options = { move: { direction } };
return new Particle(1, container, undefined, options);
}

function segmentBounce(start: ICoordinates, stop: ICoordinates, velocity: Vector): void {
const { dx, dy } = NumberUtils.getDistances(start, stop);
const wallAngle = Math.atan2(dy, dx) + Math.PI / 2;
const wallNormalX = Math.sin(wallAngle);
const wallNormalY = -Math.cos(wallAngle);

const d = 2 * (velocity.x * wallNormalX + velocity.y * wallNormalY);

velocity.x -= d * wallNormalX;
velocity.y -= d * wallNormalY;
}

describe("Utils", () => {
describe("clamp", () => {
const min = 1;
Expand Down Expand Up @@ -52,8 +66,8 @@ describe("Utils", () => {
});

describe("isInArray", () => {
const numericArray: number[] = [1, 2, 3, Math.PI, Math.E];
const stringArray: string[] = ["lorem", "ipsum", "dolor"];
const numericArray: number[] = [ 1, 2, 3, Math.PI, Math.E ];
const stringArray: string[] = [ "lorem", "ipsum", "dolor" ];

// Numeric

Expand Down Expand Up @@ -113,7 +127,7 @@ describe("Utils", () => {
const weight1 = Math.floor(Math.random() * (size - 1) + 1);
const weight2 = 0;

expect(NumberUtils.mix(comp1, comp2, weight1, weight2), `weight 1: ${weight1}`).to.be.equal(
expect(NumberUtils.mix(comp1, comp2, weight1, weight2), `weight 1: ${ weight1 }`).to.be.equal(
Math.floor(comp1)
);
});
Expand Down Expand Up @@ -146,7 +160,7 @@ describe("Utils", () => {

describe("arrayRandomIndex", () => {
it("should always return an index that is not out of the bounds of the array", () => {
const array = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
const array = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ];
const randomIndex = Utils.arrayRandomIndex(array);

expect(randomIndex % 1).to.equal(0); // Make sure it is an integer
Expand All @@ -157,9 +171,9 @@ describe("Utils", () => {
});

describe("itemFromArray", () => {
const numericArray = [1, 2, 3, Math.PI, Math.E];
const stringArray = ["lorem", "ipsum", "dolor"];
const objectArray = [{ x: 1 }, { y: 2 }, { z: 3 }];
const numericArray = [ 1, 2, 3, Math.PI, Math.E ];
const stringArray = [ "lorem", "ipsum", "dolor" ];
const objectArray = [ { x: 1 }, { y: 2 }, { z: 3 } ];

it("should always return a random item from a numeric array", () => {
const randomItem = Utils.itemFromArray(numericArray);
Expand Down Expand Up @@ -485,4 +499,27 @@ describe("Utils", () => {
expect(NumberUtils.getParticleBaseVelocity(particle.direction).angle).to.eql((-3 * Math.PI) / 4);
});
});

describe("segmentBounce", () => {
const start = {
x: 29, //Math.floor(Math.random() * 100),
y: 82 //Math.floor(Math.random() * 100)
}, stop = {
x: 53, //Math.floor(Math.random() * 100),
y: 82 //Math.floor(Math.random() * 100)
}, velocity = Vector.origin; // angle = 238.91568442036498 * Math.PI / 180;//Math.random() * Math.PI * 2;

velocity.length = 1;
velocity.angle = 238.91568442036498 * Math.PI / 180;

console.log("segment", start, stop);
console.log("s. angle", (Math.atan2(start.y - stop.y, start.x - stop.x) * 180 / Math.PI) % 360);
console.log("p. speed", velocity.length);
console.log("p. angle", (velocity.angle * 180 / Math.PI) % 360);

segmentBounce(start, stop, velocity);

console.log("res. speed", velocity.length);
console.log("res. angle", (velocity.angle * 180 / Math.PI) % 360);
});
});
8 changes: 4 additions & 4 deletions demo/main/public/presets/localPolygonMask.json
Expand Up @@ -96,7 +96,7 @@
"area": 2000
},
"limit": 0,
"value": 80
"value": 1
},
"opacity": {
"value": 0.5
Expand All @@ -111,8 +111,8 @@
"polygon": {
"draw": {
"enable": true,
"lineColor": "rgba(255,255,255,0.2)",
"lineWidth": 0.5
"lineColor": "rgba(255,255,255,1)",
"lineWidth": 1
},
"enable": true,
"move": {
Expand All @@ -123,7 +123,7 @@
"y": 50
},
"inlineArrangement": "equidistant",
"scale": 2,
"scale": 3,
"type": "inside",
"data": "<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" height=\"210\" width=\"400\"><path d=\"M150 0 L75 200 L225 200 Z\" /></svg>"
},
Expand Down

0 comments on commit ecd2170

Please sign in to comment.