Skip to content

Commit f525124

Browse files
committed
fix: remove Observable from options function
1 parent a189f9a commit f525124

File tree

7 files changed

+93
-171
lines changed

7 files changed

+93
-171
lines changed

libs/angular-three-cannon/services/src/lib/body.ts

Lines changed: 31 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import {
2424
import { injectNgtDestroy, injectNgtRef, NgtInjectedRef, tapEffect } from 'angular-three';
2525
import { NgtcStore, NgtcUtils } from 'angular-three-cannon';
2626
import { NGTC_DEBUG_API } from 'angular-three-cannon/debug';
27-
import { combineLatest, isObservable, Observable, of, ReplaySubject, Subscription, switchMap } from 'rxjs';
27+
import { combineLatest, Observable, ReplaySubject, Subscription } from 'rxjs';
2828
import * as THREE from 'three';
2929

3030
export type NgtcAtomicApi<K extends AtomicName> = {
@@ -71,7 +71,7 @@ export interface NgtcBodyReturn<TObject extends THREE.Object3D> {
7171
api: NgtcBodyPublicApi;
7272
}
7373

74-
export type NgtcGetByIndex<T extends BodyProps> = (index: number) => T | Observable<T>;
74+
export type NgtcGetByIndex<T extends BodyProps> = (index: number) => T;
7575
export type NgtcArgFn<T> = (args: T) => unknown[];
7676

7777
export function injectPlane<TObject extends THREE.Object3D>(
@@ -204,7 +204,7 @@ function injectBody<TBodyProps extends BodyProps, TObject extends THREE.Object3D
204204
// start the pipeline as soon as bodyRef has a truthy value
205205
subscription = combineLatest([physicsStore.select('worker'), bodyRef.$])
206206
.pipe(
207-
switchMap(([worker, object]) => {
207+
tapEffect(([worker, object]) => {
208208
const currentWorker = worker;
209209

210210
const { events, refs } = physicsStore.get();
@@ -221,60 +221,38 @@ function injectBody<TBodyProps extends BodyProps, TObject extends THREE.Object3D
221221
? new Array(objectCount).fill(0).map((_, i) => `${object.uuid}/${i}`)
222222
: [object.uuid];
223223

224-
// construct an Array<Observable> from getPropsFn
225-
// so we can react to props changed
226-
const propsList$ = uuids.map((uuid, index) => {
227-
if (propsSubjectList[index]) {
228-
propsSubjectList[index].complete();
229-
}
230-
propsSubjectList[index] = new ReplaySubject<TBodyProps>(1);
224+
const propsList = uuids.map((uuid, index) => {
231225
const propsResult = getPropsFn(index);
232-
// TODO we use a propsSubject$ because we want to ensure this propsResult stream is HOT
233-
// otherwise, tapEffect will remove everything from currentWorker#bodies because the stream completes
234-
(isObservable(propsResult) ? propsResult : of(propsResult)).subscribe({
235-
next: (props) => {
236-
NgtcUtils.prepare(temp, props);
237-
if (object instanceof THREE.InstancedMesh) {
238-
object.setMatrixAt(index, temp.matrix);
239-
object.instanceMatrix.needsUpdate = true;
240-
}
241-
refs[uuid] = object;
242-
debugApi?.add(uuid, props, type);
243-
NgtcUtils.setupCollision(events, props, uuid);
244-
propsSubjectList[index].next({ ...props, args: argsFn(props.args) });
245-
},
246-
error: (error) => {
247-
console.error(`[NGT Cannon] Error with processing props: ${error}`);
248-
propsSubjectList[index].error(error);
249-
},
250-
});
226+
NgtcUtils.prepare(temp, propsResult);
227+
if (object instanceof THREE.InstancedMesh) {
228+
object.setMatrixAt(index, temp.matrix);
229+
object.instanceMatrix.needsUpdate = true;
230+
}
231+
refs[uuid] = object;
232+
debugApi?.add(uuid, propsResult, type);
233+
NgtcUtils.setupCollision(events, propsResult, uuid);
234+
return { ...propsResult, args: argsFn(propsResult.args) };
235+
});
251236

252-
return propsSubjectList[index];
237+
currentWorker.addBodies({
238+
props: propsList.map(({ onCollide, onCollideBegin, onCollideEnd, ...serializableProps }) => ({
239+
onCollide: Boolean(onCollide),
240+
onCollideBegin: Boolean(onCollideBegin),
241+
onCollideEnd: Boolean(onCollideEnd),
242+
...serializableProps,
243+
})),
244+
type,
245+
uuid: uuids,
253246
});
254247

255-
return combineLatest(propsList$).pipe(
256-
tapEffect((props) => {
257-
currentWorker.addBodies({
258-
props: props.map(({ onCollide, onCollideBegin, onCollideEnd, ...serializableProps }) => ({
259-
onCollide: Boolean(onCollide),
260-
onCollideBegin: Boolean(onCollideBegin),
261-
onCollideEnd: Boolean(onCollideEnd),
262-
...serializableProps,
263-
})),
264-
type,
265-
uuid: uuids,
266-
});
267-
// clean up CannonWorker whenever props changes/completes
268-
return () => {
269-
uuids.forEach((uuid) => {
270-
delete refs[uuid];
271-
debugApi?.remove(uuid);
272-
delete events[uuid];
273-
});
274-
currentWorker.removeBodies({ uuid: uuids });
275-
};
276-
})
277-
);
248+
return () => {
249+
uuids.forEach((uuid) => {
250+
delete refs[uuid];
251+
debugApi?.remove(uuid);
252+
delete events[uuid];
253+
});
254+
currentWorker.removeBodies({ uuid: uuids });
255+
};
278256
})
279257
)
280258
.subscribe();

libs/angular-three-cannon/services/src/lib/constraint.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,9 @@ import {
88
LockConstraintOpts,
99
PointToPointConstraintOpts,
1010
} from '@pmndrs/cannon-worker-api';
11-
import { injectNgtRef, is, makeId, NgtInjectedRef, tapEffect } from 'angular-three';
11+
import { injectNgtDestroy, injectNgtRef, is, makeId, NgtInjectedRef, tapEffect } from 'angular-three';
1212
import { NgtcStore } from 'angular-three-cannon';
13-
import { combineLatest, Observable, takeUntil } from 'rxjs';
14-
import { injectOptionsProcessor } from './utils';
13+
import { combineLatest, takeUntil } from 'rxjs';
1514

1615
export interface NgtcConstraintApi {
1716
disable: () => void;
@@ -45,7 +44,7 @@ export type NgtcOptsFunction<
4544
TOptions extends HingeConstraintOpts | ConstraintOptns = TConstraintType extends 'Hinge'
4645
? HingeConstraintOpts
4746
: ConstraintOptns
48-
> = () => Observable<TOptions> | TOptions;
47+
> = () => TOptions;
4948

5049
export function injectPointToPointConstraint<
5150
TObjectA extends THREE.Object3D = THREE.Object3D,
@@ -115,17 +114,18 @@ function injectConstraint<
115114
bodyB: NgtInjectedRef<TObjectB> | TObjectB,
116115
optsFn: NgtcOptsFunction<TConstraintType, TOptions> = () => ({} as TOptions)
117116
): NgtcConstraintReturn<TConstraintType, TObjectA, TObjectB> {
118-
const { opts$, destroy$ } = injectOptionsProcessor(optsFn);
117+
const { destroy$ } = injectNgtDestroy();
119118
const uuid = makeId();
120119

121120
const physicsStore = inject(NgtcStore, { skipSelf: true });
122121

123122
const bodyARef = !is.ref(bodyA) ? injectNgtRef(bodyA) : bodyA;
124123
const bodyBRef = !is.ref(bodyB) ? injectNgtRef(bodyB) : bodyB;
125124

126-
combineLatest([physicsStore.select('worker'), bodyARef.$, bodyBRef.$, opts$])
125+
combineLatest([physicsStore.select('worker'), bodyARef.$, bodyBRef.$])
127126
.pipe(
128-
tapEffect(([worker, bodyA, bodyB, opts]) => {
127+
tapEffect(([worker, bodyA, bodyB]) => {
128+
const opts = optsFn();
129129
worker.addConstraint({ props: [bodyA.uuid, bodyB.uuid, opts], type, uuid });
130130
return () => worker.removeConstraint({ uuid });
131131
}),

libs/angular-three-cannon/services/src/lib/contact-material.ts

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,24 @@
11
import { inject } from '@angular/core';
22
import { ContactMaterialOptions, MaterialOptions } from '@pmndrs/cannon-worker-api';
3-
import { makeId, tapEffect } from 'angular-three';
3+
import { injectNgtDestroy, makeId, tapEffect } from 'angular-three';
44
import { NgtcStore } from 'angular-three-cannon';
5-
import { combineLatest, Observable, takeUntil } from 'rxjs';
6-
import { injectOptionsProcessor } from './utils';
5+
import { takeUntil } from 'rxjs';
76

87
export function injectContactMaterial(
98
materialA: MaterialOptions,
109
materialB: MaterialOptions,
11-
optionsFn: () => Observable<ContactMaterialOptions> | ContactMaterialOptions
10+
optionsFn: () => ContactMaterialOptions
1211
) {
13-
const { opts$, destroy$ } = injectOptionsProcessor(optionsFn);
12+
const { destroy$ } = injectNgtDestroy();
1413

1514
const physicsStore = inject(NgtcStore, { skipSelf: true });
1615
const uuid = makeId();
1716

18-
combineLatest([physicsStore.select('worker'), opts$])
17+
physicsStore
18+
.select('worker')
1919
.pipe(
20-
tapEffect(([worker, options]) => {
20+
tapEffect((worker) => {
21+
const options = optionsFn();
2122
worker.addContactMaterial({ props: [materialA, materialB, options], uuid });
2223
return () => worker.removeContactMaterial({ uuid });
2324
}),

libs/angular-three-cannon/services/src/lib/ray.ts

Lines changed: 11 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,31 @@
11
import { inject } from '@angular/core';
22
import { RayhitEvent, RayMode, RayOptions } from '@pmndrs/cannon-worker-api';
3-
import { makeId, tapEffect } from 'angular-three';
3+
import { injectNgtDestroy, makeId, tapEffect } from 'angular-three';
44
import { NgtcStore } from 'angular-three-cannon';
5-
import { combineLatest, Observable, takeUntil } from 'rxjs';
6-
import { injectOptionsProcessor } from './utils';
5+
import { takeUntil } from 'rxjs';
76

8-
export function injectRaycastClosest(
9-
optionsFn: () => Observable<RayOptions> | RayOptions,
10-
callback: (e: RayhitEvent) => void
11-
) {
7+
export function injectRaycastClosest(optionsFn: () => RayOptions, callback: (e: RayhitEvent) => void) {
128
injectRay('Closest', optionsFn, callback);
139
}
1410

15-
export function injectRaycastAny(
16-
optionsFn: () => Observable<RayOptions> | RayOptions,
17-
callback: (e: RayhitEvent) => void
18-
) {
11+
export function injectRaycastAny(optionsFn: () => RayOptions, callback: (e: RayhitEvent) => void) {
1912
injectRay('Any', optionsFn, callback);
2013
}
2114

22-
export function injectRaycastAll(
23-
optionsFn: () => Observable<RayOptions> | RayOptions,
24-
callback: (e: RayhitEvent) => void
25-
) {
15+
export function injectRaycastAll(optionsFn: () => RayOptions, callback: (e: RayhitEvent) => void) {
2616
injectRay('All', optionsFn, callback);
2717
}
2818

29-
function injectRay(
30-
mode: RayMode,
31-
optionsFn: () => Observable<RayOptions> | RayOptions,
32-
callback: (event: RayhitEvent) => void
33-
): void {
34-
const { opts$, destroy$ } = injectOptionsProcessor(optionsFn);
19+
function injectRay(mode: RayMode, optionsFn: () => RayOptions, callback: (event: RayhitEvent) => void): void {
20+
const { destroy$ } = injectNgtDestroy();
3521
const uuid = makeId();
3622
const physicsStore = inject(NgtcStore, { skipSelf: true });
3723

38-
combineLatest([physicsStore.select('worker'), opts$])
24+
physicsStore
25+
.select('worker')
3926
.pipe(
40-
tapEffect(([worker, opts]) => {
27+
tapEffect((worker) => {
28+
const opts = optionsFn();
4129
const events = physicsStore.get('events');
4230
events[uuid] = { rayhit: callback };
4331
worker.addRay({ props: { ...opts, mode }, uuid });

libs/angular-three-cannon/services/src/lib/raycast-vehicle.ts

Lines changed: 29 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { WheelInfoOptions } from '@pmndrs/cannon-worker-api';
33
import { injectNgtDestroy, injectNgtRef, NgtInjectedRef, tapEffect } from 'angular-three';
44
import { NgtcStore, NgtcUtils } from 'angular-three-cannon';
55
import { combineLatest, takeUntil } from 'rxjs';
6-
import { Object3D } from 'three';
6+
import * as THREE from 'three';
77

88
export interface NgtcRaycastVehicleProps {
99
chassisBody: NgtInjectedRef<THREE.Object3D>;
@@ -36,44 +36,42 @@ export function injectRaycastVehicle<TObject extends THREE.Object3D = THREE.Obje
3636

3737
let ref = injectNgtRef<TObject>();
3838

39-
if (instanceRef) {
40-
ref = instanceRef;
41-
}
39+
if (instanceRef) ref = instanceRef;
4240

4341
queueMicrotask(() => {
44-
if (!ref.nativeElement) {
45-
ref.nativeElement = new Object3D() as TObject;
46-
}
42+
if (!ref.nativeElement) ref.nativeElement = new THREE.Object3D() as TObject;
4743
});
4844

49-
const { chassisBody, indexForwardAxis = 2, indexRightAxis = 0, indexUpAxis = 1, wheelInfos, wheels } = fn();
45+
queueMicrotask(() => {
46+
const { chassisBody, indexForwardAxis = 2, indexRightAxis = 0, indexUpAxis = 1, wheelInfos, wheels } = fn();
5047

51-
combineLatest([store.select('worker'), ref.$, chassisBody.$, ...wheels.map((wheel) => wheel.$)])
52-
.pipe(
53-
tapEffect(([worker, object]) => {
54-
const uuid = object.uuid;
55-
const chassisBodyUUID = NgtcUtils.getUUID(chassisBody);
56-
const wheelUUIDs = wheels.map((wheel) => NgtcUtils.getUUID(wheel));
48+
combineLatest([store.select('worker'), ref.$, chassisBody.$, ...wheels.map((wheel) => wheel.$)])
49+
.pipe(
50+
tapEffect(([worker, object]) => {
51+
const uuid = object.uuid;
52+
const chassisBodyUUID = NgtcUtils.getUUID(chassisBody);
53+
const wheelUUIDs = wheels.map((wheel) => NgtcUtils.getUUID(wheel));
5754

58-
if (!chassisBodyUUID || !wheelUUIDs.every((v) => typeof v === 'string')) return;
55+
if (!chassisBodyUUID || !wheelUUIDs.every((v) => typeof v === 'string')) return;
5956

60-
worker.addRaycastVehicle({
61-
props: [
62-
chassisBodyUUID,
63-
wheelUUIDs as string[],
64-
wheelInfos,
65-
indexForwardAxis,
66-
indexRightAxis,
67-
indexUpAxis,
68-
],
69-
uuid,
70-
});
57+
worker.addRaycastVehicle({
58+
props: [
59+
chassisBodyUUID,
60+
wheelUUIDs as string[],
61+
wheelInfos,
62+
indexForwardAxis,
63+
indexRightAxis,
64+
indexUpAxis,
65+
],
66+
uuid,
67+
});
7168

72-
return () => worker.removeRaycastVehicle({ uuid });
73-
}),
74-
takeUntil(destroy$)
75-
)
76-
.subscribe();
69+
return () => worker.removeRaycastVehicle({ uuid });
70+
}),
71+
takeUntil(destroy$)
72+
)
73+
.subscribe();
74+
});
7775

7876
const api = {
7977
applyEngineForce: (value: number, wheelIndex: number) => {

libs/angular-three-cannon/services/src/lib/spring.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
import { inject } from '@angular/core';
22
import { SpringOptns } from '@pmndrs/cannon-worker-api';
3-
import { makeId, NgtInjectedRef, tapEffect } from 'angular-three';
3+
import { injectNgtDestroy, makeId, NgtInjectedRef, tapEffect } from 'angular-three';
44
import { NgtcStore } from 'angular-three-cannon';
5-
import { combineLatest, Observable, takeUntil } from 'rxjs';
6-
import { injectOptionsProcessor } from './utils';
5+
import { combineLatest, takeUntil } from 'rxjs';
76

87
export interface NgtcSpringApi {
98
setDamping: (value: number) => void;
@@ -27,15 +26,16 @@ export function injectSpring<
2726
>(
2827
bodyA: NgtInjectedRef<TObjectA>,
2928
bodyB: NgtInjectedRef<TObjectB>,
30-
optsFn: () => Observable<SpringOptns> | SpringOptns
29+
optsFn: () => SpringOptns
3130
): NgtcSpringReturn<TObjectA, TObjectB> {
3231
const store = inject(NgtcStore, { skipSelf: true });
3332
const uuid = makeId();
34-
const { opts$, destroy$ } = injectOptionsProcessor(optsFn);
33+
const { destroy$ } = injectNgtDestroy();
3534

36-
combineLatest([store.select('worker'), bodyA.$, bodyB.$, opts$])
35+
combineLatest([store.select('worker'), bodyA.$, bodyB.$])
3736
.pipe(
38-
tapEffect(([worker, bodyA, bodyB, opts]) => {
37+
tapEffect(([worker, bodyA, bodyB]) => {
38+
const opts = optsFn();
3939
worker.addSpring({ props: [bodyA.uuid, bodyB.uuid, opts], uuid });
4040
return () => worker.removeSpring({ uuid });
4141
}),

0 commit comments

Comments
 (0)