Skip to content

Commit ad67270

Browse files
committed
bb anchor
1 parent 100ef3c commit ad67270

File tree

5 files changed

+185
-0
lines changed

5 files changed

+185
-0
lines changed

libs/core/src/lib/renderer/index.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,16 @@ class NgtRenderer implements Renderer2 {
310310
const pRS = parent.__ngt_renderer__;
311311
const cRS = oldChild.__ngt_renderer__;
312312

313+
if (
314+
(!cRS || !pRS) &&
315+
parent instanceof Element &&
316+
(oldChild instanceof Element || oldChild instanceof Text || oldChild instanceof Comment)
317+
) {
318+
this.delegate.removeChild(parent, oldChild);
319+
this.store.destroy(oldChild, parent);
320+
return;
321+
}
322+
313323
if (cRS[NgtRendererClassId.type] === 'dom' && (!pRS || pRS[NgtRendererClassId.type] === 'dom')) {
314324
this.delegate.removeChild(parent, oldChild);
315325
this.store.destroy(oldChild, parent);
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
import { NgIf } from '@angular/common';
2+
import { CUSTOM_ELEMENTS_SCHEMA, Component, Input } from '@angular/core';
3+
import { NgtArgs } from 'angular-three';
4+
import { NgtsOrbitControls } from 'angular-three-soba/controls';
5+
import { NgtsHtml } from 'angular-three-soba/misc';
6+
import { NgtsBBAnchor, NgtsBBAnchorState } from 'angular-three-soba/staging';
7+
import { NgtsSobaContent } from 'angular-three-soba/utils';
8+
import { makeCanvasOptions, makeDecorators, makeStoryObject, number } from '../setup-canvas';
9+
10+
@Component({
11+
selector: 'bb-anchor-scene',
12+
standalone: true,
13+
template: `
14+
<ngts-orbit-controls [autoRotate]="true" />
15+
<ngt-mesh #mesh>
16+
<ngt-icosahedron-geometry />
17+
<ngt-mesh-basic-material color="hotpink" [wireframe]="true" />
18+
<ngts-bb-anchor [anchor]="anchor">
19+
<ng-content />
20+
</ngts-bb-anchor>
21+
</ngt-mesh>
22+
<ng-container *ngIf="drawBoundingBox">
23+
<ngt-box-helper *args="[mesh, 'cyan']" />
24+
</ng-container>
25+
`,
26+
imports: [NgtsOrbitControls, NgtsBBAnchor, NgIf, NgtArgs],
27+
schemas: [CUSTOM_ELEMENTS_SCHEMA],
28+
})
29+
class BBAnchorScene {
30+
@Input() anchor: NgtsBBAnchorState['anchor'] = [1, 1, 1];
31+
@Input() drawBoundingBox = true;
32+
}
33+
34+
@Component({
35+
standalone: true,
36+
template: `
37+
<bb-anchor-scene [anchor]="[anchorX, anchorY, anchorZ]" [drawBoundingBox]="drawBoundingBox">
38+
<ngt-mesh [position]="-0.1">
39+
<ngt-sphere-geometry *args="[0.25]" />
40+
<ngt-mesh-basic-material color="lime" />
41+
</ngt-mesh>
42+
</bb-anchor-scene>
43+
`,
44+
imports: [NgtArgs, BBAnchorScene],
45+
schemas: [CUSTOM_ELEMENTS_SCHEMA],
46+
})
47+
class MeshBBAnchorStory {
48+
@Input() anchorX = 1;
49+
@Input() anchorY = 1;
50+
@Input() anchorZ = 1;
51+
@Input() drawBoundingBox = true;
52+
}
53+
54+
@Component({
55+
standalone: true,
56+
template: `
57+
<bb-anchor-scene [anchor]="[anchorX, anchorY, anchorZ]" [drawBoundingBox]="drawBoundingBox">
58+
<ngts-html [center]="true" [style]="{ color: 'white', whiteSpace: 'nowrap' }">
59+
<ng-container *ngtsSobaContent>Html element</ng-container>
60+
</ngts-html>
61+
</bb-anchor-scene>
62+
`,
63+
imports: [NgtsHtml, NgtsSobaContent, BBAnchorScene],
64+
schemas: [CUSTOM_ELEMENTS_SCHEMA],
65+
})
66+
class HtmlBBAnchorStory {
67+
@Input() anchorX = 1;
68+
@Input() anchorY = 1;
69+
@Input() anchorZ = 1;
70+
@Input() drawBoundingBox = true;
71+
}
72+
73+
export default {
74+
title: 'Staging/BBAnchor',
75+
decorators: makeDecorators(),
76+
};
77+
78+
const canvasOptions = makeCanvasOptions({
79+
camera: { position: [2, 2, 2] },
80+
controls: false,
81+
});
82+
83+
const argsOptions = {
84+
drawBoundingBox: true,
85+
anchorX: number(1, { range: true, min: -1, max: 1, step: 0.1 }),
86+
anchorY: number(1, { range: true, min: -1, max: 1, step: 0.1 }),
87+
anchorZ: number(1, { range: true, min: -1, max: 1, step: 0.1 }),
88+
};
89+
90+
export const WithHTML = makeStoryObject(HtmlBBAnchorStory, { canvasOptions, argsOptions });
91+
export const WithMesh = makeStoryObject(MeshBBAnchorStory, { canvasOptions, argsOptions });
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import { CUSTOM_ELEMENTS_SCHEMA, Component, Input, computed, effect } from '@angular/core';
2+
import { extend, injectBeforeRender, injectNgtRef, signalStore, type NgtGroup } from 'angular-three';
3+
import * as THREE from 'three';
4+
import { Group } from 'three';
5+
6+
const boundingBox = new THREE.Box3();
7+
const boundingBoxSize = new THREE.Vector3();
8+
9+
export type NgtsBBAnchorState = {
10+
anchor: THREE.Vector3 | [number, number, number];
11+
};
12+
13+
declare global {
14+
interface HTMLElementTagNameMap {
15+
/**
16+
* @extends ngt-group
17+
*/
18+
'ngts-bb-anchor': NgtsBBAnchorState & NgtGroup;
19+
}
20+
}
21+
22+
extend({ Group });
23+
24+
@Component({
25+
selector: 'ngts-bb-anchor',
26+
standalone: true,
27+
template: `
28+
<ngt-group ngtCompound [ref]="groupRef">
29+
<ng-content />
30+
</ngt-group>
31+
`,
32+
schemas: [CUSTOM_ELEMENTS_SCHEMA],
33+
})
34+
export class NgtsBBAnchor {
35+
private inputs = signalStore<NgtsBBAnchorState>();
36+
37+
@Input({ required: true, alias: 'anchor' }) set _anchor(anchor: NgtsBBAnchorState['anchor']) {
38+
this.inputs.set({ anchor });
39+
}
40+
41+
groupRef = injectNgtRef<Group>();
42+
private parentRef = injectNgtRef<THREE.Object3D | null>();
43+
private anchor = this.inputs.select('anchor');
44+
private xyz = computed(() => {
45+
const anchor = this.anchor();
46+
return Array.isArray(anchor) ? anchor : [anchor.x, anchor.y, anchor.z];
47+
});
48+
49+
constructor() {
50+
this.reattachParent();
51+
this.beforeRender();
52+
}
53+
54+
private reattachParent() {
55+
effect(() => {
56+
const group = this.groupRef.nativeElement;
57+
if (!group) return;
58+
console.log(group);
59+
if (group.parent?.parent) {
60+
this.parentRef.nativeElement = group.parent.parent;
61+
group.parent.parent.add(group);
62+
}
63+
});
64+
}
65+
66+
private beforeRender() {
67+
injectBeforeRender(() => {
68+
const [parent, group, anchor] = [this.parentRef.nativeElement, this.groupRef.nativeElement, this.xyz()];
69+
70+
if (parent) {
71+
boundingBox.setFromObject(parent);
72+
boundingBox.getSize(boundingBoxSize);
73+
74+
group.position.set(
75+
parent.position.x + (boundingBoxSize.x * anchor[0]) / 2,
76+
parent.position.y + (boundingBoxSize.y * anchor[1]) / 2,
77+
parent.position.z + (boundingBoxSize.z * anchor[2]) / 2,
78+
);
79+
}
80+
});
81+
}
82+
}

libs/soba/staging/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
export * from './accumulative-shadows/accumulative-shadows';
22
export * from './accumulative-shadows/randomized-lights';
33
export * from './backdrop/backdrop';
4+
export * from './bb-anchor/bb-anchor';
45
export * from './bounds/bounds';
56
export * from './camera-shake/camera-shake';
67
export * from './center/center';

tools/scripts/generate-soba-json.mjs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ const entryPoints = {
4848
'accumulative-shadows/randomized-lights',
4949
'stage',
5050
'backdrop',
51+
'bb-anchor',
5152
],
5253
};
5354

0 commit comments

Comments
 (0)