@@ -6,22 +6,57 @@ const mock = require('mock-require');
66const { requireUncached } = require ( './TestTools' ) ;
77const 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
2661const 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
222237module . exports = { runExample } ;
0 commit comments