1- import {
2- ChangeDetectionStrategy ,
3- Component ,
4- Signal ,
5- afterNextRender ,
6- effect ,
7- input ,
8- signal ,
9- untracked ,
10- } from '@angular/core' ;
1+ import { Directive , Injector , afterNextRender , effect , inject , input , signal , untracked } from '@angular/core' ;
112import {
123 CannonWorkerAPI ,
134 CannonWorkerProps ,
@@ -84,15 +75,6 @@ export interface NgtcPhysicsOptions extends CannonWorkerProps {
8475 stepSize ?: number ;
8576}
8677
87- export interface NgtcPhysicsApi {
88- bodies : { [ uuid : string ] : number } ;
89- events : NgtcCannonEvents ;
90- refs : Refs ;
91- scaleOverrides : ScaleOverrides ;
92- subscriptions : Subscriptions ;
93- worker : Signal < CannonWorkerAPI > ;
94- }
95-
9678const defaultOptions : NgtcPhysicsOptions = {
9779 allowSleep : false ,
9880 axisIndex : 0 ,
@@ -117,14 +99,7 @@ type NgtsPhysicsUpdatableOptions = Extract<
11799 'gravity' | 'iterations' | 'tolerance' | 'broadphase' | 'axisIndex'
118100> ;
119101
120- @Component ( {
121- selector : 'ngtc-physics' ,
122- standalone : true ,
123- template : `
124- <ng-content />
125- ` ,
126- changeDetection : ChangeDetectionStrategy . OnPush ,
127- } )
102+ @Directive ( { selector : 'ngtc-physics' , standalone : true } )
128103export class NgtcPhysics {
129104 private store = injectStore ( ) ;
130105
@@ -138,38 +113,48 @@ export class NgtcPhysics {
138113
139114 private invalidate = this . store . select ( 'invalidate' ) ;
140115 // @ts -expect-error - worker is not nullable, and we don't want to use ! operator.
141- private worker = signal < CannonWorkerAPI > ( null ) ;
116+ private cannonWorker = signal < CannonWorkerAPI > ( null ) ;
142117
143- api : NgtcPhysicsApi = {
144- bodies : { } ,
145- events : { } ,
146- refs : { } ,
147- scaleOverrides : { } ,
148- subscriptions : { } ,
149- worker : this . worker . asReadonly ( ) ,
150- } ;
118+ bodies : { [ uuid : string ] : number } = { } ;
119+ events : NgtcCannonEvents = { } ;
120+ refs : Refs = { } ;
121+ scaleOverrides : ScaleOverrides = { } ;
122+ subscriptions : Subscriptions = { } ;
123+ worker = this . cannonWorker . asReadonly ( ) ;
151124
152125 constructor ( ) {
126+ const injector = inject ( Injector ) ;
127+
128+ // NOTE: set new cannonworker in afterNextRender
129+ // - so inputs are resolved
130+ // - so the worker is instantiated only once
131+ // - effects are started after worker is instantiated
153132 afterNextRender ( ( ) => {
154- this . worker . set ( new CannonWorkerAPI ( this . options ( ) ) ) ;
155- } ) ;
133+ this . cannonWorker . set ( new CannonWorkerAPI ( this . options ( ) ) ) ;
156134
157- effect ( ( onCleanup ) => {
158- const cleanup = this . connectWorkerEffect ( ) ;
159- onCleanup ( ( ) => cleanup ?.( ) ) ;
160- } ) ;
135+ effect (
136+ ( onCleanup ) => {
137+ const cleanup = this . connectWorkerEffect ( ) ;
138+ onCleanup ( ( ) => cleanup ?.( ) ) ;
139+ } ,
140+ { injector } ,
141+ ) ;
161142
162- effect ( ( ) => {
163- this . updateWorkerStateEffect ( 'axisIndex' , this . axisIndex ) ;
164- this . updateWorkerStateEffect ( 'broadphase' , this . broadphase ) ;
165- this . updateWorkerStateEffect ( 'gravity' , this . gravity ) ;
166- this . updateWorkerStateEffect ( 'iterations' , this . iterations ) ;
167- this . updateWorkerStateEffect ( 'tolerance' , this . tolerance ) ;
143+ effect (
144+ ( ) => {
145+ this . updateWorkerStateEffect ( 'axisIndex' , this . axisIndex ) ;
146+ this . updateWorkerStateEffect ( 'broadphase' , this . broadphase ) ;
147+ this . updateWorkerStateEffect ( 'gravity' , this . gravity ) ;
148+ this . updateWorkerStateEffect ( 'iterations' , this . iterations ) ;
149+ this . updateWorkerStateEffect ( 'tolerance' , this . tolerance ) ;
150+ } ,
151+ { injector } ,
152+ ) ;
168153 } ) ;
169154
170155 let timeSinceLastCalled = 0 ;
171156 injectBeforeRender ( ( { delta } ) => {
172- const [ { isPaused, maxSubSteps, stepSize } , worker ] = [ this . options ( ) , this . worker ( ) ] ;
157+ const [ { isPaused, maxSubSteps, stepSize } , worker ] = [ this . options ( ) , this . cannonWorker ( ) ] ;
173158 if ( isPaused || ! worker || stepSize == null ) return ;
174159 timeSinceLastCalled += delta ;
175160 worker . step ( { maxSubSteps, stepSize, timeSinceLastCalled } ) ;
@@ -178,7 +163,7 @@ export class NgtcPhysics {
178163 }
179164
180165 private connectWorkerEffect ( ) {
181- const worker = this . worker ( ) as NgtcCannonWorker ;
166+ const worker = this . cannonWorker ( ) as NgtcCannonWorker ;
182167 if ( ! worker ) return ;
183168
184169 worker . connect ( ) ;
@@ -200,29 +185,29 @@ export class NgtcPhysics {
200185 key : TUpdatableKey ,
201186 option : ( ) => NgtcPhysicsOptions [ TUpdatableKey ] ,
202187 ) {
203- const worker = this . worker ( ) ;
188+ const worker = this . cannonWorker ( ) ;
204189 if ( ! worker ) return ;
205190 Object . assign ( worker , { [ key ] : option ( ) } ) ;
206191 }
207192
208193 private collideHandler ( { body, contact : { bi, bj, ...contactRest } , target, ...rest } : WorkerCollideEvent [ 'data' ] ) {
209- const { events, refs } = this . api ;
194+ const { events, refs } = this ;
210195 const cb = events [ target ] ?. collide ;
211196 if ( cb ) {
212197 cb ( { body : refs [ body ] , contact : { bi : refs [ bi ] , bj : refs [ bj ] , ...contactRest } , target : refs [ target ] , ...rest } ) ;
213198 }
214199 }
215200
216201 private collideBeginHandler ( { bodyA, bodyB } : WorkerCollideBeginEvent [ 'data' ] ) {
217- const { events, refs } = this . api ;
202+ const { events, refs } = this ;
218203 const cbA = events [ bodyA ] ?. collideBegin ;
219204 if ( cbA ) cbA ( { body : refs [ bodyB ] , op : 'event' , target : refs [ bodyA ] , type : 'collideBegin' } ) ;
220205 const cbB = events [ bodyB ] ?. collideBegin ;
221206 if ( cbB ) cbB ( { body : refs [ bodyA ] , op : 'event' , target : refs [ bodyB ] , type : 'collideBegin' } ) ;
222207 }
223208
224209 private collideEndHandler ( { bodyA, bodyB } : WorkerCollideEndEvent [ 'data' ] ) {
225- const { events, refs } = this . api ;
210+ const { events, refs } = this ;
226211 const cbA = events [ bodyA ] ?. collideEnd ;
227212 if ( cbA ) cbA ( { body : refs [ bodyB ] , op : 'event' , target : refs [ bodyA ] , type : 'collideEnd' } ) ;
228213 const cbB = events [ bodyB ] ?. collideEnd ;
@@ -238,7 +223,7 @@ export class NgtcPhysics {
238223 } : WorkerFrameMessage [ 'data' ] ) {
239224 const [ { shouldInvalidate } , { bodies, subscriptions, refs, scaleOverrides } , invalidate ] = [
240225 untracked ( this . options ) ,
241- this . api ,
226+ this ,
242227 this . invalidate ( ) ,
243228 ] ;
244229 for ( let i = 0 ; i < uuids . length ; i ++ ) {
@@ -270,7 +255,7 @@ export class NgtcPhysics {
270255 }
271256
272257 private rayhitHandler ( { body, ray : { uuid, ...rayRest } , ...rest } : WorkerRayhitEvent [ 'data' ] ) {
273- const { events, refs } = this . api ;
258+ const { events, refs } = this ;
274259 const cb = events [ uuid ] ?. rayhit ;
275260 if ( cb ) cb ( { body : body ? refs [ body ] : null , ray : { uuid, ...rayRest } , ...rest } ) ;
276261 }
0 commit comments