Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit fb8c2ff

Browse files
ctranctran
ctran
authored and
ctran
committed
monday morning also works :o
1 parent bc3387a commit fb8c2ff

File tree

21 files changed

+1778
-51
lines changed

21 files changed

+1778
-51
lines changed

apps/kitchen-sink-new/src/app/cannon/cannon.routes.ts

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -9,26 +9,26 @@ const routes: Routes = [
99
path: 'kinematic-cube',
1010
loadComponent: () => import('./kinematic-cube/kinematic-cube'),
1111
},
12-
// {
13-
// path: 'compound',
14-
// loadComponent: () => import('./compound/compound'),
15-
// },
16-
// {
17-
// path: 'chain',
18-
// loadComponent: () => import('./chain/chain'),
19-
// },
20-
// {
21-
// path: 'cube-heap',
22-
// loadComponent: () => import('./cube-heap/cube-heap'),
23-
// },
24-
// {
25-
// path: 'convexpolyhedron',
26-
// loadComponent: () => import('./convexpolyhedron/convexpolyhedron'),
27-
// },
28-
// {
29-
// path: 'monday-morning',
30-
// loadComponent: () => import('./monday-morning/monday-morning'),
31-
// },
12+
{
13+
path: 'compound',
14+
loadComponent: () => import('./compound/compound'),
15+
},
16+
{
17+
path: 'chain',
18+
loadComponent: () => import('./chain/chain'),
19+
},
20+
{
21+
path: 'cube-heap',
22+
loadComponent: () => import('./cube-heap/cube-heap'),
23+
},
24+
{
25+
path: 'convexpolyhedron',
26+
loadComponent: () => import('./convexpolyhedron/convexpolyhedron'),
27+
},
28+
{
29+
path: 'monday-morning',
30+
loadComponent: () => import('./monday-morning/monday-morning'),
31+
},
3232
{
3333
path: '',
3434
redirectTo: 'basic',
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { ChangeDetectionStrategy, Component } from '@angular/core';
2+
import { NgtCanvas } from 'angular-three-core-new';
3+
import { Experience } from './experience';
4+
5+
@Component({
6+
standalone: true,
7+
template: `
8+
<ngt-canvas [sceneGraph]="scene" [options]="{ shadows: true, camera: { fov: 50, position: [0, 5, 20] } }" />
9+
<div class="absolute bottom-4 right-4 font-mono text-white">* Click to reset</div>
10+
`,
11+
imports: [NgtCanvas],
12+
changeDetection: ChangeDetectionStrategy.OnPush,
13+
host: { class: 'chain-cannon' },
14+
})
15+
export default class Chain {
16+
scene = Experience;
17+
}
Lines changed: 248 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,248 @@
1+
import {
2+
CUSTOM_ELEMENTS_SCHEMA,
3+
ChangeDetectionStrategy,
4+
Component,
5+
ElementRef,
6+
InjectionToken,
7+
Injector,
8+
Signal,
9+
afterNextRender,
10+
computed,
11+
inject,
12+
input,
13+
signal,
14+
viewChild,
15+
} from '@angular/core';
16+
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
17+
import { CylinderArgs, Triplet } from '@pmndrs/cannon-worker-api';
18+
import { NgtcPhysics } from 'angular-three-cannon';
19+
import { injectBox, injectCylinder, injectSphere } from 'angular-three-cannon/body';
20+
import { injectConeTwist } from 'angular-three-cannon/constraint';
21+
import { NgtcDebug } from 'angular-three-cannon/debug';
22+
import { NgtArgs, extend, injectBeforeRender, injectStore } from 'angular-three-core-new';
23+
import * as THREE from 'three';
24+
import { Color, ColorRepresentation, Mesh, Object3D } from 'three';
25+
26+
extend(THREE);
27+
28+
const Parent = new InjectionToken<{ position: Signal<Triplet>; ref: Signal<ElementRef<Object3D>> }>('PARENT');
29+
30+
@Component({
31+
selector: 'app-chain-link',
32+
standalone: true,
33+
template: `
34+
<ngt-mesh #mesh>
35+
<ngt-cylinder-geometry *args="args()" />
36+
<ngt-mesh-standard-material [roughness]="0.3" [color]="color()" />
37+
</ngt-mesh>
38+
<ng-content />
39+
`,
40+
imports: [NgtArgs],
41+
schemas: [CUSTOM_ELEMENTS_SCHEMA],
42+
changeDetection: ChangeDetectionStrategy.OnPush,
43+
providers: [
44+
{
45+
provide: Parent,
46+
useFactory: (chainLink: ChainLink) => ({ ref: chainLink.mesh, position: chainLink.position }),
47+
deps: [ChainLink],
48+
},
49+
],
50+
})
51+
export class ChainLink {
52+
parent = inject(Parent, { skipSelf: true });
53+
54+
maxMultiplier = input<number>();
55+
color = input<ColorRepresentation>('#575757');
56+
args = input<CylinderArgs>([0.5, 0.5, 2, 16]);
57+
58+
height = computed(() => this.args()[2] ?? 2);
59+
position = computed<Triplet>(() => {
60+
const [[x, y, z], height] = [this.parent.position(), this.height()];
61+
return [x, y - height, z];
62+
});
63+
64+
mesh = viewChild.required<ElementRef<Mesh>>('mesh');
65+
66+
cylinder = injectCylinder(
67+
() => ({ mass: 1, args: this.args(), linearDamping: 0.8, position: this.position() }),
68+
this.mesh,
69+
);
70+
71+
constructor() {
72+
const injector = inject(Injector);
73+
// NOTE: we want to run this in afterNextRender because we want the input to resolve
74+
afterNextRender(() => {
75+
injectConeTwist(this.parent.ref, this.mesh, {
76+
injector,
77+
options: {
78+
angle: Math.PI / 8,
79+
axisA: [0, 1, 0],
80+
axisB: [0, 1, 0],
81+
maxMultiplier: this.maxMultiplier(),
82+
pivotA: [0, -this.height() / 2, 0],
83+
pivotB: [0, this.height() / 2, 0],
84+
twistAngle: 0,
85+
},
86+
});
87+
});
88+
}
89+
}
90+
91+
function notUndefined<T>(value: T | undefined): value is T {
92+
return value !== undefined;
93+
}
94+
95+
const maxMultiplierExamples = [0, 500, 1000, 1500, undefined] as const;
96+
97+
@Component({
98+
selector: 'app-chain',
99+
standalone: true,
100+
template: `
101+
@if (length() > 0) {
102+
<app-chain-link [color]="color()" [maxMultiplier]="maxMultiplier()">
103+
<app-chain [length]="length() - 1" [maxMultiplier]="maxMultiplier()" />
104+
</app-chain-link>
105+
}
106+
`,
107+
imports: [ChainLink],
108+
schemas: [CUSTOM_ELEMENTS_SCHEMA],
109+
})
110+
export class Chain {
111+
length = input.required<number>();
112+
maxMultiplier = input<number>();
113+
114+
color = computed(() => {
115+
const maxMultiplier = this.maxMultiplier();
116+
if (maxMultiplier === undefined) return '#575757';
117+
118+
const maxExample = Math.max(...maxMultiplierExamples.filter(notUndefined));
119+
const red = Math.floor(Math.min(100, (maxMultiplier / maxExample) * 100));
120+
121+
return new Color(`rgb(${red}%, 0%, ${100 - red}%)`);
122+
});
123+
}
124+
125+
@Component({
126+
selector: 'app-pointer-handle',
127+
standalone: true,
128+
template: `
129+
<ngt-group>
130+
<ngt-mesh #mesh>
131+
<ngt-box-geometry *args="args()" />
132+
<ngt-mesh-standard-material [roughness]="0.3" color="#575757" />
133+
</ngt-mesh>
134+
<ng-content />
135+
</ngt-group>
136+
`,
137+
imports: [NgtArgs],
138+
schemas: [CUSTOM_ELEMENTS_SCHEMA],
139+
changeDetection: ChangeDetectionStrategy.OnPush,
140+
providers: [
141+
{
142+
provide: Parent,
143+
useFactory: (handle: PointerHandle) => ({ ref: handle.mesh, position: () => handle.position }),
144+
deps: [PointerHandle],
145+
},
146+
],
147+
})
148+
export class PointerHandle {
149+
size = input.required<number>();
150+
args = computed<Triplet>(() => [this.size(), this.size(), this.size() * 2]);
151+
152+
position: Triplet = [0, 0, 0];
153+
mesh = viewChild.required<ElementRef<Mesh>>('mesh');
154+
155+
boxApi = injectBox(() => ({ args: this.args(), position: this.position, type: 'Kinematic' }), this.mesh);
156+
157+
constructor() {
158+
injectBeforeRender(({ pointer: { x, y }, viewport: { width, height } }) => {
159+
this.boxApi()?.position.set((x * width) / 2, (y * height) / 2, 0);
160+
});
161+
}
162+
}
163+
164+
@Component({
165+
selector: 'app-static-handle',
166+
standalone: true,
167+
template: `
168+
<ngt-group>
169+
<ngt-mesh #mesh>
170+
<ngt-sphere-geometry *args="[radius(), 64, 64]" />
171+
<ngt-mesh-standard-material [roughness]="0.3" color="#575757" />
172+
</ngt-mesh>
173+
<ng-content />
174+
</ngt-group>
175+
`,
176+
imports: [NgtArgs],
177+
providers: [
178+
{
179+
provide: Parent,
180+
useFactory: (handle: StaticHandle) => ({ ref: handle.mesh, position: handle.position }),
181+
deps: [StaticHandle],
182+
},
183+
],
184+
schemas: [CUSTOM_ELEMENTS_SCHEMA],
185+
changeDetection: ChangeDetectionStrategy.OnPush,
186+
})
187+
export class StaticHandle {
188+
position = input.required<Triplet>();
189+
radius = input.required<number>();
190+
mesh = viewChild.required<ElementRef<Mesh>>('mesh');
191+
192+
sphere = injectSphere(() => ({ args: [this.radius()], position: this.position(), type: 'Static' }), this.mesh);
193+
}
194+
195+
@Component({
196+
standalone: true,
197+
template: `
198+
<ngt-color *args="['#171720']" attach="background" />
199+
<ngt-ambient-light [intensity]="0.5 * Math.PI" />
200+
<ngt-point-light [position]="[-10, -10, -10]" [intensity]="Math.PI" [decay]="0" />
201+
<ngt-spot-light
202+
[position]="[10, 10, 10]"
203+
[angle]="0.8"
204+
[penumbra]="1"
205+
[intensity]="Math.PI"
206+
[decay]="0"
207+
[castShadow]="true"
208+
/>
209+
210+
<ngtc-physics [options]="{ gravity: [0, -40, 0], allowSleep: false }">
211+
<app-pointer-handle [size]="1.5">
212+
<app-chain [length]="7" />
213+
</app-pointer-handle>
214+
@for (maxMultiplier of maxMultiplierExamples(); track maxMultiplier.key) {
215+
<app-static-handle [radius]="1.5" [position]="maxMultiplier.position">
216+
<app-chain [maxMultiplier]="maxMultiplier.value" [length]="8" />
217+
</app-static-handle>
218+
}
219+
</ngtc-physics>
220+
`,
221+
imports: [NgtcPhysics, NgtcDebug, NgtArgs, PointerHandle, Chain, StaticHandle],
222+
changeDetection: ChangeDetectionStrategy.OnPush,
223+
schemas: [CUSTOM_ELEMENTS_SCHEMA],
224+
host: { class: 'chain-experience' },
225+
})
226+
export class Experience {
227+
Math = Math;
228+
229+
private store = injectStore();
230+
231+
resetCount = signal(0);
232+
maxMultiplierExamples = computed(() =>
233+
maxMultiplierExamples.map((value, index, array) => ({
234+
value,
235+
key: `${value}-${this.resetCount()}`,
236+
position: [(array.length * -4) / 2 + index * 4, 8, 0] as Triplet,
237+
})),
238+
);
239+
240+
constructor() {
241+
this.store
242+
.get('pointerMissed$')
243+
.pipe(takeUntilDestroyed())
244+
.subscribe(() => {
245+
this.resetCount.update((prev) => prev + 1);
246+
});
247+
}
248+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { ChangeDetectionStrategy, Component } from '@angular/core';
2+
import { NgtCanvas } from 'angular-three-core-new';
3+
import { Experience } from './experience';
4+
5+
@Component({
6+
standalone: true,
7+
template: `
8+
<ngt-canvas [sceneGraph]="scene" [options]="{ shadows: true, camera: { fov: 50, position: [-2, 1, 7] } }" />
9+
`,
10+
imports: [NgtCanvas],
11+
changeDetection: ChangeDetectionStrategy.OnPush,
12+
host: { class: 'compound-cannon' },
13+
})
14+
export default class Compound {
15+
scene = Experience;
16+
}

0 commit comments

Comments
 (0)