Skip to content

Commit

Permalink
added stable sorting to test worker and refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
liabru committed Nov 30, 2021
1 parent bcc3168 commit 81dd2fb
Showing 1 changed file with 111 additions and 96 deletions.
207 changes: 111 additions & 96 deletions test/ExampleWorker.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,57 @@ const mock = require('mock-require');
const { requireUncached } = require('./TestTools');
const consoleOriginal = global.console;

const intrinsicProps = [
// Common
'id', 'label',
const runExample = options => {
const Matter = prepareMatter(options);
const logs = prepareEnvironment(Matter);

// Constraint
'angularStiffness', 'bodyA', 'bodyB', 'damping', 'length', 'stiffness',
const Examples = requireUncached('../examples/index');
const example = Examples[options.name]();
const engine = example.engine;

let totalMemory = 0;
let totalDuration = 0;
let overlapTotal = 0;
let overlapCount = 0;

// Body
'area', 'axes', 'collisionFilter', 'category', 'mask',
'group', 'density', 'friction', 'frictionAir', 'frictionStatic', 'inertia', 'inverseInertia', 'inverseMass', 'isSensor',
'isSleeping', 'isStatic', 'mass', 'parent', 'parts', 'restitution', 'sleepThreshold', 'slop',
'timeScale', 'vertices',
global.gc();

// Composite
'bodies', 'constraints', 'composites'
];
for (let i = 0; i < options.updates; i += 1) {
const startTime = process.hrtime();
totalMemory += process.memoryUsage().heapUsed;

Matter.Engine.update(engine, 1000 / 60);

const duration = process.hrtime(startTime);
totalDuration += duration[0] * 1e9 + duration[1];
totalMemory += process.memoryUsage().heapUsed;

const pairsList = engine.pairs.list;
const pairsListLength = engine.pairs.list.length;

for (let p = 0; p < pairsListLength; p += 1) {
const pair = pairsList[p];
const separation = pair.separation - pair.slop;

if (pair.isActive && !pair.isSensor) {
overlapTotal += separation > 0 ? separation : 0;
overlapCount += 1;
}
}
}

resetEnvironment();

return {
name: options.name,
duration: totalDuration,
overlap: overlapTotal / (overlapCount || 1),
memory: totalMemory,
logs: logs,
extrinsic: captureExtrinsics(engine, Matter),
intrinsic: captureIntrinsics(engine, Matter),
};
};

const prepareMatter = (options) => {
const Matter = requireUncached(options.useDev ? '../build/matter.dev' : '../build/matter');
Expand All @@ -37,6 +72,37 @@ const prepareMatter = (options) => {
Matter.Runner.create = Matter.Runner.run = noop;
Matter.MouseConstraint.create = Matter.Mouse.create = noop;
Matter.Common.info = Matter.Common.warn = Matter.Common.log;

if (options.stableSort) {
const MatterSATCollides = Matter.SAT.collides;
Matter.SAT.collides = function(bodyA, bodyB, previousCollision, pairActive) {
const _bodyA = bodyA.id < bodyB.id ? bodyA : bodyB;
const _bodyB = bodyA.id < bodyB.id ? bodyB : bodyA;
return MatterSATCollides(_bodyA, _bodyB, previousCollision, pairActive);
};

Matter.after('Detector.collisions', function() { this.sort(collisionCompareId); });
Matter.after('Composite.allBodies', function() { sortById(this); });
Matter.after('Composite.allConstraints', function() { sortById(this); });
Matter.after('Composite.allComposites', function() { sortById(this); });

Matter.before('Pairs.update', function(pairs) {
pairs.list.sort((pairA, pairB) => collisionCompareId(pairA.collision, pairB.collision));
});

Matter.after('Pairs.update', function(pairs) {
pairs.list.sort((pairA, pairB) => collisionCompareId(pairA.collision, pairB.collision));
});
}

if (options.jitter) {
Matter.after('Body.create', function() {
Matter.Body.applyForce(this, this.position, {
x: Math.cos(this.id * this.id) * options.jitter * this.mass,
y: Math.sin(this.id * this.id) * options.jitter * this.mass
});
});
}

return Matter;
};
Expand Down Expand Up @@ -64,21 +130,8 @@ const resetEnvironment = () => {
mock.stopAll();
};

const limitPrecision = (val, precision=3) => parseFloat(val.toPrecision(precision));

const sortById = (objs) => {
objs.sort((objA, objB) => objA.id - objB.id);
return objs;
};

const engineCapture = (engine, Matter) => ({
timestamp: limitPrecision(engine.timing.timestamp),
extrinsic: worldCaptureExtrinsic(engine.world, Matter),
intrinsic: worldCaptureIntrinsic(engine.world, Matter)
});

const worldCaptureExtrinsic = (world, Matter) => ({
bodies: sortById(Matter.Composite.allBodies(world)).reduce((bodies, body) => {
const captureExtrinsics = ({ world }, Matter) => ({
bodies: Matter.Composite.allBodies(world).reduce((bodies, body) => {
bodies[body.id] = [
body.position.x,
body.position.y,
Expand All @@ -91,7 +144,7 @@ const worldCaptureExtrinsic = (world, Matter) => ({

return bodies;
}, {}),
constraints: sortById(Matter.Composite.allConstraints(world)).reduce((constraints, constraint) => {
constraints: Matter.Composite.allConstraints(world).reduce((constraints, constraint) => {
const positionA = Matter.Constraint.pointAWorld(constraint);
const positionB = Matter.Constraint.pointBWorld(constraint);

Expand All @@ -106,38 +159,38 @@ const worldCaptureExtrinsic = (world, Matter) => ({
}, {})
});

const worldCaptureIntrinsic = (world, Matter) => worldCaptureIntrinsicBase({
bodies: sortById(Matter.Composite.allBodies(world)).reduce((bodies, body) => {
const captureIntrinsics = ({ world }, Matter) => formatIntrinsics({
bodies: Matter.Composite.allBodies(world).reduce((bodies, body) => {
bodies[body.id] = body;
return bodies;
}, {}),
constraints: sortById(Matter.Composite.allConstraints(world)).reduce((constraints, constraint) => {
constraints: Matter.Composite.allConstraints(world).reduce((constraints, constraint) => {
constraints[constraint.id] = constraint;
return constraints;
}, {}),
composites: sortById(Matter.Composite.allComposites(world)).reduce((composites, composite) => {
composites: Matter.Composite.allComposites(world).reduce((composites, composite) => {
composites[composite.id] = {
bodies: sortById(Matter.Composite.allBodies(composite)).map(body => body.id),
constraints: sortById(Matter.Composite.allConstraints(composite)).map(constraint => constraint.id),
composites: sortById(Matter.Composite.allComposites(composite)).map(composite => composite.id)
bodies: Matter.Composite.allBodies(composite).map(body => body.id),
constraints: Matter.Composite.allConstraints(composite).map(constraint => constraint.id),
composites: Matter.Composite.allComposites(composite).map(composite => composite.id)
};
return composites;
}, {})
});

const worldCaptureIntrinsicBase = (obj, depth=0) => {
const formatIntrinsics = (obj, depth=0) => {
if (obj === Infinity) {
return 'Infinity';
} else if (typeof obj === 'number') {
return limitPrecision(obj);
} else if (Array.isArray(obj)) {
return obj.map(item => worldCaptureIntrinsicBase(item, depth + 1));
return obj.map(item => formatIntrinsics(item, depth + 1));
} else if (typeof obj !== 'object') {
return obj;
}

const result = Object.entries(obj)
.filter(([key]) => depth <= 1 || intrinsicProps.includes(key))
.filter(([key]) => depth <= 1 || intrinsicProperties.includes(key))
.reduce((cleaned, [key, val]) => {
if (val && val.id && String(val.id) !== key) {
val = val.id;
Expand All @@ -147,76 +200,38 @@ const worldCaptureIntrinsicBase = (obj, depth=0) => {
val = `[${val.length}]`;
}

cleaned[key] = worldCaptureIntrinsicBase(val, depth + 1);
cleaned[key] = formatIntrinsics(val, depth + 1);
return cleaned;
}, {});

return Object.keys(result).sort()
.reduce((sorted, key) => (sorted[key] = result[key], sorted), {});
};

const runExample = options => {
const Matter = prepareMatter(options);
const logs = prepareEnvironment(Matter);

const Examples = requireUncached('../examples/index');
const example = Examples[options.name]();
const engine = example.engine;

let totalMemory = 0;
let totalDuration = 0;
let overlapTotal = 0;
let overlapCount = 0;

const bodies = Matter.Composite.allBodies(engine.world);

if (options.jitter) {
for (let i = 0; i < bodies.length; i += 1) {
const body = bodies[i];

Matter.Body.applyForce(body, body.position, {
x: Math.cos(i * i) * options.jitter * body.mass,
y: Math.sin(i * i) * options.jitter * body.mass
});
}
}

global.gc();
const intrinsicProperties = [
// Common
'id', 'label',

for (let i = 0; i < options.totalUpdates; i += 1) {
const startTime = process.hrtime();
totalMemory += process.memoryUsage().heapUsed;

Matter.Engine.update(engine, 1000 / 60);
// Constraint
'angularStiffness', 'bodyA', 'bodyB', 'damping', 'length', 'stiffness',

const duration = process.hrtime(startTime);
totalDuration += duration[0] * 1e9 + duration[1];
totalMemory += process.memoryUsage().heapUsed;
// Body
'area', 'axes', 'collisionFilter', 'category', 'mask',
'group', 'density', 'friction', 'frictionAir', 'frictionStatic', 'inertia', 'inverseInertia', 'inverseMass', 'isSensor',
'isSleeping', 'isStatic', 'mass', 'parent', 'parts', 'restitution', 'sleepThreshold', 'slop',
'timeScale', 'vertices',

const pairsList = engine.pairs.list;
const pairsListLength = engine.pairs.list.length;
// Composite
'bodies', 'constraints', 'composites'
];

for (let p = 0; p < pairsListLength; p += 1) {
const pair = pairsList[p];
const separation = pair.separation - pair.slop;
const collisionId = (collision) =>
Math.min(collision.bodyA.id, collision.bodyB.id) + Math.max(collision.bodyA.id, collision.bodyB.id) * 10000;

if (pair.isActive && !pair.isSensor) {
overlapTotal += separation > 0 ? separation : 0;
overlapCount += 1;
}
}
}
const collisionCompareId = (collisionA, collisionB) => collisionId(collisionA) - collisionId(collisionB);

resetEnvironment();
const sortById = (objs) => objs.sort((objA, objB) => objA.id - objB.id);

return {
name: options.name,
duration: totalDuration,
overlap: overlapTotal / (overlapCount || 1),
memory: totalMemory,
logs,
...engineCapture(engine, Matter)
};
};
const limitPrecision = (val, precision=3) => parseFloat(val.toPrecision(precision));

module.exports = { runExample };

0 comments on commit 81dd2fb

Please sign in to comment.