Skip to content
This repository has been archived by the owner on Feb 10, 2023. It is now read-only.

Commit

Permalink
fix(cannon): strong type object type for physics APIs
Browse files Browse the repository at this point in the history
  • Loading branch information
nartc committed May 10, 2022
1 parent 6bf9d89 commit 7e0d788
Show file tree
Hide file tree
Showing 5 changed files with 107 additions and 65 deletions.
73 changes: 43 additions & 30 deletions libs/cannon/src/lib/body.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,8 @@ export interface NgtPhysicsBodyPublicApi extends WorkerApi {
at: (index: number) => WorkerApi;
}

export interface NgtPhysicBodyReturn {
ref: Ref<THREE.Object3D>;
export interface NgtPhysicBodyReturn<TObject extends THREE.Object3D> {
ref: Ref<TObject>;
api: NgtPhysicsBodyPublicApi;
}

Expand All @@ -91,7 +91,10 @@ type ArgFn<T> = (args: T) => unknown[];

const temp = new THREE.Object3D();

function applyBodyProps<TBodyProps extends BodyProps>(ref: Ref<THREE.Object3D>, props: TBodyProps) {
function applyBodyProps<TBodyProps extends BodyProps, TObject extends THREE.Object3D = THREE.Object3D>(
ref: Ref<TObject>,
props: TBodyProps
) {
const objectProps: UnknownRecord = {};
if (props.position) {
objectProps['position'] = makeVector3(props.position);
Expand Down Expand Up @@ -160,29 +163,33 @@ export class NgtPhysicBody extends NgtComponentStore {
super();
}

usePlane(fn: GetByIndex<PlaneProps>, useOnTemplate = true, ref?: Ref<THREE.Object3D>) {
return this.useBody('Plane', fn, () => [], useOnTemplate, ref);
usePlane<TObject extends THREE.Object3D>(fn: GetByIndex<PlaneProps>, useOnTemplate = true, ref?: Ref<TObject>) {
return this.useBody<PlaneProps, TObject>('Plane', fn, () => [], useOnTemplate, ref);
}

useBox(fn: GetByIndex<BoxProps>, useOnTemplate = true, ref?: Ref<THREE.Object3D>) {
useBox<TObject extends THREE.Object3D>(fn: GetByIndex<BoxProps>, useOnTemplate = true, ref?: Ref<TObject>) {
const defaultBoxArgs: NgtTriple = [1, 1, 1];
return this.useBody('Box', fn, (args = defaultBoxArgs): NgtTriple => args, useOnTemplate, ref);
return this.useBody<BoxProps, TObject>('Box', fn, (args = defaultBoxArgs): NgtTriple => args, useOnTemplate, ref);
}

useCylinder(fn: GetByIndex<CylinderProps>, useOnTemplate = true, ref?: Ref<THREE.Object3D>) {
return this.useBody('Cylinder', fn, (args = [] as []) => args, useOnTemplate, ref);
useCylinder<TObject extends THREE.Object3D>(fn: GetByIndex<CylinderProps>, useOnTemplate = true, ref?: Ref<TObject>) {
return this.useBody<CylinderProps, TObject>('Cylinder', fn, (args = [] as []) => args, useOnTemplate, ref);
}

useHeightfield(fn: GetByIndex<HeightfieldProps>, useOnTemplate = true, ref?: Ref<THREE.Object3D>) {
return this.useBody('Heightfield', fn, (args) => args, useOnTemplate, ref);
useHeightfield<TObject extends THREE.Object3D>(
fn: GetByIndex<HeightfieldProps>,
useOnTemplate = true,
ref?: Ref<TObject>
) {
return this.useBody<HeightfieldProps, TObject>('Heightfield', fn, (args) => args, useOnTemplate, ref);
}

useParticle(fn: GetByIndex<ParticleProps>, useOnTemplate = true, ref?: Ref<THREE.Object3D>) {
return this.useBody('Particle', fn, () => [], useOnTemplate, ref);
useParticle<TObject extends THREE.Object3D>(fn: GetByIndex<ParticleProps>, useOnTemplate = true, ref?: Ref<TObject>) {
return this.useBody<ParticleProps, TObject>('Particle', fn, () => [], useOnTemplate, ref);
}

useSphere(fn: GetByIndex<SphereProps>, useOnTemplate = true, ref?: Ref<THREE.Object3D>) {
return this.useBody(
useSphere<TObject extends THREE.Object3D>(fn: GetByIndex<SphereProps>, useOnTemplate = true, ref?: Ref<TObject>) {
return this.useBody<SphereProps, TObject>(
'Sphere',
fn,
(args: SphereArgs = [1]): SphereArgs => {
Expand All @@ -194,12 +201,16 @@ export class NgtPhysicBody extends NgtComponentStore {
);
}

useTrimesh(fn: GetByIndex<TrimeshProps>, useOnTemplate = true, ref?: Ref<THREE.Object3D>) {
return this.useBody<TrimeshProps>('Trimesh', fn, (args) => args, useOnTemplate, ref);
useTrimesh<TObject extends THREE.Object3D>(fn: GetByIndex<TrimeshProps>, useOnTemplate = true, ref?: Ref<TObject>) {
return this.useBody<TrimeshProps, TObject>('Trimesh', fn, (args) => args, useOnTemplate, ref);
}

useConvexPolyhedron(fn: GetByIndex<ConvexPolyhedronProps>, useOnTemplate = true, ref?: Ref<THREE.Object3D>) {
return this.useBody<ConvexPolyhedronProps>(
useConvexPolyhedron<TObject extends THREE.Object3D>(
fn: GetByIndex<ConvexPolyhedronProps>,
useOnTemplate = true,
ref?: Ref<TObject>
) {
return this.useBody<ConvexPolyhedronProps, TObject>(
'ConvexPolyhedron',
fn,
([vertices, faces, normals, axes, boundingSphereRadius] = []): ConvexPolyhedronArgs<NgtTriple> => [
Expand All @@ -214,26 +225,30 @@ export class NgtPhysicBody extends NgtComponentStore {
);
}

useCompoundBody(fn: GetByIndex<CompoundBodyProps>, useOnTemplate = true, ref?: Ref<THREE.Object3D>) {
return this.useBody('Compound', fn, (args) => args as unknown[], useOnTemplate, ref);
useCompoundBody<TObject extends THREE.Object3D>(
fn: GetByIndex<CompoundBodyProps>,
useOnTemplate = true,
ref?: Ref<TObject>
) {
return this.useBody<CompoundBodyProps, TObject>('Compound', fn, (args) => args as unknown[], useOnTemplate, ref);
}

private useBody<TBodyProps extends BodyProps>(
private useBody<TBodyProps extends BodyProps, TObject extends THREE.Object3D>(
type: BodyShapeType,
getPropsFn: GetByIndex<TBodyProps>,
argsFn: ArgFn<TBodyProps['args']>,
useOnTemplate = true,
instanceRef?: Ref<THREE.Object3D>
): NgtPhysicBodyReturn {
instanceRef?: Ref<TObject>
): NgtPhysicBodyReturn<TObject> {
return this.zone.runOutsideAngular(() => {
let ref = instanceRef as Ref<THREE.Object3D>;
let ref = instanceRef as Ref<TObject>;

if (!ref) {
ref = new Ref<THREE.Object3D>();
ref = new Ref<TObject>();
}

if (!ref.value && !useOnTemplate) {
ref.set(prepareInstance(new THREE.Object3D(), () => this.store.get()));
ref.set(prepareInstance(new THREE.Object3D() as TObject, () => this.store.get()));
}

const physicsStore = this.physicsStore;
Expand Down Expand Up @@ -306,9 +321,7 @@ export class NgtPhysicBody extends NgtComponentStore {
worker.removeBodies({ uuid });
};
})
)(
combineLatest([physicsStore.select((s) => s.worker), ref.pipe(filter((obj): obj is THREE.Object3D => !!obj))])
);
)(combineLatest([physicsStore.select((s) => s.worker), ref.pipe(filter((obj): obj is TObject => !!obj))]));
});

return {
Expand Down
60 changes: 42 additions & 18 deletions libs/cannon/src/lib/constraint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,13 @@ type NgtConstraintORHingeApi<T extends 'Hinge' | ConstraintTypes> = T extends Co
? ConstraintApi
: HingeConstraintApi;

export interface NgtPhysicConstraintReturn<T extends 'Hinge' | ConstraintTypes> {
bodyA: Ref<THREE.Object3D>;
bodyB: Ref<THREE.Object3D>;
export interface NgtPhysicConstraintReturn<
T extends 'Hinge' | ConstraintTypes,
TObjectA extends THREE.Object3D = THREE.Object3D,
TObjectB extends THREE.Object3D = THREE.Object3D
> {
bodyA: Ref<TObjectA>;
bodyB: Ref<TObjectB>;
api: NgtConstraintORHingeApi<T>;
}

Expand All @@ -48,38 +52,58 @@ export class NgtPhysicConstraint extends NgtComponentStore {
super();
}

usePointToPointConstraint(bodyA: Ref<THREE.Object3D>, bodyB: Ref<THREE.Object3D>, optns: PointToPointConstraintOpts) {
return this.useConstraint('PointToPoint', bodyA, bodyB, optns);
usePointToPointConstraint<
TObjectA extends THREE.Object3D = THREE.Object3D,
TObjectB extends THREE.Object3D = THREE.Object3D
>(bodyA: Ref<TObjectA>, bodyB: Ref<TObjectB>, optns: PointToPointConstraintOpts) {
return this.useConstraint<'PointToPoint', TObjectA, TObjectB>('PointToPoint', bodyA, bodyB, optns);
}

useConeTwistConstraint(bodyA: Ref<THREE.Object3D>, bodyB: Ref<THREE.Object3D>, optns: ConeTwistConstraintOpts) {
return this.useConstraint('ConeTwist', bodyA, bodyB, optns);
useConeTwistConstraint<
TObjectA extends THREE.Object3D = THREE.Object3D,
TObjectB extends THREE.Object3D = THREE.Object3D
>(bodyA: Ref<TObjectA>, bodyB: Ref<TObjectB>, optns: ConeTwistConstraintOpts) {
return this.useConstraint<'ConeTwist', TObjectA, TObjectB>('ConeTwist', bodyA, bodyB, optns);
}

useDistanceConstraint(bodyA: Ref<THREE.Object3D>, bodyB: Ref<THREE.Object3D>, optns: DistanceConstraintOpts) {
return this.useConstraint('Distance', bodyA, bodyB, optns);
useDistanceConstraint<
TObjectA extends THREE.Object3D = THREE.Object3D,
TObjectB extends THREE.Object3D = THREE.Object3D
>(bodyA: Ref<TObjectA>, bodyB: Ref<TObjectB>, optns: DistanceConstraintOpts) {
return this.useConstraint<'Distance', TObjectA, TObjectB>('Distance', bodyA, bodyB, optns);
}

useHingeConstraint(bodyA: Ref<THREE.Object3D>, bodyB: Ref<THREE.Object3D>, optns: HingeConstraintOpts) {
return this.useConstraint('Hinge', bodyA, bodyB, optns);
useHingeConstraint<
TObjectA extends THREE.Object3D = THREE.Object3D,
TObjectB extends THREE.Object3D = THREE.Object3D
>(bodyA: Ref<TObjectA>, bodyB: Ref<TObjectB>, optns: HingeConstraintOpts) {
return this.useConstraint<'Hinge', TObjectA, TObjectB>('Hinge', bodyA, bodyB, optns);
}

useLockConstraint(bodyA: Ref<THREE.Object3D>, bodyB: Ref<THREE.Object3D>, optns: LockConstraintOpts) {
return this.useConstraint('Lock', bodyA, bodyB, optns);
useLockConstraint<TObjectA extends THREE.Object3D = THREE.Object3D, TObjectB extends THREE.Object3D = THREE.Object3D>(
bodyA: Ref<TObjectA>,
bodyB: Ref<TObjectB>,
optns: LockConstraintOpts
) {
return this.useConstraint<'Lock', TObjectA, TObjectB>('Lock', bodyA, bodyB, optns);
}

private useConstraint<TConstraintType extends 'Hinge' | ConstraintTypes>(
private useConstraint<
TConstraintType extends 'Hinge' | ConstraintTypes,
TObjectA extends THREE.Object3D = THREE.Object3D,
TObjectB extends THREE.Object3D = THREE.Object3D
>(
type: TConstraintType,
bodyA: Ref<THREE.Object3D>,
bodyB: Ref<THREE.Object3D>,
bodyA: Ref<TObjectA>,
bodyB: Ref<TObjectB>,
opts: ConstraintOptns | HingeConstraintOpts = {}
): NgtPhysicConstraintReturn<TConstraintType> {
): NgtPhysicConstraintReturn<TConstraintType, TObjectA, TObjectB> {
return this.zone.runOutsideAngular(() => {
const physicsStore = this.physicsStore;
const uuid = makeId();

this.onCanvasReady(this.store.ready$, () => {
this.effect<[CannonWorkerAPI, THREE.Object3D, THREE.Object3D]>(
this.effect<[CannonWorkerAPI, TObjectA, TObjectB]>(
tapEffect(([worker, a, b]) => {
worker.addConstraint({
props: [a.uuid, b.uuid, opts],
Expand Down
18 changes: 8 additions & 10 deletions libs/cannon/src/lib/raycast-vehicle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ export interface NgtPhysicRaycastVehiclePublicApi {
remove: () => void;
}

export interface NgtPhysicRaycastVehicleReturn {
ref: Ref<THREE.Object3D>;
export interface NgtPhysicRaycastVehicleReturn<TObject extends THREE.Object3D = THREE.Object3D> {
ref: Ref<TObject>;
api: NgtPhysicRaycastVehiclePublicApi;
}

Expand All @@ -39,20 +39,20 @@ export class NgtPhysicRaycastVehicle extends NgtComponentStore {
super();
}

useRaycastVehicle(
useRaycastVehicle<TObject extends THREE.Object3D = THREE.Object3D>(
fn: () => NgtPhysicRaycastVehicleProps,
useOnTemplate = true,
instanceRef?: Ref<THREE.Object3D>
): NgtPhysicRaycastVehicleReturn {
instanceRef?: Ref<TObject>
): NgtPhysicRaycastVehicleReturn<TObject> {
return this.zone.runOutsideAngular(() => {
let ref = instanceRef as Ref<THREE.Object3D>;
let ref = instanceRef as Ref<TObject>;

if (!ref) {
ref = new Ref();
}

if (!ref.value && !useOnTemplate) {
ref.set(prepare(new THREE.Object3D(), () => this.store.get()));
ref.set(prepare(new THREE.Object3D() as TObject, () => this.store.get()));
}

const physicsStore = this.physicsStore;
Expand Down Expand Up @@ -80,9 +80,7 @@ export class NgtPhysicRaycastVehicle extends NgtComponentStore {
worker.removeRaycastVehicle({ uuid });
};
})
)(
combineLatest([physicsStore.select((s) => s.worker), ref.pipe(filter((obj): obj is THREE.Object3D => !!obj))])
);
)(combineLatest([physicsStore.select((s) => s.worker), ref.pipe(filter((obj): obj is TObject => !!obj))]));
});

return {
Expand Down
15 changes: 11 additions & 4 deletions libs/cannon/src/lib/spring.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,12 @@ export interface NgtPhysicSpringApi {
remove: () => void;
}

export interface NgtPhysicSpringReturn {
bodyA: Ref<THREE.Object3D>;
bodyB: Ref<THREE.Object3D>;
export interface NgtPhysicSpringReturn<
TObjectA extends THREE.Object3D = THREE.Object3D,
TObjectB extends THREE.Object3D = THREE.Object3D
> {
bodyA: Ref<TObjectA>;
bodyB: Ref<TObjectB>;
api: NgtPhysicSpringApi;
}

Expand All @@ -28,7 +31,11 @@ export class NgtPhysicSpring extends NgtComponentStore {
super();
}

useSpring(bodyA: Ref<THREE.Object3D>, bodyB: Ref<THREE.Object3D>, optns: SpringOptns): NgtPhysicSpringReturn {
useSpring<TObjectA extends THREE.Object3D = THREE.Object3D, TObjectB extends THREE.Object3D = THREE.Object3D>(
bodyA: Ref<TObjectA>,
bodyB: Ref<TObjectB>,
optns: SpringOptns
): NgtPhysicSpringReturn<TObjectA, TObjectB> {
return this.zone.runOutsideAngular(() => {
const physicsStore = this.physicsStore;
const uuid = makeId();
Expand Down
6 changes: 3 additions & 3 deletions libs/cannon/src/lib/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,14 @@ import * as THREE from 'three';
export class NgtCannonUtils {
static incrementingId = 0;

static getUUID(ref: Ref<THREE.Object3D>, index?: number): string | null {
static getUUID<TObject extends THREE.Object3D = THREE.Object3D>(ref: Ref<TObject>, index?: number): string | null {
const suffix = index === undefined ? '' : `/${index}`;
if (typeof ref === 'function') return null;
return ref && ref.value && `${ref.value.uuid}${suffix}`;
}

static subscribe<T extends SubscriptionName>(
ref: Ref<THREE.Object3D>,
static subscribe<T extends SubscriptionName, TObject extends THREE.Object3D = THREE.Object3D>(
ref: Ref<TObject>,
worker: CannonWorkerAPI,
subscriptions: Subscriptions,
type: T,
Expand Down

0 comments on commit 7e0d788

Please sign in to comment.