UNPKG

@c8y/ngx-components

Version:

Angular modules for Cumulocity IoT applications

265 lines (259 loc) 17.9 kB
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