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

Commit

Permalink
feat(soba): add SobaLineController for abstraction
Browse files Browse the repository at this point in the history
  • Loading branch information
nartc committed Nov 22, 2021
1 parent aa4d285 commit 2181e96
Show file tree
Hide file tree
Showing 3 changed files with 205 additions and 86 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import {
DestroyedService,
NGT_OBJECT_3D_CONTROLLER_PROVIDER,
} from '@angular-three/core';
import {
ChangeDetectorRef,
InjectionToken,
Optional,
Provider,
} from '@angular/core';
import { takeUntil } from 'rxjs';
import { NgtSobaLineController } from './line.controller';

export const NGT_SOBA_LINE_WATCHED_CONTROLLER = new InjectionToken(
'Watched Line Controller'
);

export const NGT_SOBA_LINE_CONTROLLER_PROVIDER: Provider[] = [
NGT_OBJECT_3D_CONTROLLER_PROVIDER,
DestroyedService,
{
provide: NGT_SOBA_LINE_WATCHED_CONTROLLER,
deps: [
[new Optional(), NgtSobaLineController],
ChangeDetectorRef,
DestroyedService,
],
useFactory: sobaLineWatchedControllerFactory,
},
];

export function sobaLineWatchedControllerFactory(
controller: NgtSobaLineController | null,
cdr: ChangeDetectorRef,
destroyed: DestroyedService
) {
if (!controller) return null;

controller.change$.pipe(takeUntil(destroyed)).subscribe(() => {
cdr.markForCheck();
});

return controller;
}
126 changes: 40 additions & 86 deletions packages/soba/abstractions/src/lib/line/line.component.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import {
NGT_OBJECT_3D_CONTROLLER_PROVIDER,
DestroyedService,
NGT_OBJECT_3D_WATCHED_CONTROLLER,
NgtAnimationReady,
NgtColor,
NgtCoreModule,
NgtObject3dController,
NgtVector3,
Expand All @@ -13,149 +11,105 @@ import { NgtLine2Module } from '@angular-three/core/meshes';
import {
ChangeDetectionStrategy,
Component,
EventEmitter,
Inject,
Input,
NgModule,
NgZone,
OnChanges,
Output,
OnInit,
SimpleChanges,
} from '@angular/core';
import { merge, ReplaySubject, takeUntil } from 'rxjs';
import * as THREE from 'three';
import { Line2 } from 'three/examples/jsm/lines/Line2';
import { LineGeometry } from 'three/examples/jsm/lines/LineGeometry';
import {
LineMaterial,
LineMaterialParameters,
} from 'three/examples/jsm/lines/LineMaterial';
NGT_SOBA_LINE_CONTROLLER_PROVIDER,
NGT_SOBA_LINE_WATCHED_CONTROLLER,
} from './line-watched-controller.di';
import { NgtSobaLineController } from './line.controller';

@Component({
selector: 'ngt-soba-line[points]',
exportAs: 'ngtSobaLine',
template: `
<ngt-line2
(ready)="onLineReady($event)"
(animateReady)="animateReady.emit($event)"
(ready)="sobaLineController.onLineReady($event)"
(animateReady)="sobaLineController.animateReady.emit($event)"
[controller]="object3dController"
>
<ngt-line-geometry (ready)="onGeometryReady($event)"></ngt-line-geometry>
<ngt-line-material
(ready)="onMaterialReady($event)"
[parameters]="parameters"
(ready)="sobaLineController.onMaterialReady($event)"
[parameters]="sobaLineController.parameters"
></ngt-line-material>
</ngt-line2>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
providers: [NGT_OBJECT_3D_CONTROLLER_PROVIDER],
providers: [NGT_SOBA_LINE_CONTROLLER_PROVIDER],
})
export class NgtSobaLine implements OnChanges {
export class NgtSobaLine implements OnChanges, OnInit {
@Input() points!: Array<NgtVector3>;
@Input() vertexColors?: Array<NgtColor>;

@Input() color: NgtColor = 'black';
@Input() lineWidth?: LineMaterialParameters['linewidth'];
@Input() dashed?: LineMaterialParameters['dashed'];

@Input() materialParameters?: Omit<
LineMaterialParameters,
'vertexColors' | 'color' | 'linewidth' | 'dashed' | 'resolution'
> = {};

@Output() ready = new EventEmitter<Line2>();
@Output() animateReady = new EventEmitter<NgtAnimationReady<Line2>>();

resolution = new THREE.Vector2(512, 512);
parameters!: LineMaterialParameters;
line!: Line2;
geometry!: LineGeometry;
material!: LineMaterial;

private pointsChange$ = new ReplaySubject<SimpleChanges>(1);

constructor(
@Inject(NGT_OBJECT_3D_WATCHED_CONTROLLER)
public object3dController: NgtObject3dController,
private ngZone: NgZone
@Inject(NGT_SOBA_LINE_WATCHED_CONTROLLER)
public sobaLineController: NgtSobaLineController,
private ngZone: NgZone,
private destroyed: DestroyedService
) {}

ngOnChanges(changes: SimpleChanges) {
this.ngZone.runOutsideAngular(() => {
this.parameters = {
color: this.color as number,
linewidth: this.lineWidth,
dashed: this.dashed,
vertexColors: Boolean(this.vertexColors),
resolution: this.resolution,
...this.materialParameters,
};

if (changes.points || changes.vertexColors) {
if (this.geometry) {
this.setupGeometry();
}
this.sobaLineController.mergeParameters({ resolution: this.resolution });

if (this.line) {
this.line.computeLineDistances();
}
}

if (changes.dashed) {
if (this.material) {
this.dasherize();
}
if (changes.points) {
this.pointsChange$.next(changes);
}
});
}

onGeometryReady(geometry: LineGeometry) {
this.ngZone.runOutsideAngular(() => {
this.geometry = geometry;
this.setupGeometry();
});
}

onLineReady(line: Line2) {
this.ngZone.runOutsideAngular(() => {
this.line = line;
this.line.computeLineDistances();
this.ngZone.run(() => {
this.ready.emit(line);
ngOnInit() {
merge(this.pointsChange$, this.sobaLineController.change$)
.pipe(takeUntil(this.destroyed))
.subscribe((changes) => {
if (changes.point || changes.vertexColors) {
if (this.sobaLineController.geometry) {
this.setupGeometry();
}
}
});
});
}

onMaterialReady(material: LineMaterial) {
onGeometryReady(geometry: LineGeometry) {
this.ngZone.runOutsideAngular(() => {
this.material = material;
this.dasherize();
this.sobaLineController.geometry = geometry;
this.setupGeometry();
});
}

private dasherize() {
if (this.dashed) {
this.material.defines.USE_DASH = '';
} else {
delete this.material.defines.USE_DASH;
}
}

private setupGeometry() {
const pointValues = this.points.map((p) =>
p instanceof THREE.Vector3 ? p.toArray() : p
);
this.geometry.setPositions((pointValues as any).flat());
this.sobaLineController.geometry.setPositions((pointValues as any).flat());

if (this.vertexColors) {
const colorValues = this.vertexColors.map((c) =>
if (this.sobaLineController.vertexColors) {
const colorValues = this.sobaLineController.vertexColors.map((c) =>
c instanceof THREE.Color ? c.toArray() : c
);
this.geometry.setColors((colorValues as any).flat());
this.sobaLineController.geometry.setColors((colorValues as any).flat());
}
}
}

@NgModule({
declarations: [NgtSobaLine],
exports: [NgtSobaLine],
declarations: [NgtSobaLine, NgtSobaLineController],
exports: [NgtSobaLine, NgtSobaLineController],
imports: [
NgtCoreModule,
NgtLine2Module,
Expand Down
121 changes: 121 additions & 0 deletions packages/soba/abstractions/src/lib/line/line.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import { Controller, NgtAnimationReady, NgtColor } from '@angular-three/core';
import {
Directive,
EventEmitter,
Input,
NgZone,
OnInit,
Output,
SimpleChanges,
} from '@angular/core';
import { Line2 } from 'three/examples/jsm/lines/Line2';
import { LineGeometry } from 'three/examples/jsm/lines/LineGeometry';
import {
LineMaterial,
LineMaterialParameters,
} from 'three/examples/jsm/lines/LineMaterial';

@Directive({
selector:
'ngt-soba-line,ngt-soba-quadratic-bezier-line,ngt-soba-cubic-bezier-line',
exportAs: 'ngtSobaLineController',
})
export class NgtSobaLineController extends Controller implements OnInit {
@Input() vertexColors?: Array<NgtColor>;

@Input() color: NgtColor = 'black';
@Input() lineWidth?: LineMaterialParameters['linewidth'];
@Input() dashed?: LineMaterialParameters['dashed'];

@Input() materialParameters?: Omit<
LineMaterialParameters,
'vertexColors' | 'color' | 'linewidth' | 'dashed' | 'resolution'
> = {};

@Input() sobaLineController?: NgtSobaLineController;

@Output() ready = new EventEmitter<Line2>();
@Output() animateReady = new EventEmitter<NgtAnimationReady<Line2>>();

parameters!: LineMaterialParameters;
line!: Line2;
geometry!: LineGeometry;
material!: LineMaterial;

constructor(private ngZone: NgZone) {
super();
}

ngOnChanges(changes: SimpleChanges) {
if (this.sobaLineController) {
this.sobaLineController.ngOnChanges(changes);
} else {
super.ngOnChanges(changes);
}
this.ngZone.runOutsideAngular(() => {
this.parameters = {
color: this.color as number,
linewidth: this.lineWidth,
dashed: this.dashed,
vertexColors: Boolean(this.vertexColors),
...this.materialParameters,
};

if (changes.vertexColors) {
if (this.line) {
this.line.computeLineDistances();
}
}

if (changes.dashed) {
if (this.material) {
this.dasherize();
}
}
});
}

ngOnInit() {
this.ngZone.runOutsideAngular(() => {
if (this.sobaLineController) {
this.vertexColors = this.sobaLineController.vertexColors;
this.color = this.sobaLineController.color;
this.lineWidth = this.sobaLineController.lineWidth;
this.dashed = this.sobaLineController.dashed;
this.materialParameters = this.sobaLineController.materialParameters;

this.ready = this.sobaLineController.ready;
this.animateReady = this.sobaLineController.animateReady;
}
});
}

mergeParameters(extra: Record<string, unknown> = {}) {
this.parameters = Object.assign(this.parameters || {}, extra);
}

onLineReady(line: Line2) {
this.ngZone.runOutsideAngular(() => {
this.line = line;
this.line.computeLineDistances();
this.ngZone.run(() => {
this.ready.emit(line);
});
});
}

onMaterialReady(material: LineMaterial) {
this.ngZone.runOutsideAngular(() => {
this.material = material;
this.dasherize();
});
}

private dasherize() {
if (this.dashed) {
this.material.defines.USE_DASH = '';
} else {
delete this.material.defines.USE_DASH;
}
}
}

0 comments on commit 2181e96

Please sign in to comment.