Skip to content

Commit

Permalink
feat: allow upload
Browse files Browse the repository at this point in the history
WIP: rotaation widget
  • Loading branch information
xgui3783 committed Mar 29, 2024
1 parent d32a5ab commit eb4623f
Show file tree
Hide file tree
Showing 37 changed files with 1,217 additions and 124 deletions.
1 change: 1 addition & 0 deletions frontend/src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ import { Subject, debounceTime, map, merge, shareReplay } from 'rxjs';
{
provide: VOLUBA_APP_CONFIG,
useValue: {
uploadUrl: "https://zam10143.zam.kfa-juelich.de/chumni",
linearBackend: "https://voluba-backend.apps.tc.humanbrainproject.eu"
} as VolubaAppConfig
}
Expand Down
66 changes: 66 additions & 0 deletions frontend/src/const.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ export function transCoordSpcScaling(src: CoordSpace, dst: CoordSpace): export_n
}

export type VolubaAppConfig = {
uploadUrl: string
linearBackend: string
}

Expand All @@ -144,3 +145,68 @@ export type LinearXformResult = {
target_point: number[]
}[]
}

export const LOGIN_METHODS = [{
name: 'ebrains keycloak',
href: '/hbp-oidc-v2/auth'
}]

type ChumniNg = {
data_type: string
resolution: number[]
size: number[]
transform: number[][]
type: string // "image" | "segmentation"
}

type ChumniNifti = {
affineMatrix: number[][]
niftiVersion: string // "Nifti1" | "Nifti2"
byteOrder: string // "LITTLE_ENDIAN" | "BIG_ENDIAN"
size: number[]
voxelSize: number[]
spatialUnits: string
temporalUnits: string
dataType: string // float etc
coordinateSystem: string // SCANNER_ANAT etc
}

export type ChumniPreflightResp = {
fileName: string
neuroglancer: ChumniNg
nifti: ChumniNifti
warnings: string[]
}

export type ChumniVolume = {
visibility: 'public' | 'private'
name: string
extra: {
data: {
minValue: 0
maxValue: 0
}
fileName: string
fileSize: number
fileSizeUncompressed: number
neuroglancer: ChumniNg
nifti: ChumniNifti
uploaded: string
warnings: string[]
}
links: {
/**
* Absolute path (i.e. starts with /)
*/
normalized: string
}
}

export const SEGMENTATION_EXPLAINER_TEXT = "A segmentation nii file can be ingested differently to an image nii file"

export function trimFilename(filename: string): string {
if (filename.length < 10) {
return filename
}
return `${filename.slice(0, 10)}...`
}
111 changes: 111 additions & 0 deletions frontend/src/io/dragDrop.directive.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import { ChangeDetectorRef, Directive, ElementRef, EventEmitter, HostBinding, HostListener, Input, Output, inject } from "@angular/core";
import { fromEvent, merge, Observable, of } from "rxjs";
import { debounceTime, distinctUntilChanged, map, scan, switchMap, takeUntil } from "rxjs/operators";
import { MatSnackBar, MatSnackBarRef, SimpleSnackBar } from "src/sharedModule"
import { DestroyDirective } from "src/util/destroy.directive";

@Directive({
selector: '[drag-drop-file]',
exportAs: 'dragDropFile',
hostDirectives: [
DestroyDirective
]
})

export class DragDropFileDirective {

destroyed$ = inject(DestroyDirective).destroyed$

@Input()
public snackText: string|undefined

@Output('drag-drop-file')
public dragDropOnDrop: EventEmitter<File[]> = new EventEmitter()

@HostBinding('style.transition')
public transition = `opacity 300ms ease-in`

@HostBinding('style.opacity')
public hostOpacity: number = 0.5

get opacity() {
return this.hostOpacity
}

@Input('drag-drop-file-opacity')
set opacity(val: number) {
this.hostOpacity = val
this.cdr.markForCheck()
}

public snackbarRef: MatSnackBarRef<SimpleSnackBar>|undefined

private dragover$: Observable<boolean>

@HostListener('dragover', ['$event'])
public ondragover(ev: DragEvent) {
ev.preventDefault()
}

@HostListener('drop', ['$event'])
public ondrop(ev: DragEvent) {
ev.preventDefault()
this.reset()

this.dragDropOnDrop.emit(Array.from(ev?.dataTransfer?.files || []))
}

public reset() {
if (this.snackbarRef) {
this.snackbarRef.dismiss()
}
this.snackbarRef = undefined
this.opacity = 0.5
}

constructor(private snackBar: MatSnackBar, private el: ElementRef, private cdr: ChangeDetectorRef) {
this.dragover$ = merge(
of(null),
fromEvent(this.el.nativeElement, 'drop'),
).pipe(
switchMap(() => merge(
fromEvent(this.el.nativeElement, 'dragenter').pipe(
map(() => 1),
),
fromEvent(this.el.nativeElement, 'dragleave').pipe(
map(() => -1),
),
).pipe(
scan((acc, curr) => acc + curr, 0),
map(val => val > 0),
)),
)

this.dragover$.pipe(
takeUntil(this.destroyed$),
debounceTime(16),
distinctUntilChanged(),
).subscribe(flag => {
if (flag) {
this.snackbarRef = this.snackBar.open(
this.snackText || `Drop file(s) here.`, 'Dismiss',
{
panelClass: 'sxplr-pe-none'
}
)

/**
* In buggy scenarios, user could at least dismiss by action
*/
this.snackbarRef.afterDismissed().subscribe(reason => {
if (reason.dismissedByAction) {
this.reset()
}
})
this.opacity = 0.2
} else {
this.reset()
}
})
}
}
7 changes: 7 additions & 0 deletions frontend/src/io/module.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,22 @@
import { NgModule } from "@angular/core";
import { SaveToFileDirective } from "./saveFile.directive";
import { LoadFromFileDirective } from "./loadFile.directive";
import { DragDropFileDirective } from "./dragDrop.directive";
import { SharedModule } from "src/sharedModule/sharedModule";

@NgModule({
imports: [
SharedModule,
],
declarations: [
SaveToFileDirective,
LoadFromFileDirective,
DragDropFileDirective,
],
exports: [
SaveToFileDirective,
LoadFromFileDirective,
DragDropFileDirective,
]
})

Expand Down
2 changes: 1 addition & 1 deletion frontend/src/landmarks/listview/listview.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ export class ListviewComponent {
)
const position = [...landmark.position]
const { vec3 } = export_nehuba
if (landmark.targetVolumeId === inc?.['@id']) {
if (landmark.targetVolumeId === inc?.id) {
vec3.transformMat4(position, position, xform)
}

Expand Down
8 changes: 4 additions & 4 deletions frontend/src/landmarks/toolbar/toolbar.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -222,8 +222,8 @@ export class ToolbarComponent {
return
}
const volId = !!purgatory
? incomingVol['@id']
: referenceVol['@id']
? incomingVol.id
: referenceVol.id

this.store.dispatch(
app.actions.addLandmark({
Expand Down Expand Up @@ -307,11 +307,11 @@ export class ToolbarComponent {
id,
incLm: {
position: inc.coord.map(v => v*1e6) as [number, number, number],
targetVolumeId: selectedIncomingVolume['@id']
targetVolumeId: selectedIncomingVolume.id
},
tmplLm: {
position: ref.coord.map(v => v*1e6) as [number, number, number],
targetVolumeId: selectedRefenceVolume['@id']
targetVolumeId: selectedRefenceVolume.id
},
name
}
Expand Down
10 changes: 7 additions & 3 deletions frontend/src/layer-tune/layer-tune.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,21 @@ import { MatInputModule } from '@angular/material/input';
import { MatSlideToggleModule } from '@angular/material/slide-toggle';
import { ScrollableInput } from './scrollableInput.directive';
import { SharedModule } from 'src/sharedModule/sharedModule';
import { RotationWidgetCmp } from './rotation-widget/rotation-widget.components';

@NgModule({
declarations: [TuneUiComponent, ScrollableInput, RotationWidgetCmp],
declarations: [
TuneUiComponent,
ScrollableInput,
],
imports: [
CommonModule,
ReactiveFormsModule,
MatInputModule,
MatSlideToggleModule,
SharedModule,
],
exports: [TuneUiComponent, RotationWidgetCmp],
exports: [
TuneUiComponent
],
})
export class LayerTuneModule {}
6 changes: 6 additions & 0 deletions frontend/src/layer-tune/rotation-widget/consts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export type SvgPath = {
path: {
type: 'M' | 'C' | 'z'
coords: number[][]
}[]
}
24 changes: 24 additions & 0 deletions frontend/src/layer-tune/rotation-widget/module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { CommonModule } from "@angular/common";
import { NO_ERRORS_SCHEMA, NgModule } from "@angular/core";
import { GetClosestFurtherestPipe } from "./pathGetClosestFarthest.pipe";
import { SvgPathToDPipe } from "./pathToD.pipe";
import { RotationWidgetCmp } from "./rotation-widget.components";

@NgModule({
imports: [
CommonModule,
],
declarations: [
RotationWidgetCmp,
SvgPathToDPipe,
GetClosestFurtherestPipe,
],
exports: [
RotationWidgetCmp
],
schemas: [
NO_ERRORS_SCHEMA,
]
})

export class RotationWidgetModule {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { Pipe, PipeTransform } from "@angular/core";
import { SvgPath } from "./consts";

@Pipe({
name: 'getClosestFurtherest',
pure: true
})

export class GetClosestFurtherestPipe implements PipeTransform{
transform(value: SvgPath) {
const arr = value.path.map(p => p.coords).flatMap(v => v).filter(v => !!v)
const sorted = arr.map((coord, idx) => {
return {
z: coord[2],
idx,
}
})
sorted.sort((a, b) => a.z - b.z)
const sortedIdx = sorted[0].idx

return {
closest: arr[sortedIdx],
furthest: arr[(sortedIdx + 7) % arr.length]
}
}
}
16 changes: 16 additions & 0 deletions frontend/src/layer-tune/rotation-widget/pathToD.pipe.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { Pipe, PipeTransform } from "@angular/core";
import { SvgPath } from "./consts";

@Pipe({
name: 'pathToD',
pure: true
})

export class SvgPathToDPipe implements PipeTransform{
transform(value: SvgPath): string[] {
return value.path.map(obj => {
const coordStr = obj.coords.map(coord => coord.slice(0, 2)).join(" ")
return `${obj.type}${coordStr}`
})
}
}
Loading

0 comments on commit eb4623f

Please sign in to comment.