Skip to content

Commit 81dd2fb

Browse files
committed
added stable sorting to test worker and refactor
1 parent bcc3168 commit 81dd2fb

1 file changed

Lines changed: 111 additions & 96 deletions

File tree

test/ExampleWorker.js

Lines changed: 111 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -6,22 +6,57 @@ const mock = require('mock-require');
66
const { requireUncached } = require('./TestTools');
77
const consoleOriginal = global.console;
88

9-
const intrinsicProps = [
10-
// Common
11-
'id', 'label',
9+
const runExample = options => {
10+
const Matter = prepareMatter(options);
11+
const logs = prepareEnvironment(Matter);
1212

13-
// Constraint
14-
'angularStiffness', 'bodyA', 'bodyB', 'damping', 'length', 'stiffness',
13+
const Examples = requireUncached('../examples/index');
14+
const example = Examples[options.name]();
15+
const engine = example.engine;
16+
17+
let totalMemory = 0;
18+
let totalDuration = 0;
19+
let overlapTotal = 0;
20+
let overlapCount = 0;
1521

16-
// Body
17-
'area', 'axes', 'collisionFilter', 'category', 'mask',
18-
'group', 'density', 'friction', 'frictionAir', 'frictionStatic', 'inertia', 'inverseInertia', 'inverseMass', 'isSensor',
19-
'isSleeping', 'isStatic', 'mass', 'parent', 'parts', 'restitution', 'sleepThreshold', 'slop',
20-
'timeScale', 'vertices',
22+
global.gc();
2123

22-
// Composite
23-
'bodies', 'constraints', 'composites'
24-
];
24+
for (let i = 0; i < options.updates; i += 1) {
25+
const startTime = process.hrtime();
26+
totalMemory += process.memoryUsage().heapUsed;
27+
28+
Matter.Engine.update(engine, 1000 / 60);
29+
30+
const duration = process.hrtime(startTime);
31+
totalDuration += duration[0] * 1e9 + duration[1];
32+
totalMemory += process.memoryUsage().heapUsed;
33+
34+
const pairsList = engine.pairs.list;
35+
const pairsListLength = engine.pairs.list.length;
36+
37+
for (let p = 0; p < pairsListLength; p += 1) {
38+
const pair = pairsList[p];
39+
const separation = pair.separation - pair.slop;
40+
41+
if (pair.isActive && !pair.isSensor) {
42+
overlapTotal += separation > 0 ? separation : 0;
43+
overlapCount += 1;
44+
}
45+
}
46+
}
47+
48+
resetEnvironment();
49+
50+
return {
51+
name: options.name,
52+
duration: totalDuration,
53+
overlap: overlapTotal / (overlapCount || 1),
54+
memory: totalMemory,
55+
logs: logs,
56+
extrinsic: captureExtrinsics(engine, Matter),
57+
intrinsic: captureIntrinsics(engine, Matter),
58+
};
59+
};
2560

2661
const prepareMatter = (options) => {
2762
const Matter = requireUncached(options.useDev ? '../build/matter.dev' : '../build/matter');
@@ -37,6 +72,37 @@ const prepareMatter = (options) => {
3772
Matter.Runner.create = Matter.Runner.run = noop;
3873
Matter.MouseConstraint.create = Matter.Mouse.create = noop;
3974
Matter.Common.info = Matter.Common.warn = Matter.Common.log;
75+
76+
if (options.stableSort) {
77+
const MatterSATCollides = Matter.SAT.collides;
78+
Matter.SAT.collides = function(bodyA, bodyB, previousCollision, pairActive) {
79+
const _bodyA = bodyA.id < bodyB.id ? bodyA : bodyB;
80+
const _bodyB = bodyA.id < bodyB.id ? bodyB : bodyA;
81+
return MatterSATCollides(_bodyA, _bodyB, previousCollision, pairActive);
82+
};
83+
84+
Matter.after('Detector.collisions', function() { this.sort(collisionCompareId); });
85+
Matter.after('Composite.allBodies', function() { sortById(this); });
86+
Matter.after('Composite.allConstraints', function() { sortById(this); });
87+
Matter.after('Composite.allComposites', function() { sortById(this); });
88+
89+
Matter.before('Pairs.update', function(pairs) {
90+
pairs.list.sort((pairA, pairB) => collisionCompareId(pairA.collision, pairB.collision));
91+
});
92+
93+
Matter.after('Pairs.update', function(pairs) {
94+
pairs.list.sort((pairA, pairB) => collisionCompareId(pairA.collision, pairB.collision));
95+
});
96+
}
97+
98+
if (options.jitter) {
99+
Matter.after('Body.create', function() {
100+
Matter.Body.applyForce(this, this.position, {
101+
x: Math.cos(this.id * this.id) * options.jitter * this.mass,
102+
y: Math.sin(this.id * this.id) * options.jitter * this.mass
103+
});
104+
});
105+
}
40106

41107
return Matter;
42108
};
@@ -64,21 +130,8 @@ const resetEnvironment = () => {
64130
mock.stopAll();
65131
};
66132

67-
const limitPrecision = (val, precision=3) => parseFloat(val.toPrecision(precision));
68-
69-
const sortById = (objs) => {
70-
objs.sort((objA, objB) => objA.id - objB.id);
71-
return objs;
72-
};
73-
74-
const engineCapture = (engine, Matter) => ({
75-
timestamp: limitPrecision(engine.timing.timestamp),
76-
extrinsic: worldCaptureExtrinsic(engine.world, Matter),
77-
intrinsic: worldCaptureIntrinsic(engine.world, Matter)
78-
});
79-
80-
const worldCaptureExtrinsic = (world, Matter) => ({
81-
bodies: sortById(Matter.Composite.allBodies(world)).reduce((bodies, body) => {
133+
const captureExtrinsics = ({ world }, Matter) => ({
134+
bodies: Matter.Composite.allBodies(world).reduce((bodies, body) => {
82135
bodies[body.id] = [
83136
body.position.x,
84137
body.position.y,
@@ -91,7 +144,7 @@ const worldCaptureExtrinsic = (world, Matter) => ({
91144

92145
return bodies;
93146
}, {}),
94-
constraints: sortById(Matter.Composite.allConstraints(world)).reduce((constraints, constraint) => {
147+
constraints: Matter.Composite.allConstraints(world).reduce((constraints, constraint) => {
95148
const positionA = Matter.Constraint.pointAWorld(constraint);
96149
const positionB = Matter.Constraint.pointBWorld(constraint);
97150

@@ -106,38 +159,38 @@ const worldCaptureExtrinsic = (world, Matter) => ({
106159
}, {})
107160
});
108161

109-
const worldCaptureIntrinsic = (world, Matter) => worldCaptureIntrinsicBase({
110-
bodies: sortById(Matter.Composite.allBodies(world)).reduce((bodies, body) => {
162+
const captureIntrinsics = ({ world }, Matter) => formatIntrinsics({
163+
bodies: Matter.Composite.allBodies(world).reduce((bodies, body) => {
111164
bodies[body.id] = body;
112165
return bodies;
113166
}, {}),
114-
constraints: sortById(Matter.Composite.allConstraints(world)).reduce((constraints, constraint) => {
167+
constraints: Matter.Composite.allConstraints(world).reduce((constraints, constraint) => {
115168
constraints[constraint.id] = constraint;
116169
return constraints;
117170
}, {}),
118-
composites: sortById(Matter.Composite.allComposites(world)).reduce((composites, composite) => {
171+
composites: Matter.Composite.allComposites(world).reduce((composites, composite) => {
119172
composites[composite.id] = {
120-
bodies: sortById(Matter.Composite.allBodies(composite)).map(body => body.id),
121-
constraints: sortById(Matter.Composite.allConstraints(composite)).map(constraint => constraint.id),
122-
composites: sortById(Matter.Composite.allComposites(composite)).map(composite => composite.id)
173+
bodies: Matter.Composite.allBodies(composite).map(body => body.id),
174+
constraints: Matter.Composite.allConstraints(composite).map(constraint => constraint.id),
175+
composites: Matter.Composite.allComposites(composite).map(composite => composite.id)
123176
};
124177
return composites;
125178
}, {})
126179
});
127180

128-
const worldCaptureIntrinsicBase = (obj, depth=0) => {
181+
const formatIntrinsics = (obj, depth=0) => {
129182
if (obj === Infinity) {
130183
return 'Infinity';
131184
} else if (typeof obj === 'number') {
132185
return limitPrecision(obj);
133186
} else if (Array.isArray(obj)) {
134-
return obj.map(item => worldCaptureIntrinsicBase(item, depth + 1));
187+
return obj.map(item => formatIntrinsics(item, depth + 1));
135188
} else if (typeof obj !== 'object') {
136189
return obj;
137190
}
138191

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

150-
cleaned[key] = worldCaptureIntrinsicBase(val, depth + 1);
203+
cleaned[key] = formatIntrinsics(val, depth + 1);
151204
return cleaned;
152205
}, {});
153206

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

158-
const runExample = options => {
159-
const Matter = prepareMatter(options);
160-
const logs = prepareEnvironment(Matter);
161-
162-
const Examples = requireUncached('../examples/index');
163-
const example = Examples[options.name]();
164-
const engine = example.engine;
165-
166-
let totalMemory = 0;
167-
let totalDuration = 0;
168-
let overlapTotal = 0;
169-
let overlapCount = 0;
170-
171-
const bodies = Matter.Composite.allBodies(engine.world);
172-
173-
if (options.jitter) {
174-
for (let i = 0; i < bodies.length; i += 1) {
175-
const body = bodies[i];
176-
177-
Matter.Body.applyForce(body, body.position, {
178-
x: Math.cos(i * i) * options.jitter * body.mass,
179-
y: Math.sin(i * i) * options.jitter * body.mass
180-
});
181-
}
182-
}
183-
184-
global.gc();
211+
const intrinsicProperties = [
212+
// Common
213+
'id', 'label',
185214

186-
for (let i = 0; i < options.totalUpdates; i += 1) {
187-
const startTime = process.hrtime();
188-
totalMemory += process.memoryUsage().heapUsed;
189-
190-
Matter.Engine.update(engine, 1000 / 60);
215+
// Constraint
216+
'angularStiffness', 'bodyA', 'bodyB', 'damping', 'length', 'stiffness',
191217

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

196-
const pairsList = engine.pairs.list;
197-
const pairsListLength = engine.pairs.list.length;
224+
// Composite
225+
'bodies', 'constraints', 'composites'
226+
];
198227

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

203-
if (pair.isActive && !pair.isSensor) {
204-
overlapTotal += separation > 0 ? separation : 0;
205-
overlapCount += 1;
206-
}
207-
}
208-
}
231+
const collisionCompareId = (collisionA, collisionB) => collisionId(collisionA) - collisionId(collisionB);
209232

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

212-
return {
213-
name: options.name,
214-
duration: totalDuration,
215-
overlap: overlapTotal / (overlapCount || 1),
216-
memory: totalMemory,
217-
logs,
218-
...engineCapture(engine, Matter)
219-
};
220-
};
235+
const limitPrecision = (val, precision=3) => parseFloat(val.toPrecision(precision));
221236

222237
module.exports = { runExample };

0 commit comments

Comments
 (0)