Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Toggle crosshair and perspective background #1153

Open
wants to merge 11 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions common/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,11 @@
// volume tuning specific
VOLUME_TUNING_EXPAND: 'Expand volume tuning widget',
BULK_DELETE_ANNOTATIONS: 'Delete all user annotations',

// Config component
AXIS_LINE_TOOLTIP: `Show axis lines on the views`,
BACKGROUND_COLORING_TOOLTIP: `Show blue background coloring on the perspective view`,

}

exports.IDS = {
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"@angular-devkit/build-angular": "^12.2.13",
"@angular/cli": "^12.2.13",
"@angular/compiler-cli": "^12.2.13",
"@ngrx/component-store": "^12.2.0",
"@types/jasmine": "~3.8.0",
"@types/node": "^12.11.1",
"@typescript-eslint/eslint-plugin": "^4.29.2",
Expand Down
168 changes: 168 additions & 0 deletions src/ui/config/configCmp/config.component.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
import {ConfigComponent} from "src/ui/config/configCmp/config.component";
import {ComponentFixture, TestBed} from "@angular/core/testing";
import {Observable, of} from "rxjs";
import {AngularMaterialModule} from "src/sharedModules";
import {PluginModule} from "src/plugin";
import {LayoutModule} from "src/layouts/layout.module";
import {ConfigStore} from "src/ui/config/configCmp/config.store";
import {Action, StoreModule} from "@ngrx/store";
import {HttpClientModule} from "@angular/common/http";
import {provideMockActions} from "@ngrx/effects/testing";
import {MockStore, provideMockStore} from "@ngrx/store/testing";
import {BS_ENDPOINT} from "src/util/constants";
import {HarnessLoader} from "@angular/cdk/testing";
import {TestbedHarnessEnvironment} from "@angular/cdk/testing/testbed";
import {MatSlideToggleHarness} from "@angular/material/slide-toggle/testing";
import {
ngViewerSelectorPanelMode,
ngViewerSelectorPanelOrder
} from "src/services/state/ngViewerState/selectors";
import {PANELS} from "src/services/state/ngViewerState/constants";
import {BrowserAnimationsModule} from "@angular/platform-browser/animations";
import {PureContantService} from "src/util";


fdescribe('config.component.ts', () => {
let component: ConfigComponent
let fixture: ComponentFixture<ConfigComponent>

const mockConfigStore = jasmine.createSpyObj(
'ConfigStore',
['setState',
'setAxisLineVisible',
'setSliceBackground',
'setBackgroundVisibility'],
{ sliceBackgroundRgb$: of('#CCCCCC'),
axisLineVisible$: of(false),
togglePerspectiveViewSubstrate$: of(true)})

const MOCK_BS_ENDPOINT = `http://localhost:1234`
const actions$: Observable<Action> = of({type: 'TEST'})
let mockStore: MockStore
let loader: HarnessLoader;



beforeEach((async () => {
await TestBed.configureTestingModule({
declarations: [ ConfigComponent, ],
imports: [
StoreModule.forRoot({}),
AngularMaterialModule,
BrowserAnimationsModule,
HttpClientModule,
PluginModule,
LayoutModule
],
providers: [
provideMockActions(() => actions$),
provideMockStore({
initialState: {
viewerConfigState: {
gpuLimit: 1e9,
animation: true
}
}
}),
{
provide: BS_ENDPOINT,
useValue: MOCK_BS_ENDPOINT
},
{
provide: PureContantService,
useFactory: () => {
return {
getViewerConfig: jasmine.createSpy('getViewerConfig')
}
}
}
],
}).compileComponents()

await TestBed.overrideProvider(ConfigStore, { useValue: mockConfigStore })

mockStore = await TestBed.inject(MockStore)
await mockStore.overrideSelector(ngViewerSelectorPanelMode, PANELS.FOUR_PANEL)
await mockStore.overrideSelector(ngViewerSelectorPanelOrder, '0123')

fixture = await TestBed.createComponent(ConfigComponent)
await fixture.detectChanges()
loader = await TestbedHarnessEnvironment.loader(fixture);
component = await fixture.componentInstance


}))

describe('Viewer config', () => {

beforeEach(async () => {
const configEl: HTMLElement = fixture.nativeElement;
const tabGroup = configEl.querySelector('mat-tab-group')!
const tabEl = tabGroup.querySelector('[aria-label="viewer-tab"]')!

const event = await new MouseEvent('click', {bubbles: true})
tabEl.dispatchEvent(event);
})

describe('set axlisLineVisible visibility', () => {

it('axisLine toggle should false by default', async () => {
const axisLineEl = await loader.getAllHarnesses(
MatSlideToggleHarness.with({
name: 'axis-line-toggle',
})
)
const isChecked = await axisLineEl[0].isChecked()
expect(isChecked).toBeFalse()
})

it('toggle axis line element should call mockConfigStore -> setAxisLineVisible', async () => {
const axisLineEl = await loader.getAllHarnesses(
MatSlideToggleHarness.with({
name: 'axis-line-toggle',
})
)
await axisLineEl[0].toggle()
expect(mockConfigStore.setAxisLineVisible).toHaveBeenCalledWith(true);
})

});

describe('set background visibility', () => {

it('togglePerspectiveViewSubstrate toggle should false by default', async () => {
const persBgEl = await loader.getAllHarnesses(
MatSlideToggleHarness.with({
name: 'perspective-background-toggle',
})
)
const isChecked = await persBgEl[0].isChecked()
expect(isChecked).toBeTrue()
})

it('Bg color toggle should call mockConfigStore -> setBackgroundVisibility', async () => {
const persBgEl = await loader.getAllHarnesses(
MatSlideToggleHarness.with({
name: 'perspective-background-toggle',
})
)
await persBgEl[0].toggle()
expect(mockConfigStore.setBackgroundVisibility).toHaveBeenCalledWith(false);
})

it('backgroundColorPicker default value should be correct', async () => {
const persBgEl = await loader.getAllHarnesses(
MatSlideToggleHarness.with({
name: 'perspective-background-toggle',
})
)
await persBgEl[0].toggle()
const colorPicker = await fixture.nativeElement.querySelector('[name="backgroundColorPicker"]')!
expect(colorPicker.value).toEqual('#cccccc')
})

});

})

});
12 changes: 10 additions & 2 deletions src/ui/config/configCmp/config.component.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,22 @@
import { Component, OnDestroy, OnInit } from '@angular/core'
import { Component, Inject, OnDestroy, OnInit, Optional} from '@angular/core'
import { select, Store } from '@ngrx/store';
import { combineLatest, Observable, Subscription } from 'rxjs';
import { debounceTime, distinctUntilChanged, map, startWith } from 'rxjs/operators';
import { SUPPORTED_PANEL_MODES } from 'src/services/state/ngViewerState.store';
import { ngViewerActionSetPanelOrder } from 'src/services/state/ngViewerState.store.helper';
import { VIEWER_CONFIG_ACTION_TYPES, StateInterface as ViewerConfiguration } from 'src/services/state/viewerConfig.store'
import { IavRootStoreInterface } from 'src/services/stateStore.service';
import { isIdentityQuat } from 'src/viewerModule/nehuba/util';
import {isIdentityQuat, NEHUBA_INSTANCE_INJTKN} from 'src/viewerModule/nehuba/util';
import {MatSlideToggleChange} from "@angular/material/slide-toggle";
import {MatSliderChange} from "@angular/material/slider";
import { PureContantService } from 'src/util';
import { ngViewerActionSwitchPanelMode } from 'src/services/state/ngViewerState/actions';
import { ngViewerSelectorPanelMode, ngViewerSelectorPanelOrder } from 'src/services/state/ngViewerState/selectors';
import { viewerStateSelectorNavigation } from 'src/services/state/viewerState/selectors';
import {NehubaViewerUnit} from "src/viewerModule/nehuba";
import { ARIA_LABELS } from 'common/constants'
import {ComponentStore} from "src/viewerModule/componentStore";
import {ConfigStore} from "src/ui/config/configCmp/config.store";

const GPU_TOOLTIP = `Higher GPU usage can cause crashes on lower end machines`
const ANIMATION_TOOLTIP = `Animation can cause slowdowns in lower end machines`
Expand All @@ -33,6 +37,8 @@ export class ConfigComponent implements OnInit, OnDestroy {
public GPU_TOOLTIP = GPU_TOOLTIP
public ANIMATION_TOOLTIP = ANIMATION_TOOLTIP
public MOBILE_UI_TOOLTIP = MOBILE_UI_TOOLTIP
public AXIS_LINE_TOOLTIP = ARIA_LABELS.AXIS_LINE_TOOLTIP
public BACKGROUND_COLORING_TOOLTIP = ARIA_LABELS.BACKGROUND_COLORING_TOOLTIP
public supportedPanelModes = SUPPORTED_PANEL_MODES

/**
Expand All @@ -58,6 +64,7 @@ export class ConfigComponent implements OnInit, OnDestroy {
constructor(
private store: Store<IavRootStoreInterface>,
private pureConstantService: PureContantService,
public readonly configStore: ConfigStore,
) {

this.useMobileUI$ = this.pureConstantService.useTouchUI$
Expand Down Expand Up @@ -185,4 +192,5 @@ export class ConfigComponent implements OnInit, OnDestroy {
}

public stepSize: number = 10

}
59 changes: 59 additions & 0 deletions src/ui/config/configCmp/config.store.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import {ConfigStore} from "src/ui/config/configCmp/config.store";

describe('> config.store.ts', () => {
describe('> Viewer config', () => {

let configStore
beforeEach(() => {
configStore = new ConfigStore()
configStore.setState({
sliceBackground: [],
axisLineVisible: false,
togglePerspectiveViewSubstrate: false
})
})

it('set axlisLineVisible visibility', (done) => {
configStore.setAxisLineVisible(true)
configStore.state$.subscribe((state) => {
expect(state.axisLineVisible).toBeTrue()
done()
})

})

describe('set slice background color', () => {
it('set background with hex string', (done) => {
configStore.setSliceBackground('#32a852')
configStore.state$.subscribe((state) => {
expect(state.sliceBackground).toEqual([50, 168, 82, 0.2])
done()
})
})
it('set background with rgb', (done) => {
configStore.setSliceBackground([50, 50, 50])
configStore.state$.subscribe((state) => {
expect(state.sliceBackground).toEqual([50, 50, 50, 0.2])
done()
})
})
it('set background with rgba', (done) => {
configStore.setSliceBackground([0, 0, 0, 1])
configStore.state$.subscribe((state) => {
expect(state.sliceBackground).toEqual([0, 0, 0, 1])
done()
})
})

})

it('set background visibility', (done) => {
configStore.setBackgroundVisibility(true)
configStore.state$.subscribe((state) => {
expect(state.togglePerspectiveViewSubstrate).toBeTrue()
done()
})
})

})
})
95 changes: 95 additions & 0 deletions src/ui/config/configCmp/config.store.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import {Injectable} from "@angular/core";
import {Observable} from "rxjs";
import {ComponentStore} from "@ngrx/component-store";
import {take} from "rxjs/operators";
export interface ConfigState {
sliceBackground: any[]
axisLineVisible: boolean
togglePerspectiveViewSubstrate: boolean
}

@Injectable({
providedIn: 'root',
})
export class ConfigStore extends ComponentStore<ConfigState> {

private get viewer(){
return (window as any).viewer
}

private get nehubaViewer(){
return (window as any).nehubaViewer
}

constructor() {
super({
sliceBackground: [],
axisLineVisible: false,
togglePerspectiveViewSubstrate: false
})

// ToDo find better way to get nehubaviewer containing cofig
setTimeout(() => {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@xgui3783 on my local machine viewerCtrlCmp.component.ts > ViewerCtrlCmp > toggleParcVsbl > if _flagDelin is false > calls schedulRedraw FAILED test is failing? (it was not failed here, but I think potentially it could) could it be caused by setTimeout? if it is, would you have any idea how to get nehubaviewer containing config on to set initial values?

if (this.nehubaViewer && this.nehubaViewer.config) {
this.setSliceBackground(
this.nehubaViewer.config.layout.useNehubaPerspective.drawSubstrates.color
.map((v, i) => i === 3 ? v : Math.floor(v * 255))
)
this.select(state => state.sliceBackground).pipe(take(1)).subscribe(background => {
this.setBackgroundVisibility(background.length && background[3] > 0)
})
this.setAxisLineVisible((window as any).viewer.showAxisLines.value ? true : false)
}
})
}

readonly sliceBackground$: Observable<any[]> = this.select(state => state.sliceBackground)
readonly sliceBackgroundRgb$: Observable<string> = this.select(state => {
const color = state.sliceBackground
return color.length? '#' + [color[0], color[1], color[2]].map(x => x.toString(16).length === 1 ? '0' + x.toString(16) : x.toString(16)).join('') : ''
})
readonly axisLineVisible$: Observable<boolean> = this.select(state => state.axisLineVisible)
readonly togglePerspectiveViewSubstrate$: Observable<boolean> = this.select(state => state.togglePerspectiveViewSubstrate)


readonly setAxisLineVisible = this.updater((state, value: boolean) => {
if (this.viewer) this.viewer.showAxisLines.restoreState(value)
return {
...state,
axisLineVisible: value
}
})

readonly setSliceBackground = this.updater((state, value: any) => {
if (typeof value === 'string') value = this.hexToRgb(value)
value = value.length === 4 ? value : [...value, 0.2]

if (this.nehubaViewer) {
this.nehubaViewer.config.layout.useNehubaPerspective.drawSubstrates.color = value.map((v, i) => i === 3 ? v : v / 255.0)
this.nehubaViewer.redraw()
}

return {
...state,
sliceBackground: value
}
})

readonly setBackgroundVisibility = this.updater((state, value: boolean) => {

if (this.nehubaViewer) {
this.nehubaViewer.config.layout.useNehubaPerspective.drawSubstrates.color[3] = value ? 0.2 : 0
this.nehubaViewer.redraw()
}

return {
...state,
togglePerspectiveViewSubstrate: value
}
})

public hexToRgb(hex) : any[] {
return hex.match(/\w\w/g).map(x => parseInt(x, 16))
}

}
Loading