Skip to content

Commit

Permalink
feat: implement linear transform backend
Browse files Browse the repository at this point in the history
fix: infinite loop feedback on scale/voxelsize
feat: ng layer shader tune
feat: add support for n5
feat: add export/import of matrices
feat: keyboard shortcut for locking/unlocking inc volume
  • Loading branch information
xgui3783 committed Mar 28, 2024
1 parent c3f2113 commit d32a5ab
Show file tree
Hide file tree
Showing 29 changed files with 759 additions and 133 deletions.
4 changes: 4 additions & 0 deletions frontend/angular.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@
"input": "export-nehuba/dist/min/async_computation.bundle.js",
"inject": false,
"bundleName": "async_computation.bundle"
},{
"input": "export-nehuba/dist/min/blosc.bundle.js",
"inject": false,
"bundleName": "blosc.bundle"
}]
},
"configurations": {
Expand Down
23 changes: 14 additions & 9 deletions frontend/global.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ declare namespace export_nehuba {
static fromValues(...arr: number[]): vec3
static transformMat4(rec: vec3|number[], src: vec3|number[], mat: mat4): vec3
static transformQuat(rec: vec3, src: vec3, quat: quat): vec3
static sub(rec: vec3, src: vec3, dst: vec3): vec3
static sub(rec: vec3|number[], src: vec3|number[], dst: vec3|number[]): vec3|number[]
static create(): vec3
static add(rec: vec3, src: vec3, dst: vec3): vec3
static add(rec: vec3|number[], src: vec3|number[], dst: vec3|number[]): vec3|number[]
static mul(rec: vec3|number[], src: vec3|number[], m: vec3|number[]): vec3|number[]
static inverse(rec: vec3, src: vec3): vec3
static scale(rec: vec3, src: vec3, s: number): vec3
Expand All @@ -27,17 +27,18 @@ declare namespace export_nehuba {
class mat4 extends Float32Array {
static fromValues(...a: number[]): mat4
static create(): mat4
static getTranslation(rec: vec3, mat: mat4): vec3
static fromRotationTranslationScale(out: mat4, q: quat, v: vec3, s: vec3): mat4
static getRotation(out: quat, mat: mat4): quat
static getTranslation(rec: vec3|number[], mat: mat4|number[]): vec3|number[]
static fromRotationTranslationScale(out: mat4|number[], q: quat|number[], v: vec3|number[], s: vec3|number[]): mat4|number[]
static getRotation(out: quat, mat: mat4|number[]): quat
static getScaling(out: vec3, mat: mat4): vec3
static translate(out: mat4, src: mat4, translate: vec3): mat4
static fromScaling(out: mat4, scale: vec3): mat4
static mul(out: mat4, src: mat4, m: mat4): mat4
static mul(out: mat4|number[], src: mat4|number[], m: mat4|number[]): mat4|number[]
static invert(out: mat4, src: mat4): mat4
static fromTranslation(out: mat4, transl: vec3): mat4
static fromTranslation(out: mat4|number[], transl: vec3|number[]): mat4|number[]
static fromRotation(out: mat4, angle: number, axis: vec3 ): mat4
static transpose(out: mat4, src: mat4): mat4
static fromQuat(out: mat4|number[], q: quat|number[]): mat4|number[]
}

class UrlHashBinding {
Expand Down Expand Up @@ -84,10 +85,13 @@ declare namespace export_nehuba {
setVisible(flag: boolean): void
}

interface NgJsonable {
interface NgJsonable<T extends Record<string, any>=any> {
restoreState(state: any): void
toJSON(): any
toJSON(): T
}

type Unit = 'km' | 'm' | 'mm' | 'µm' | 'nm' | 'pm'
type Dimension = [number, Unit]

interface NehubaViewer {
ngviewer: {
Expand All @@ -107,6 +111,7 @@ declare namespace export_nehuba {
position: NgJsonable
}
}
coordinateSpace: NgJsonable<Record<'x'|'y'|'z', Dimension>>
}
readonly navigationState: {
readonly position: {
Expand Down
25 changes: 24 additions & 1 deletion frontend/src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { Store, StoreModule, select } from '@ngrx/store';
import { reducers, metaReducers, effects } from 'src/state';
import { EffectsModule } from '@ngrx/effects';
import { SharedModule } from 'src/sharedModule/sharedModule';
import { DEBOUNCED_WINDOW_RESIZE } from 'src/const';
import { DEBOUNCED_WINDOW_RESIZE, GET_NEHUBA_INJ, GetNehuba, VOLUBA_APP_CONFIG, VolubaAppConfig } from 'src/const';
import { Subject, debounceTime, map, merge, shareReplay } from 'rxjs';

@NgModule({
Expand Down Expand Up @@ -48,6 +48,29 @@ import { Subject, debounceTime, map, merge, shareReplay } from 'rxjs';
},
deps: [ Store ]
},
{
provide: GET_NEHUBA_INJ,
useFactory: () => {
let _callback: () => export_nehuba.NehubaViewer|null|undefined
return {
provideNehubaInstance(callback) {
_callback = callback
},
getNehubaInstance() {
if (!_callback) {
throw new Error(`No providers for nehuba was provided`)
}
return _callback()
},
} as GetNehuba
}
},
{
provide: VOLUBA_APP_CONFIG,
useValue: {
linearBackend: "https://voluba-backend.apps.tc.humanbrainproject.eu"
} as VolubaAppConfig
}
],
bootstrap: [AppComponent],
})
Expand Down
79 changes: 79 additions & 0 deletions frontend/src/const.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,82 @@ export function sliceViewEvIncludes(ev: SliceViewEvent, list: SliceViewEvent[]):
export const EXPORT_LANDMARKS_TYPE = 'https://voluba.apps.hbp.eu/@types/landmarks'

export type OverlayLm = Landmark & { color: string, id: string, highlighted?: boolean }

export type GetNehuba = {
getNehubaInstance(): export_nehuba.NehubaViewer|null|undefined
provideNehubaInstance(callback: () => export_nehuba.NehubaViewer|null|undefined): void
}

export const GET_NEHUBA_INJ = new InjectionToken('GET_NEHUBA_INJ')

export const XFORM_FILE_TYPE = "https://voluba.apps.hbp.eu/@types/transform"

type CoordSpace = Record<'x'|'y'|'z', export_nehuba.Dimension>

export const cvtToNm = {
pm: (v: number) => v * 1e-3,
nm: (v: number) => v,
μm: (v: number) => v * 1e3,
mm: (v: number) => v * 1e6,
cm: (v: number) => v * 1e7,
m: (v: number) => v * 1e9,
km: (v: number) => v * 1e12,
} as const

export type VoxelUnit = keyof typeof cvtToNm

export const cvtNmTo: Record<VoxelUnit, (nm: number) => number> = {
pm: v => v * 1e3,
nm: v => v,
μm: v => v * 1e-3,
mm: v => v * 1e-6,
cm: v => v * 1e-7,
m: v => v * 1e-9,
km: v => v * 1e-12,
}

export function canBeConverted(a: string): a is VoxelUnit{
return a in cvtToNm
}

function coordSpaceToNmScale(coordSpace: CoordSpace): export_nehuba.vec3 {
const { vec3 } = export_nehuba
const returnValues: number[] = []
const keys: (keyof CoordSpace)[] = ['x', 'y', 'z']
for (const key of keys){
const [ value, unit ] = coordSpace[key]
if (!canBeConverted(unit)){
throw new Error(`${unit} cannot be convertd`)
}
returnValues.push(
cvtToNm[unit](value)
)
}
return vec3.fromValues(...returnValues)
}

export function transCoordSpcScaling(src: CoordSpace, dst: CoordSpace): export_nehuba.vec3 {
const { vec3 } = export_nehuba
const srcScale = coordSpaceToNmScale(src)
const dstScale = coordSpaceToNmScale(dst)

return vec3.div(vec3.create(), dstScale, srcScale) as export_nehuba.vec3
}

export type VolubaAppConfig = {
linearBackend: string
}

export const VOLUBA_APP_CONFIG = new InjectionToken<VolubaAppConfig>("VOLUBA_APP_CONFIG")

export type LinearXformResult = {
RMSE: number
inverse_matrix: number[][]
transformation_matrix: number[][]
landmark_pairs: {
active: boolean
mismatch: number
source_point: number[]
target_point: number[]
}[]
}
2 changes: 1 addition & 1 deletion frontend/src/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@
<link href="http://127.0.0.1:8080/cdn/font.css" rel="stylesheet">
<link href="http://127.0.0.1:8080/cdn/icon.css" rel="stylesheet">
<script src="main.bundle.js" defer></script>
<script type="module" src="http://127.0.0.1:8080/cdn/ng-layer-tune/dist/ng-layer-tune/ng-layer-tune.esm.js"></script>
<style>
html, body
{
background-color: rgba(10, 10, 10, 0.3);
overflow: hidden;
}
</style>
</head>
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/landmarks/landmarks.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { OverlayComponent } from './overlay/overlay.component';
import { OverlayPositionPipe } from './overlay/overlayPosition.pipe';
import { OverlayStemStylePipe } from './overlay/overlayStemStyle.pipe';
import { IOModule } from 'src/io/module';
import { HttpClientModule } from '@angular/common/http';

@NgModule({
declarations: [
Expand All @@ -22,6 +23,7 @@ import { IOModule } from 'src/io/module';
FormsModule,
SharedModule,
IOModule,
HttpClientModule,
],
exports: [ListviewComponent, ToolbarComponent, OverlayComponent],
})
Expand Down
33 changes: 30 additions & 3 deletions frontend/src/landmarks/toolbar/toolbar.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

<button mat-fab
[color]="(addLmMode$ | async) ? 'primary' : 'default'"
matTooltip="Toggle add landmark mode"
(click)="toggleLandmarkMode()">
<mat-icon fontIcon="add_location"></mat-icon>
</button>
Expand All @@ -25,8 +26,34 @@

<mat-divider vertical="true"></mat-divider>

<button mat-icon-button color="primary" (click)="onCalculate()">
<mat-icon fontIcon="calculate"></mat-icon>
</button>
<ng-template [ngIf]="view.calcXformBusy">
<mat-progress-spinner mode="indeterminate">
</mat-progress-spinner>
</ng-template>
<ng-template [ngIf]="!view.calcXformBusy">
<button mat-icon-button color="primary"
matTooltip="Compute linear transform from landmarks"
[matMenuTriggerFor]="calculationMenu">
<mat-icon fontIcon="calculate"></mat-icon>
</button>
</ng-template>

<mat-menu #calculationMenu="matMenu">
<ng-template [ngIf]="view.landmarkLength < 3">
<span class="warning">
<mat-icon>warning</mat-icon>
<span>
need at least 3 landmark pairs
</span>
</span>
</ng-template>
<ng-template [ngIf]="view.landmarkLength >= 3">

<ng-template ngFor [ngForOf]="view.transformationTypes" let-transformType>
<button mat-menu-item (click)="onCalculate(transformType.value)">
{{ transformType.text }}
</button>
</ng-template>
</ng-template>
</mat-menu>
</ng-template>
21 changes: 20 additions & 1 deletion frontend/src/landmarks/toolbar/toolbar.component.scss
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,23 @@
display: flex;
flex-direction: row;
justify-content: space-evenly;
}
}

.warning
{
margin: 2rem 1rem;
padding: 0.2rem;
display: flex;
align-items: center;

> *
{
margin: 0.4rem;
}
}

mat-progress-spinner
{
max-width: 3rem;
max-height: 3rem;
}
Loading

0 comments on commit d32a5ab

Please sign in to comment.