import { constructors, particle } from '../core/library.js';
import { mergeOver, pushUnique, λnull } from '../core/utilities.js';
import { requestVector, releaseVector } from './vector.js';
import baseMix from '../mixin/base.js';The Scrawl-canvas particle physics engine is a simple system designed to allow developers a way to add particle-based effects to their canvas animation scenes. The physics engine is built on top of the following components:
We do not have to handle particle generation and manipulation ourselves. Instead, Scrawl-canvas gives us three dedicated entitys which we use to add particle animation effects to the canvas scene. These entitys are:
The Scrawl-canvas particle physics engine system is based on a fairly classical understanding of particle kinetics (applying forces and constraints to a small, spherical object in 3D space) and kinematics (the movement of the small object in response to the forces and constraints applied to it).
A Scrawl-canvas Spring object connects together two Particle objects, linking them together with a set of constraints which together exert a spring force on the Particles.
The Spring factory uses the Base mixin, thus Spring objects can be cloned and killed like other Scrawl-canvas objects. Spring objects are stored in the scrawl.library.spring section of the Scrawl-canvas library object.
import { constructors, particle } from '../core/library.js';
import { mergeOver, pushUnique, λnull } from '../core/utilities.js';
import { requestVector, releaseVector } from './vector.js';
import baseMix from '../mixin/base.js';const Spring = function (items = {}) {
this.makeName(items.name);
this.register();
this.set(this.defs);
this.set(items);
if (!this.action) this.action = λnull;
return this;
};let P = Spring.prototype = Object.create(Object.prototype);
P.type = 'Spring';
P.lib = 'spring';
P.isArtefact = false;
P.isAsset = false;P = baseMix(P);let defaultAttributes = {particleFrom, particleTo - String name of a Particle, or the Particle object itself. These attributes hold references to the Particle objects involved in this constraint.
particleFrom: null,
particleFromIsStatic: false,
particleTo: null,
particleToIsStatic: false,springConstant - float Number. Larger values make the spring stiffer. Suggested values: 5 - 300
springConstant: 50,damperConstant - float Number. Larger values forces the spring to take a longer time to come to equilibrium. Suggested values: 5 - 50
damperConstant: 10,restLength - The spring’s ideal length - the further away from its ideal, the more force the spring will apply to its connected body objects to get them back to their optimal distance
restLength: 1,
};
P.defs = mergeOver(P.defs, defaultAttributes);P.packetObjects = pushUnique(P.packetObjects, ['particleFrom', 'particleTo']);P.kill = function () {
this.deregister();
return true;
};let S = P.setters;particleFrom, particleTo
S.particleFrom = function (item) {
if (item.substring) item = particle[item];
if (item && item.type === 'Particle') this.particleFrom = item;
};
S.particleTo = function (item) {
if (item.substring) item = particle[item];
if (item && item.type === 'Particle') this.particleTo = item;
};P.applySpring = function () {
let {particleFrom, particleTo, particleFromIsStatic, particleToIsStatic, springConstant, damperConstant, restLength} = this;
if (particleFrom && particleTo) {
let {position: fromPosition, velocity: fromVelocity, load: fromLoad} = particleFrom;
let {position: toPosition, velocity: toVelocity, load: toLoad} = particleTo;
let dVelocity = requestVector(toVelocity).vectorSubtract(fromVelocity),
dPosition = requestVector(toPosition).vectorSubtract(fromPosition);
let firstNorm = requestVector(dPosition).normalize(),
secondNorm = requestVector(firstNorm);
firstNorm.scalarMultiply(springConstant * (dPosition.getMagnitude() - restLength));
dVelocity.vectorMultiply(secondNorm).scalarMultiply(damperConstant).vectorMultiply(secondNorm);
let force = requestVector(firstNorm).vectorAdd(dVelocity);
if (!particleFromIsStatic) fromLoad.vectorAdd(force);
if (!particleToIsStatic) toLoad.vectorSubtract(force);
releaseVector(dVelocity, dPosition, firstNorm, secondNorm, force);
}
};scrawl.makeNet({
name: 'test-net',
generate: function () {
let { name, particleStore, springs, springConstant, damperConstant } = this;
let leftParticle, rightParticle;
// generate particles
leftParticle = makeParticle({
name: `${name}-left`,
positionX: 0,
positionY: 0,
});
rightParticle = leftParticle.clone({
name: `${name}-right`,
positionX: 100,
});
leftParticle.run(0, 0, false);
rightParticle.run(0, 0, false);
particleStore.push(leftParticle, rightParticle);
// generate spring
let mySpring = makeSpring({
name: `${name}-link-${i}-${i+1}`,
particleFrom: leftParticle,
particleTo: rightParticle,
springConstant,
damperConstant,
restLength: 100,
});
springs.push(mySpring);
},
...
}).run();
const makeSpring = function (items) {
return new Spring(items);
};
constructors.Spring = Spring;export {
makeSpring,
};