@c8y/ngx-components
Version:
Angular modules for Cumulocity IoT applications
265 lines (259 loc) • 17.9 kB
JavaScript
import * as i0 from '@angular/core';
import { Input, ViewChild, Component, Optional } from '@angular/core';
import { of, Subject, from, combineLatest, BehaviorSubject } from 'rxjs';
import { shareReplay, filter, switchMap, map, distinctUntilChanged, startWith } from 'rxjs/operators';
import { loadThree } from '@c8y/ngx-components/lazy/three';
import { loadOrbitControls } from '@c8y/ngx-components/lazy/three-orbit-controls';
import * as i2 from '@c8y/ngx-components';
import { MeasurementRealtimeService, CoreModule } from '@c8y/ngx-components';
import { loadBoxModel } from '@c8y/ngx-components/widgets/implementations/three-d-rotation/lazy-box-model';
import { loadPhoneModel } from '@c8y/ngx-components/widgets/implementations/three-d-rotation/lazy-phone-model';
import * as i2$1 from '@c8y/ngx-components/context-dashboard';
import * as i1 from '@angular/forms';
import { Validators, NgForm, ControlContainer } from '@angular/forms';
import { ButtonsModule } from 'ngx-bootstrap/buttons';
class ThreeDRotationComponent {
constructor() {
this.angles$ = of({ x: 0, y: 0, z: 0 });
this.cameraType$ = of('PC');
this.isWireframe$ = of(true);
this.afterViewInit$ = new Subject();
}
get canvas() {
return this.canvasRef?.nativeElement;
}
ngOnInit() {
const three$ = from(loadThree()).pipe(shareReplay(1));
const model$ = combineLatest([three$, this.modelObj$]).pipe(filter(([, modelObj]) => !!modelObj), switchMap(([three, modelObj]) => this.loadModel(modelObj, three)));
const modelWithWireframe$ = combineLatest([model$, this.isWireframe$]).pipe(map(([model, isWireframe]) => this.setWireframe(model, isWireframe)));
const rotatedModel$ = combineLatest([modelWithWireframe$, this.angles$]).pipe(filter(([, angles]) => !!angles), map(([model, angles]) => {
Object.assign(model.rotation, angles);
return model;
}));
const cameraType$ = this.cameraType$.pipe(filter(type => !!type), distinctUntilChanged());
let previousCameraType;
this.renderSubscription = combineLatest([
three$,
rotatedModel$,
cameraType$,
this.afterViewInit$
])
.pipe(filter(([, model]) => !!model))
.subscribe(async ([three, model, cameraType]) => {
if (model !== this.model || previousCameraType !== cameraType) {
this.model = model;
previousCameraType = cameraType;
this.createScene(three, model, cameraType);
}
if (!this.renderer) {
await this.setupRenderer(three);
}
this.render();
});
}
ngOnDestroy() {
this.renderSubscription?.unsubscribe();
this.controls?.dispose();
}
ngAfterViewInit() {
this.afterViewInit$.next();
}
async loadModel(modelObj, three) {
const loader = new three.ObjectLoader();
const parsedModel = await loader.parse(modelObj);
return parsedModel;
}
async setupRenderer(three) {
//* Renderer
// Use canvas element in template
this.renderer = new three.WebGLRenderer({ canvas: this.canvas });
this.renderer.setPixelRatio(devicePixelRatio);
this.renderer.setSize(this.canvas.clientWidth, this.canvas.clientHeight);
const { OrbitControls } = await loadOrbitControls();
this.controls = new OrbitControls(this.camera, this.renderer.domElement);
this.controls.enableDamping = true;
this.controls.dampingFactor = 0.25;
this.controls.rotateSpeed = 0.35;
this.controls.addEventListener('change', () => this.render());
}
setWireframe(parsedModel, isWireframe) {
parsedModel.children.forEach((child) => {
if (child.material) {
child.material.wireframe = isWireframe;
}
});
return parsedModel;
}
render() {
this.renderer?.render(this.scene, this.camera);
}
createScene(three, model, cameraType) {
//* Scene
this.scene = new three.Scene();
this.scene.background = new three.Color(0xffffff);
this.scene.add(model);
const light = new three.AmbientLight(0xffffff, 0.5);
const lightDirectional = new three.DirectionalLight(0xffffff);
const lightDirectional2 = new three.DirectionalLight(0xffffff);
lightDirectional.position.set(20, 25, 30);
lightDirectional2.position.set(-20, -25, -30);
this.scene.add(lightDirectional);
this.scene.add(lightDirectional2);
this.scene.add(light);
this.camera = this.createCamera(three, cameraType);
}
createCamera(three, cameraType) {
let camera;
switch (cameraType) {
case 'OC':
camera = new three.OrthographicCamera(30 / -2, 30 / 2, 30 / 2, 30 / -2, 1, 1000);
break;
case 'PC':
default:
camera = new three.PerspectiveCamera(30, this.getAspectRatio(), 0.1, 1000);
break;
}
camera.rotateX(Math.PI / 2);
camera.rotateY(Math.PI / 2);
camera.position.z = 23;
camera.position.x = 14;
camera.position.y = 7;
return camera;
}
getAspectRatio() {
return this.canvas.clientWidth / this.canvas.clientHeight;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ThreeDRotationComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: ThreeDRotationComponent, isStandalone: true, selector: "c8y-three-d-rotation", inputs: { angles$: "angles$", modelObj$: "modelObj$", cameraType$: "cameraType$", isWireframe$: "isWireframe$" }, viewQueries: [{ propertyName: "canvasRef", first: true, predicate: ["canvas"], descendants: true }], ngImport: i0, template: "<canvas #canvas class=\"fit-w fit-h\"></canvas>\n" }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ThreeDRotationComponent, decorators: [{
type: Component,
args: [{ selector: 'c8y-three-d-rotation', standalone: true, imports: [], template: "<canvas #canvas class=\"fit-w fit-h\"></canvas>\n" }]
}], propDecorators: { canvasRef: [{
type: ViewChild,
args: ['canvas']
}], angles$: [{
type: Input
}], modelObj$: [{
type: Input
}], cameraType$: [{
type: Input
}], isWireframe$: [{
type: Input
}] } });
class ThreeDRotationWidgetViewComponent {
constructor(measurementRealtime, dashboard) {
this.measurementRealtime = measurementRealtime;
this.dashboard = dashboard;
this.deviceId$ = new BehaviorSubject(null);
this.modelName$ = new BehaviorSubject(null);
this.cameraType$ = new BehaviorSubject('PC');
this.isWireframe$ = new BehaviorSubject(true);
this.modelObj$ = this.modelName$.pipe(filter(name => !!name), distinctUntilChanged(), switchMap(name => this.getModelUrl(name)), shareReplay(1));
this.angles$ = this.deviceId$.pipe(filter(id => !!id), distinctUntilChanged(), switchMap(id => this.getAnglesOfDevice$(id)), startWith({ x: 0, y: 0, z: 0 }));
}
ngOnChanges(changes) {
if (changes.config && this.config) {
this.onConfigChange();
}
}
onConfigChange() {
if (this.config.device?.id) {
this.deviceId$.next(`${this.config.device.id}`);
}
else if (this.dashboard?.context?.id) {
this.deviceId$.next(`${this.dashboard.context?.id}`);
}
if (this.config.objectModel) {
this.modelName$.next(this.config.objectModel);
}
if (this.config.cameraType) {
this.cameraType$.next(this.config.cameraType);
}
if (this.config.isWireframe !== undefined) {
this.isWireframe$.next(this.config.isWireframe);
}
}
async getModelUrl(model) {
// The name *.min.json still exist for backwards compatibility
// it might be stored in certain widget configs.
if (model === 'box.min.json') {
return await loadBoxModel();
}
else {
return await loadPhoneModel();
}
}
getAnglesOfDevice$(deviceId) {
const fragment = 'c8y_Acceleration';
const series = ['accelerationX', 'accelerationY', 'accelerationZ'];
return this.measurementRealtime
.latestValueOfSpecificMeasurement$(fragment, series[0], deviceId, 1)
.pipe(filter(m => !!m && m[fragment] && series.every(axisSeries => m[fragment][axisSeries])), map(measurement => {
const [xAxisValue, yAxisValue, zAxisValue] = series.map(axisSeries => Math.round(measurement[fragment][axisSeries].value));
return this.convertValues(xAxisValue, yAxisValue, zAxisValue);
}));
}
convertValues(x, y, z) {
let rotateX = Math.atan2(y, z);
let rotateY = Math.atan2(x, Math.sqrt(y * y + z * z));
rotateX = rotateX ? rotateX % (Math.PI * 2) : 0;
rotateY = rotateY ? rotateY % (Math.PI * 2) : 0;
return {
x: rotateX,
y: 0,
z: rotateY
};
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ThreeDRotationWidgetViewComponent, deps: [{ token: i2.MeasurementRealtimeService }, { token: i2$1.ContextDashboardComponent, optional: true }], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: ThreeDRotationWidgetViewComponent, isStandalone: true, selector: "c8y-three-d-rotation-widget-view", inputs: { config: "config" }, providers: [MeasurementRealtimeService], usesOnChanges: true, ngImport: i0, template: "<c8y-three-d-rotation\n [modelObj$]=\"modelObj$\"\n [angles$]=\"angles$\"\n [cameraType$]=\"cameraType$\"\n [isWireframe$]=\"isWireframe$\"\n></c8y-three-d-rotation>\n", dependencies: [{ kind: "component", type: ThreeDRotationComponent, selector: "c8y-three-d-rotation", inputs: ["angles$", "modelObj$", "cameraType$", "isWireframe$"] }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ThreeDRotationWidgetViewComponent, decorators: [{
type: Component,
args: [{ selector: 'c8y-three-d-rotation-widget-view', providers: [MeasurementRealtimeService], standalone: true, imports: [ThreeDRotationComponent], template: "<c8y-three-d-rotation\n [modelObj$]=\"modelObj$\"\n [angles$]=\"angles$\"\n [cameraType$]=\"cameraType$\"\n [isWireframe$]=\"isWireframe$\"\n></c8y-three-d-rotation>\n" }]
}], ctorParameters: () => [{ type: i2.MeasurementRealtimeService }, { type: i2$1.ContextDashboardComponent, decorators: [{
type: Optional
}] }], propDecorators: { config: [{
type: Input
}] } });
class ThreeDRotationWidgetConfigComponent {
constructor(formBuilder, form) {
this.formBuilder = formBuilder;
this.form = form;
}
onBeforeSave(config) {
if (this.formGroup.valid) {
Object.assign(config, this.formGroup.value);
return true;
}
return false;
}
ngOnInit() {
this.initForm();
}
initForm() {
this.formGroup = this.createForm(this.formBuilder);
this.form.form.addControl('config', this.formGroup);
this.formGroup.patchValue(this.config);
}
createForm(formBuilder) {
return formBuilder.group({
objectModel: ['box.min.json', [Validators.minLength(1)]],
isWireframe: [true, []],
cameraType: ['PC', [Validators.minLength(2), Validators.maxLength(2)]]
});
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ThreeDRotationWidgetConfigComponent, deps: [{ token: i1.FormBuilder }, { token: i1.NgForm }], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: ThreeDRotationWidgetConfigComponent, isStandalone: true, selector: "c8y-three-d-rotation-widget-config", inputs: { config: "config" }, ngImport: i0, template: "<fieldset class=\"c8y-fieldset\">\n <legend>{{ 'Rendering' | translate }}</legend>\n<form [formGroup]=\"formGroup\">\n <c8y-form-group class=\"form-group-sm m-b-8\">\n <label translate>Select object model for rendering</label>\n <div class=\"input-group input-group-sm\">\n <div class=\"c8y-select-wrapper\">\n <select class=\"form-control\" formControlName=\"objectModel\">\n <option value=\"box.min.json\" translate>Box model</option>\n <option value=\"phoneModel.min.json\" translate>Phone model</option>\n </select>\n </div>\n <span class=\"input-group-addon bg-level-0\">\n <label class=\"c8y-switch\">\n <input type=\"checkbox\" formControlName=\"isWireframe\" />\n <span></span>\n <span translate>Wireframe</span>\n </label>\n </span>\n </div>\n </c8y-form-group>\n\n <c8y-form-group class=\"form-group-sm\">\n <label translate>Camera type</label>\n <div class=\"c8y-select-wrapper\">\n <select class=\"form-control\" formControlName=\"cameraType\">\n <option value=\"OC\" translate>Orthographic camera</option>\n <option value=\"PC\" translate>Perspective camera</option>\n </select>\n </div>\n </c8y-form-group>\n</form>\n</fieldset>\n", dependencies: [{ kind: "ngmodule", type: CoreModule }, { kind: "pipe", type: i2.C8yTranslatePipe, name: "translate" }, { kind: "directive", type: i2.C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i1.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "component", type: i2.FormGroupComponent, selector: "c8y-form-group", inputs: ["hasError", "hasWarning", "hasSuccess", "novalidation", "status"] }, { kind: "directive", type: i2.RequiredInputPlaceholderDirective, selector: "input[required], input[formControlName]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: ButtonsModule }], viewProviders: [{ provide: ControlContainer, useExisting: NgForm }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ThreeDRotationWidgetConfigComponent, decorators: [{
type: Component,
args: [{ selector: 'c8y-three-d-rotation-widget-config', viewProviders: [{ provide: ControlContainer, useExisting: NgForm }], standalone: true, imports: [CoreModule, ButtonsModule], template: "<fieldset class=\"c8y-fieldset\">\n <legend>{{ 'Rendering' | translate }}</legend>\n<form [formGroup]=\"formGroup\">\n <c8y-form-group class=\"form-group-sm m-b-8\">\n <label translate>Select object model for rendering</label>\n <div class=\"input-group input-group-sm\">\n <div class=\"c8y-select-wrapper\">\n <select class=\"form-control\" formControlName=\"objectModel\">\n <option value=\"box.min.json\" translate>Box model</option>\n <option value=\"phoneModel.min.json\" translate>Phone model</option>\n </select>\n </div>\n <span class=\"input-group-addon bg-level-0\">\n <label class=\"c8y-switch\">\n <input type=\"checkbox\" formControlName=\"isWireframe\" />\n <span></span>\n <span translate>Wireframe</span>\n </label>\n </span>\n </div>\n </c8y-form-group>\n\n <c8y-form-group class=\"form-group-sm\">\n <label translate>Camera type</label>\n <div class=\"c8y-select-wrapper\">\n <select class=\"form-control\" formControlName=\"cameraType\">\n <option value=\"OC\" translate>Orthographic camera</option>\n <option value=\"PC\" translate>Perspective camera</option>\n </select>\n </div>\n </c8y-form-group>\n</form>\n</fieldset>\n" }]
}], ctorParameters: () => [{ type: i1.FormBuilder }, { type: i1.NgForm }], propDecorators: { config: [{
type: Input
}] } });
/**
* Generated bundle index. Do not edit.
*/
export { ThreeDRotationComponent, ThreeDRotationWidgetConfigComponent, ThreeDRotationWidgetViewComponent };
//# sourceMappingURL=c8y-ngx-components-widgets-implementations-three-d-rotation.mjs.map