Skip to content
Snippets Groups Projects
editAnnotation.component.ts 10.34 KiB
import {
  AfterViewInit,
  ChangeDetectorRef,
  Component,
  EventEmitter,
  Inject,
  Input,
  OnDestroy,
  OnInit,
  Optional,
  Output
} from "@angular/core";
import {FormBuilder, FormGroup, Validators} from "@angular/forms";
import {animate, style, transition, trigger} from "@angular/animations";
import {VIEWER_INJECTION_TOKEN} from "src/ui/layerbrowser/layerDetail/layerDetail.component";
import {Subscription} from "rxjs";
import {filter, map} from "rxjs/operators";
import {select, Store} from "@ngrx/store";
import {viewerStateSelectedTemplateSelector} from "src/services/state/viewerState/selectors";

@Component({
  selector: 'edit-annotation',
  templateUrl: './editAnnotation.template.html',
  styleUrls: ['./editAnnotation.style.css'],
  animations: [
    trigger(
      'enterAnimation', [
        transition(':enter', [
          style({transform: 'translateY(100%)', opacity: 0}),
          animate('100ms', style({transform: 'translateY(0)', opacity: 1}))
        ]),
        // transition(':leave', [
        //   style({transform: 'translateY(0)', opacity: 1}),
        //   animate('100ms', style({transform: 'translateY(100%)', opacity: 0}))
        // ])
      ]
    )
  ],
})
export class EditAnnotationComponent implements OnInit, OnDestroy {

    @Input() editingAnnotation: any
    @Input() showOnFocus = false
    @Input() cursorOut = false

    @Output() saveAnnotation: EventEmitter<any> = new EventEmitter()
    @Output() selectingOutput: EventEmitter<any> = new EventEmitter()
    @Output() editingMode: EventEmitter<any> = new EventEmitter()

    public selecting: string
    public position1Selected = false
    public position2Selected = false

    public showFull = false
    public annotationForm: FormGroup
    public loading = false
    public mousePos

    public selectedTemplate: string
    public ellipsoidMinRadius = 0.2

    // public voxelSize

    public subscriptions: Subscription[] = []

    public annotationTypes = [{name: 'Point', class: 'fas fa-circle'},
      {name: 'Line', class: 'fas fa-slash', twoCoordinates: true},
      {name: 'Bounding box', class: 'far fa-square', twoCoordinates: true},
      {name: 'Ellipsoid', class: 'fas fa-bullseye', twoCoordinates: true}]
    public selectedType: any

    get nehubaViewer() {
      return (window as any).nehubaViewer
    }
    get interactiveViewer() {
      return (window as any).interactiveViewer
    }
    private get viewer(){
      return this.injectedViewer || (window as any).viewer
    }

    constructor(private formBuilder: FormBuilder,
                private changeDetectionRef: ChangeDetectorRef,
                private store: Store<any>,
                @Optional() @Inject(VIEWER_INJECTION_TOKEN) private injectedViewer) {
      this.annotationForm = this.formBuilder.group({
        id: [{value: null, disabled: true}],
        position1: [{value: '', disabled: this.loading}],
        position2: [{value: '', disabled: this.loading}],
        name: [{value: '', disabled: this.loading}, {
          validators: [Validators.maxLength(200)]
        }],
        description: [{value: '', disabled: this.loading}, {
          validators: [Validators.maxLength(1000)]
        }],
        templateName: [{value: ''}],
        type: [{value: 'point'}],
        annotationVisible: [true]
      })
    }

    ngOnInit() {
      this.selectType(this.annotationTypes[0])

      this.subscriptions.push(
        this.nehubaViewer.mousePosition.inVoxels
          .subscribe(floatArr => {
            // this.mousePos = floatArr && Array.from(floatArr).map((val: number) => val / 1e6)
            this.mousePos = floatArr && floatArr

            if (this.selecting === 'position1' && this.mousePos) {
              this.annotationForm.controls.position1.setValue(this.mousePos.join())
            } else if (this.selecting === 'position2' && this.mousePos) {
              if (this.annotationForm.controls.type.value === 'ellipsoid') {
                this.annotationForm.controls.position2.setValue([
                  this.getRadii(this.annotationForm.controls.position1.value.split(',').map(n => +n), this.mousePos),
                ].join())
              } else {
                this.annotationForm.controls.position2.setValue(this.mousePos.join())
              }

              if (this.annotationForm.controls.position1.value
                  && this.selectedType.twoCoordinates
                  && this.annotationForm.controls.position2.value) {
                this.setAnnotation()
              }
            }
          }),

        this.interactiveViewer.viewerHandle.mouseEvent.pipe(
          filter((e: any) => e.eventName === 'click')
        ).subscribe(() => {
          if (this.selecting === 'position1' && this.annotationForm.controls.position1.value) {
            this.selectPosition1()
            if (this.selectedType.twoCoordinates) {
              this.changeSelectingPoint('position2')
            } else {
              this.changeSelectingPoint('')
            }
            this.changeDetectionRef.detectChanges()
          } else if (this.selecting === 'position2' && this.mousePos) {
            this.selectPosition2()
          }
        }),
        this.store.pipe(
          select(viewerStateSelectedTemplateSelector)
        ).subscribe(tmpl => {
          this.annotationForm.controls.templateName.setValue(tmpl.name)
          this.selectedTemplate = tmpl.name
        })
      )

      // this.voxelSize = this.nehubaViewer.config.dataset.initialNgState.navigation.pose.position.voxelSize
    }

    changeSelectingPoint(selecting) {
      this.selecting = selecting
      this.selectingOutput.emit(selecting)
    }

    selectPosition1() {
      this.position1Selected = true
      if (!this.selectedType.twoCoordinates) {
        this.changeSelectingPoint('')
        this.setAnnotation()
      }

    }

    selectPosition2() {
      this.position2Selected = true
      this.changeSelectingPoint('')
      this.changeDetectionRef.detectChanges()

      if (this.position1Selected) {
        this.changeSelectingPoint('')
        this.setAnnotation()
      }
    }

    position1CursorOut() {
      if (this.annotationForm.controls.position1.value && !this.cursorOut) {
        this.selectPosition1()
      }
    }
    position2CursorOut() {
      if (this.annotationForm.controls.position2.value && !this.cursorOut) {
        this.selectPosition2()
      }
    }

    selectType(type) {
      this.selectedType = type
      this.annotationForm.controls.type.setValue(type.name.toLowerCase())
      this.annotationForm.controls.position1.setValue('')
      this.annotationForm.controls.position2.setValue('')
      if (!this.showOnFocus || this.showFull) this.changeSelectingPoint('position1')
      this.position1Selected = false
      this.position2Selected = false
    }

    focusInName() {
      if (this.showOnFocus) {
        if (!this.showFull) {
          this.changeSelectingPoint('position1')
        }
        this.showFull = true
        this.editingMode.emit(this.showFull)
      }
    }

    setAnnotation() {
      const annotationLayer = this.viewer.layerManager.getLayerByName('user_annotations').layer
      const annotations = annotationLayer.localAnnotations.toJSON()

      // ToDo Still some error with the logic
      // const position1Voxel = this.annotationForm.controls.position1.value.split(',')
      //   .map((r, i) => r/this.voxelSize[i])
      // const position2Voxel = this.annotationForm.controls.position2.value.split(',')
      //   .map((r, i) => r/this.voxelSize[i])

      const position1Voxel = this.annotationForm.controls.position1.value.split(',')
      const position2Voxel = this.annotationForm.controls.position2.value.split(',')

      annotations.push({
        description: this.annotationForm.controls.description.value? this.annotationForm.controls.description.value : '',
        id: 'adding',
        point: this.annotationForm.controls.type.value === 'point'? this.annotationForm.controls.position1.value.split(',') : null,
        pointA: this.annotationForm.controls.type.value === 'line' || this.annotationForm.controls.type.value === 'bounding box'?
          position1Voxel : null,
        pointB: this.annotationForm.controls.type.value === 'line' || this.annotationForm.controls.type.value === 'bounding box'?
          position2Voxel : null,
        center: this.annotationForm.controls.type.value === 'ellipsoid'?
          position1Voxel : null,
        radii: this.annotationForm.controls.type.value === 'ellipsoid'?
          position2Voxel : null,
        type: this.annotationForm.controls.type.value !== 'bounding box'?
          this.annotationForm.controls.type.value.toUpperCase() : 'axis_aligned_bounding_box'
      })

      annotationLayer.localAnnotations.restoreState(annotations)
    }

    getRadii(a, b) {
      const returnArray = [Math.abs(b[0] - a[0]), Math.abs(b[1] - a[1]), Math.abs(b[2] - a[2])]
        .map(n => n === 0? this.ellipsoidMinRadius : n)
      return returnArray
    }

    removeLoadingAnnotation() {
      const annotationLayer = this.viewer.layerManager.getLayerByName('user_annotations')?.layer
      if (annotationLayer) {
        const annotations = annotationLayer.localAnnotations.toJSON()
        annotationLayer.localAnnotations.restoreState(annotations.filter(a => a.id !== 'adding'))
      }
    }

    submitForm() {
      if (this.annotationForm.valid) {
        // this.annotationForm.controls.annotationVisible.setValue('true')
        this.saveAnnotation.emit(this.annotationForm.value)
        this.cancelEditing()
      }
    }

    cancelEditing() {
      if (this.showOnFocus) {
        this.showFull = false
        this.editingMode.emit(this.showFull)
      }
      this.resetForm()
    }

    resetForm() {
      this.annotationForm.reset()
      this.annotationForm.markAsPristine()
      this.annotationForm.markAsUntouched()

      // Set form defaults
      this.annotationForm.controls.annotationVisible.setValue(true)
      this.annotationForm.controls.templateName.setValue(this.selectedTemplate)
      this.annotationForm.controls.templateName.setValue(this.selectedTemplate)

      this.position1Selected = false
      this.position1Selected = false
      this.selectType(this.annotationTypes[0])
      this.removeLoadingAnnotation()
      this.changeSelectingPoint('')

      Object.keys(this.annotationForm.controls).forEach(key => {
        this.annotationForm.get(key).setErrors(null)
      })
    }

    ngOnDestroy() {
      this.subscriptions.forEach(s => s.unsubscribe())
    }

}