Skip to content

Commit

Permalink
feat: allow saving/loading of landmark pairs
Browse files Browse the repository at this point in the history
feat: indicate hovered landmark
feat: drag/drop landmarks
feat: allow deletion of landmark pairs
feat: show fallback msg when no landmarks has been added
feat: on click landmark in list view, navigates there
feat: show z displacement, use different anchor
feat: add voxel size adjustments parallel to scale adjustment
feat: add flip axis
bugfix: allow the tools buttons to toggle the dialog
WIP: share export component
feat: split view mode
feat: pretty print navigation array
bugfix: floatarrayeql compare length first
bugfix: copy paste no longer copies viewer state
  • Loading branch information
xgui3783 committed Mar 25, 2024
1 parent db1cf33 commit c3f2113
Show file tree
Hide file tree
Showing 51 changed files with 1,926 additions and 454 deletions.
18 changes: 15 additions & 3 deletions frontend/global.d.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
declare namespace export_nehuba {
class vec3 extends Float32Array {
static fromValues(...arr: number[]): vec3
static transformMat4(rec: vec3, src: vec3, mat: mat4): 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 create(): vec3
static add(rec: vec3, src: vec3, dst: vec3): vec3
static mul(rec: vec3, src: vec3, m: vec3): vec3
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
static divide(out: vec3, a: vec3, b: vec3): vec3
static div(out: vec3, a: vec3, b: vec3): vec3
static div(out: vec3|number[], a: vec3|number[], b: vec3|number[]): vec3|number[]
}

class quat extends Float32Array {
Expand All @@ -37,6 +37,7 @@ declare namespace export_nehuba {
static invert(out: mat4, src: mat4): mat4
static fromTranslation(out: mat4, transl: vec3): mat4
static fromRotation(out: mat4, angle: number, axis: vec3 ): mat4
static transpose(out: mat4, src: mat4): mat4
}

class UrlHashBinding {
Expand Down Expand Up @@ -83,6 +84,11 @@ declare namespace export_nehuba {
setVisible(flag: boolean): void
}

interface NgJsonable {
restoreState(state: any): void
toJSON(): any
}

interface NehubaViewer {
ngviewer: {
layerManager: {
Expand All @@ -94,6 +100,12 @@ declare namespace export_nehuba {
changed: {
add: (callback: () => void) => void
}
},
navigationState: {
pose: {
orientation: NgJsonable
position: NgJsonable
}
}
}
readonly navigationState: {
Expand Down
14 changes: 7 additions & 7 deletions frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
"@angular/router": "^15.0.0",
"@ngrx/effects": "^15.1.0",
"@ngrx/store": "^15.1.0",
"export-nehuba": "^0.1.0-dev.3",
"export-nehuba": "^0.1.7",
"prettier": "^2.8.3",
"rxjs": "~7.5.0",
"tslib": "^2.3.0",
Expand Down
43 changes: 15 additions & 28 deletions frontend/src/app/app.module.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { LayoutModule } from 'src/layout/layout.module';
import { isDefaultMode } from "src/state/app/selectors"

import { AppComponent } from './app.component';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { ViewsModule } from 'src/views/views.module';
import { StoreModule } from '@ngrx/store';
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, SLICEVIEWS_INJECTION_TOKEN, SliceViewEvent, SliceViewProviderType, arrayEqual, sliceViewEvEql, sliceViewEvIncludes } from 'src/const';
import { Subject, debounceTime, distinctUntilChanged, map, scan, shareReplay, throttleTime } from 'rxjs';
import { DEBOUNCED_WINDOW_RESIZE } from 'src/const';
import { Subject, debounceTime, map, merge, shareReplay } from 'rxjs';

@NgModule({
declarations: [AppComponent],
Expand All @@ -28,39 +29,25 @@ import { Subject, debounceTime, distinctUntilChanged, map, scan, shareReplay, th
providers: [
{
provide: DEBOUNCED_WINDOW_RESIZE,
useFactory: () => {
useFactory: (store: Store) => {
const resizeSub = new Subject<UIEvent>()
window.addEventListener("resize", ev => {
resizeSub.next(ev)
})
return resizeSub.pipe(

return merge(
store.pipe(
select(isDefaultMode),
map(flag => new UIEvent(`x-resize-default-mode-${flag ? 'on' : 'off'}`))
),
resizeSub,
).pipe(
debounceTime(160),
shareReplay(1),
)
}
},
deps: [ Store ]
},
{
provide: SLICEVIEWS_INJECTION_TOKEN,
useFactory: () => {
const obs = new Subject<SliceViewEvent>()
return {
register: obs.next.bind(obs),
observable: obs.pipe(
scan((acc, v) => {
if (sliceViewEvIncludes(v, acc)) {
return acc
}
return acc.concat(v)
}, [] as SliceViewEvent[]),
map(v => v.slice(0, 4)),
distinctUntilChanged(
(p, c) => arrayEqual(p, c, sliceViewEvEql)
),
shareReplay(1),
)
} as SliceViewProviderType
}
}
],
bootstrap: [AppComponent],
})
Expand Down
26 changes: 18 additions & 8 deletions frontend/src/const.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { InjectionToken } from "@angular/core";
import { Observable } from "rxjs";
import { Landmark } from "./landmarks/const";

export type RecursivePartial<T extends Record<string, unknown>> = Partial<{
[K in keyof T]: Partial<T[K]>;
Expand Down Expand Up @@ -27,7 +28,19 @@ export function isVec3(input: unknown): input is Vec3 {
}

export function arrayEqual<T>(a: T[], b: T[], predicate: (a: T, b: T) => boolean = (a, b) => a === b): boolean {
return a.every((v, idx) => predicate(v, b[idx])) && a.length === b.length
return a.length === b.length && a.every((v, idx) => predicate(v, b[idx]))
}

export function FloatArrayEql(a: Float32Array, b: Float32Array): boolean {
if (a.length !== b.length) {
return false
}
for (let i = 0; i < a.length; i++){
if (a[i] !== b[i]) {
return false
}
}
return true
}

export function isDefined<T>(v: T|null|undefined): v is T {
Expand All @@ -41,17 +54,14 @@ export type SliceViewEvent = {
sliceview: export_nehuba.SliceView
}

export interface SliceViewProviderType {
observable: Observable<SliceViewEvent[]>
register: (ev: SliceViewEvent) => void
}

export const SLICEVIEWS_INJECTION_TOKEN = new InjectionToken<SliceViewProviderType>("SLICEVIEWS_INJECTION_TOKEN")

export function sliceViewEvEql(a: SliceViewEvent, b: SliceViewEvent): boolean {
return a.element === b.element && a.sliceview === b.sliceview
}

export function sliceViewEvIncludes(ev: SliceViewEvent, list: SliceViewEvent[]): boolean {
return list.some(it => sliceViewEvEql(it, ev))
}

export const EXPORT_LANDMARKS_TYPE = 'https://voluba.apps.hbp.eu/@types/landmarks'

export type OverlayLm = Landmark & { color: string, id: string, highlighted?: boolean }
3 changes: 2 additions & 1 deletion frontend/src/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
<style>
html, body
{
background-color: rgba(10, 10, 10, 1);
background-color: rgba(10, 10, 10, 0.3);
overflow: hidden;
}
</style>
</head>
Expand Down
39 changes: 39 additions & 0 deletions frontend/src/io/loadFile.directive.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { Directive, EventEmitter, HostListener, Input, Output } from "@angular/core";

@Directive({
selector: '[voluba-load-from-file]',
exportAs: 'volubaLoadFromFile',
})

export class LoadFromFileDirective {
@Input('from-file-extensions')
extensionsToLoad: string = '.json'

@Output('from-file-content')
emitContent = new EventEmitter<string>()

@HostListener('click')
load(){
const input = document.createElement("input")
input.type = 'file'
input.accept = this.extensionsToLoad
document.body.appendChild(input)
input.onchange = () => {
if (input.files?.length !== 1) {
throw new Error(`Expected one and only one file selected, but got ${input.files?.length}`)
}
const file = input.files[0]
const reader = new FileReader()
reader.onload = () => {
const out = reader.result
if (!out) throw new Error(`Could not get any content!`)
this.emitContent.emit(out as string)
}
reader.onerror = e => { throw e }
reader.readAsText(file, 'utf-8')

document.body.removeChild(input)
}
input.click()
}
}
16 changes: 16 additions & 0 deletions frontend/src/io/module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { NgModule } from "@angular/core";
import { SaveToFileDirective } from "./saveFile.directive";
import { LoadFromFileDirective } from "./loadFile.directive";

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

export class IOModule{}
35 changes: 35 additions & 0 deletions frontend/src/io/saveFile.directive.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { Directive, HostListener, Input } from "@angular/core";

const textEncoder = new TextEncoder()

@Directive({
selector: '[voluba-save-to-file]',
exportAs: 'volubaSaveToFile'
})

export class SaveToFileDirective{
@Input('to-file-content')
textToSave: string|undefined

@Input('to-file-filename')
filename: string = 'savedFile.json'

@Input('to-file-mimetype')
mimetype: string = 'application/json'

@HostListener('click')
onClick(){
const { mimetype, filename, textToSave } = this
const blob = new Blob([ textEncoder.encode(textToSave) || ''], {type: mimetype})
const _url = URL.createObjectURL(blob)

const link = document.createElement('a')
link.setAttribute('href', _url)
link.setAttribute('download', filename)
link.style.visibility = 'hidden'

document.body.appendChild(link)
link.click()
document.body.removeChild(link)
}
}
6 changes: 5 additions & 1 deletion frontend/src/landmarks/landmarks.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,22 @@ import { ToolbarComponent } from './toolbar/toolbar.component';
import { SharedModule } from 'src/sharedModule/sharedModule';
import { OverlayComponent } from './overlay/overlay.component';
import { OverlayPositionPipe } from './overlay/overlayPosition.pipe';
import { OverlayStemStylePipe } from './overlay/overlayStemStyle.pipe';
import { IOModule } from 'src/io/module';

@NgModule({
declarations: [
ListviewComponent,
ToolbarComponent,
OverlayComponent,
OverlayPositionPipe
OverlayPositionPipe,
OverlayStemStylePipe,
],
imports: [
CommonModule,
FormsModule,
SharedModule,
IOModule,
],
exports: [ListviewComponent, ToolbarComponent, OverlayComponent],
})
Expand Down
Loading

0 comments on commit c3f2113

Please sign in to comment.