diff --git a/firebird-ng/package-lock.json b/firebird-ng/package-lock.json index 8c756bb..cc13e2e 100644 --- a/firebird-ng/package-lock.json +++ b/firebird-ng/package-lock.json @@ -1,12 +1,12 @@ { "name": "firebird", - "version": "0.1.20", + "version": "0.1.23", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "firebird", - "version": "0.1.20", + "version": "0.1.23", "dependencies": { "@angular/animations": "^17.3.12", "@angular/cdk": "~17.3.10", diff --git a/firebird-ng/src/app/components/auto-rotate/auto-rotate.component.html b/firebird-ng/src/app/components/auto-rotate/auto-rotate.component.html new file mode 100644 index 0000000..cb3eb95 --- /dev/null +++ b/firebird-ng/src/app/components/auto-rotate/auto-rotate.component.html @@ -0,0 +1,7 @@ + + diff --git a/firebird-ng/src/app/components/auto-rotate/auto-rotate.component.scss b/firebird-ng/src/app/components/auto-rotate/auto-rotate.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/firebird-ng/src/app/components/auto-rotate/auto-rotate.component.test.ts b/firebird-ng/src/app/components/auto-rotate/auto-rotate.component.test.ts new file mode 100644 index 0000000..8a6a94a --- /dev/null +++ b/firebird-ng/src/app/components/auto-rotate/auto-rotate.component.test.ts @@ -0,0 +1,49 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { AutoRotateComponent } from './auto-rotate.component'; +import { EventDisplayService } from '../../../services/event-display.service'; +import { PhoenixUIModule } from '../../phoenix-ui.module'; + +describe('AutoRotateComponent', () => { + let component: AutoRotateComponent; + let fixture: ComponentFixture; + + const mockEventDisplay = { + getUIManager: jest.fn().mockReturnThis(), + setAutoRotate: jest.fn().mockReturnThis(), + }; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [PhoenixUIModule], + providers: [ + { + provide: EventDisplayService, + useValue: mockEventDisplay, + }, + ], + declarations: [AutoRotateComponent], + }).compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(AutoRotateComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + it('should toggle auto rotate', () => { + expect(component.autoRotate).toBe(false); + + component.toggleAutoRotate(); + + expect(component.autoRotate).toBe(true); + expect(mockEventDisplay.getUIManager().setAutoRotate).toHaveBeenCalledWith( + component.autoRotate, + ); + }); +}); diff --git a/firebird-ng/src/app/components/auto-rotate/auto-rotate.component.ts b/firebird-ng/src/app/components/auto-rotate/auto-rotate.component.ts new file mode 100644 index 0000000..00686d2 --- /dev/null +++ b/firebird-ng/src/app/components/auto-rotate/auto-rotate.component.ts @@ -0,0 +1,23 @@ +import { Component } from '@angular/core'; +import {EventDisplayService} from "phoenix-ui-components"; +import {MenuToggleComponent} from "../menu-toggle/menu-toggle.component"; + +@Component({ + selector: 'app-custom-auto-rotate', + templateUrl: './auto-rotate.component.html', + styleUrls: ['./auto-rotate.component.scss'], + imports: [ + MenuToggleComponent + ], + standalone: true +}) +export class AutoRotateComponent { + autoRotate = false; + + constructor(private eventDisplay: EventDisplayService) {} + + toggleAutoRotate() { + this.autoRotate = !this.autoRotate; + this.eventDisplay.getUIManager().setAutoRotate(this.autoRotate); + } +} diff --git a/firebird-ng/src/app/components/dark-theme/dark-theme.component.html b/firebird-ng/src/app/components/dark-theme/dark-theme.component.html new file mode 100644 index 0000000..5ab48a9 --- /dev/null +++ b/firebird-ng/src/app/components/dark-theme/dark-theme.component.html @@ -0,0 +1,7 @@ + + diff --git a/firebird-ng/src/app/components/dark-theme/dark-theme.component.scss b/firebird-ng/src/app/components/dark-theme/dark-theme.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/firebird-ng/src/app/components/dark-theme/dark-theme.component.test.ts b/firebird-ng/src/app/components/dark-theme/dark-theme.component.test.ts new file mode 100644 index 0000000..2e3187c --- /dev/null +++ b/firebird-ng/src/app/components/dark-theme/dark-theme.component.test.ts @@ -0,0 +1,51 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { DarkThemeComponent } from './dark-theme.component'; +import { EventDisplayService } from '../../../services/event-display.service'; +import { PhoenixUIModule } from '../../phoenix-ui.module'; + +describe('DarkThemeComponent', () => { + let component: DarkThemeComponent; + let fixture: ComponentFixture; + + const mockEventDisplay = { + getUIManager: jest.fn().mockReturnThis(), + getDarkTheme: jest.fn().mockReturnThis(), + setDarkTheme: jest.fn().mockReturnThis(), + }; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [PhoenixUIModule], + providers: [ + { + provide: EventDisplayService, + useValue: mockEventDisplay, + }, + ], + declarations: [DarkThemeComponent], + }).compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(DarkThemeComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + it('should initially get dark theme', () => { + jest.spyOn(mockEventDisplay, 'getDarkTheme'); + component.ngOnInit(); + expect(mockEventDisplay.getDarkTheme).toHaveBeenCalled(); + }); + + it('should set/toggle dark theme', () => { + component.darkTheme = false; + component.setDarkTheme(); + expect(component.darkTheme).toBe(true); + }); +}); diff --git a/firebird-ng/src/app/components/dark-theme/dark-theme.component.ts b/firebird-ng/src/app/components/dark-theme/dark-theme.component.ts new file mode 100644 index 0000000..051984e --- /dev/null +++ b/firebird-ng/src/app/components/dark-theme/dark-theme.component.ts @@ -0,0 +1,27 @@ +import { Component, type OnInit } from '@angular/core'; +import {EventDisplayService} from "phoenix-ui-components"; +import {MenuToggleComponent} from "../menu-toggle/menu-toggle.component"; + +@Component({ + selector: 'app-custom-dark-theme', + templateUrl: './dark-theme.component.html', + styleUrls: ['./dark-theme.component.scss'], + imports: [ + MenuToggleComponent + ], + standalone: true +}) +export class DarkThemeComponent implements OnInit { + darkTheme = false; + + constructor(private eventDisplay: EventDisplayService) {} + + ngOnInit(): void { + this.darkTheme = this.eventDisplay.getUIManager().getDarkTheme(); + } + + setDarkTheme() { + this.darkTheme = !this.darkTheme; + this.eventDisplay.getUIManager().setDarkTheme(this.darkTheme); + } +} diff --git a/firebird-ng/src/app/components/event-selector/event-selector.component.html b/firebird-ng/src/app/components/event-selector/event-selector.component.html new file mode 100644 index 0000000..6d64a52 --- /dev/null +++ b/firebird-ng/src/app/components/event-selector/event-selector.component.html @@ -0,0 +1,12 @@ + + + + {{ event }} + + + diff --git a/firebird-ng/src/app/components/event-selector/event-selector.component.scss b/firebird-ng/src/app/components/event-selector/event-selector.component.scss new file mode 100644 index 0000000..e1afa73 --- /dev/null +++ b/firebird-ng/src/app/components/event-selector/event-selector.component.scss @@ -0,0 +1,11 @@ +.eventSelector { + select { + width: 9rem; + padding: 5px 10px; + font-size: 12px; + border: 1px solid rgba(88, 88, 88, 0.08); + box-shadow: var(--phoenix-icon-shadow); + background-color: var(--phoenix-background-color-tertiary); + color: var(--phoenix-text-color-secondary); + } +} diff --git a/firebird-ng/src/app/components/event-selector/event-selector.component.test.ts b/firebird-ng/src/app/components/event-selector/event-selector.component.test.ts new file mode 100644 index 0000000..4ebd7c4 --- /dev/null +++ b/firebird-ng/src/app/components/event-selector/event-selector.component.test.ts @@ -0,0 +1,56 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { EventSelectorComponent } from './event-selector.component'; +import { EventDisplayService } from '../../../services/event-display.service'; +import { PhoenixUIModule } from '../../phoenix-ui.module'; + +describe('EventSelectorComponent', () => { + let component: EventSelectorComponent; + let fixture: ComponentFixture; + + const mockEventDisplayService = { + listenToLoadedEventsChange: jest.fn(), + loadEvent: jest.fn(), + }; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [PhoenixUIModule], + providers: [ + { + provide: EventDisplayService, + useValue: mockEventDisplayService, + }, + ], + declarations: [EventSelectorComponent], + }).compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(EventSelectorComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + it('should initialize to listen to loaded events change', () => { + component.ngOnInit(); + + expect( + mockEventDisplayService.listenToLoadedEventsChange, + ).toHaveBeenCalled(); + }); + + it('should change event through event display', () => { + const mockSelectEvent = { target: { value: 'TestEvent' } }; + + component.changeEvent(mockSelectEvent); + + expect(mockEventDisplayService.loadEvent).toHaveBeenCalledWith( + mockSelectEvent.target.value, + ); + }); +}); diff --git a/firebird-ng/src/app/components/event-selector/event-selector.component.ts b/firebird-ng/src/app/components/event-selector/event-selector.component.ts new file mode 100644 index 0000000..4594653 --- /dev/null +++ b/firebird-ng/src/app/components/event-selector/event-selector.component.ts @@ -0,0 +1,34 @@ +import { Component, type OnInit } from '@angular/core'; +import {EventDisplayService} from "phoenix-ui-components"; +import {MatTooltip} from "@angular/material/tooltip"; +import {NgForOf, NgIf} from "@angular/common"; + + +@Component({ + selector: 'app-custom-event-selector', + templateUrl: './event-selector.component.html', + styleUrls: ['./event-selector.component.scss'], + imports: [ + MatTooltip, + NgForOf, + NgIf + ], + standalone: true +}) +export class EventSelectorComponent implements OnInit { + // Array containing the keys of the multiple loaded events + events: string[] = []; + + constructor(private eventDisplay: EventDisplayService) {} + + ngOnInit() { + this.eventDisplay.listenToLoadedEventsChange( + (events) => (this.events = events), + ); + } + + changeEvent(selected: any) { + const value = selected.target.value; + this.eventDisplay.loadEvent(value); + } +} diff --git a/firebird-ng/src/app/components/main-view-toggle/main-view-toggle.component.html b/firebird-ng/src/app/components/main-view-toggle/main-view-toggle.component.html new file mode 100644 index 0000000..66aeb51 --- /dev/null +++ b/firebird-ng/src/app/components/main-view-toggle/main-view-toggle.component.html @@ -0,0 +1,8 @@ + + diff --git a/firebird-ng/src/app/components/main-view-toggle/main-view-toggle.component.scss b/firebird-ng/src/app/components/main-view-toggle/main-view-toggle.component.scss new file mode 100644 index 0000000..e69de29 diff --git a/firebird-ng/src/app/components/main-view-toggle/main-view-toggle.component.test.ts b/firebird-ng/src/app/components/main-view-toggle/main-view-toggle.component.test.ts new file mode 100644 index 0000000..ce19eda --- /dev/null +++ b/firebird-ng/src/app/components/main-view-toggle/main-view-toggle.component.test.ts @@ -0,0 +1,49 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { MainViewToggleComponent } from './main-view-toggle.component'; +import { EventDisplayService } from '../../../services/event-display.service'; +import { PhoenixUIModule } from '../../phoenix-ui.module'; + +describe('MainViewToggleComponent', () => { + let component: MainViewToggleComponent; + let fixture: ComponentFixture; + + const mockEventDisplay = { + getUIManager: jest.fn().mockReturnThis(), + toggleOrthographicView: jest.fn().mockReturnThis(), + }; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [PhoenixUIModule], + providers: [ + { + provide: EventDisplayService, + useValue: mockEventDisplay, + }, + ], + declarations: [MainViewToggleComponent], + }).compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(MainViewToggleComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + it('should switch main view', () => { + expect(component.orthographicView).toBe(false); + + component.switchMainView(); + + expect(component.orthographicView).toBe(true); + expect( + mockEventDisplay.getUIManager().toggleOrthographicView, + ).toHaveBeenCalled(); + }); +}); diff --git a/firebird-ng/src/app/components/main-view-toggle/main-view-toggle.component.ts b/firebird-ng/src/app/components/main-view-toggle/main-view-toggle.component.ts new file mode 100644 index 0000000..12aa669 --- /dev/null +++ b/firebird-ng/src/app/components/main-view-toggle/main-view-toggle.component.ts @@ -0,0 +1,28 @@ +import { Component } from '@angular/core'; +import {EventDisplayService} from "phoenix-ui-components"; +import {MenuToggleComponent} from "../menu-toggle/menu-toggle.component"; +import {MatTooltip} from "@angular/material/tooltip"; + + +@Component({ + selector: 'app-custom-main-view-toggle', + templateUrl: './main-view-toggle.component.html', + styleUrls: ['./main-view-toggle.component.scss'], + imports: [ + MenuToggleComponent, + MatTooltip + ], + standalone: true +}) +export class MainViewToggleComponent { + orthographicView: boolean = false; + + constructor(private eventDisplay: EventDisplayService) {} + + switchMainView() { + this.orthographicView = !this.orthographicView; + this.eventDisplay + .getUIManager() + .toggleOrthographicView(this.orthographicView); + } +} diff --git a/firebird-ng/src/app/components/menu-toggle/menu-toggle.component.html b/firebird-ng/src/app/components/menu-toggle/menu-toggle.component.html new file mode 100644 index 0000000..fcdd471 --- /dev/null +++ b/firebird-ng/src/app/components/menu-toggle/menu-toggle.component.html @@ -0,0 +1,11 @@ + + + + + diff --git a/firebird-ng/src/app/components/menu-toggle/menu-toggle.component.scss b/firebird-ng/src/app/components/menu-toggle/menu-toggle.component.scss new file mode 100644 index 0000000..0528817 --- /dev/null +++ b/firebird-ng/src/app/components/menu-toggle/menu-toggle.component.scss @@ -0,0 +1,38 @@ +:host { + display: flex; + margin: 0 0.6rem; + + .menu-toggle { + display: flex; + background: unset; + border: none; + height: 2.5rem; + width: 2.5rem; + min-height: 2.5rem; + min-width: 2.5rem; + padding: 0.65rem; + cursor: pointer; + align-self: center; + transition: all 0.4s; + + &-icon { + width: 100%; + height: 100%; + + &.active-icon { + --phoenix-options-icon-path: #00bcd4; + } + } + + &:hover { + background-color: var(--phoenix-options-icon-bg); + border-radius: 40%; + transition: all 0.4s; + } + + &.disabled { + cursor: not-allowed; + opacity: 0.4; + } + } +} diff --git a/firebird-ng/src/app/components/menu-toggle/menu-toggle.component.test.ts b/firebird-ng/src/app/components/menu-toggle/menu-toggle.component.test.ts new file mode 100644 index 0000000..570bdf8 --- /dev/null +++ b/firebird-ng/src/app/components/menu-toggle/menu-toggle.component.test.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { PhoenixUIModule } from '../../phoenix-ui.module'; + +import { MenuToggleComponent } from './menu-toggle.component'; + +describe('MenuToggleComponent', () => { + let component: MenuToggleComponent; + let fixture: ComponentFixture; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [PhoenixUIModule], + }).compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(MenuToggleComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/firebird-ng/src/app/components/menu-toggle/menu-toggle.component.ts b/firebird-ng/src/app/components/menu-toggle/menu-toggle.component.ts new file mode 100644 index 0000000..e2b8b83 --- /dev/null +++ b/firebird-ng/src/app/components/menu-toggle/menu-toggle.component.ts @@ -0,0 +1,20 @@ +import { Component, Input } from '@angular/core'; +import {NgClass} from "@angular/common"; +import {MatTooltip} from "@angular/material/tooltip"; + +@Component({ + selector: 'app-custom-menu-toggle', + templateUrl: './menu-toggle.component.html', + styleUrls: ['./menu-toggle.component.scss'], + imports: [ + NgClass, + MatTooltip + ], + standalone: true +}) +export class MenuToggleComponent { + @Input() icon: string = ''; + @Input() active: boolean = false; + @Input() tooltip: string = ''; + @Input() disabled: boolean = false; +} diff --git a/firebird-ng/src/app/components/object-clipping/object-clipping.component.html b/firebird-ng/src/app/components/object-clipping/object-clipping.component.html new file mode 100644 index 0000000..6f154ac --- /dev/null +++ b/firebird-ng/src/app/components/object-clipping/object-clipping.component.html @@ -0,0 +1,52 @@ + + + + Clipping + + + + + + + Start Angle + + + + + + Opening Angle + + + + + diff --git a/firebird-ng/src/app/components/object-clipping/object-clipping.component.scss b/firebird-ng/src/app/components/object-clipping/object-clipping.component.scss new file mode 100644 index 0000000..5a4ad30 --- /dev/null +++ b/firebird-ng/src/app/components/object-clipping/object-clipping.component.scss @@ -0,0 +1,8 @@ +.slider-btn { + overflow: visible; +} + +mat-slider { + margin-left: 0.75rem; + margin-right: 0.75rem; +} diff --git a/firebird-ng/src/app/components/object-clipping/object-clipping.component.test.ts b/firebird-ng/src/app/components/object-clipping/object-clipping.component.test.ts new file mode 100644 index 0000000..bedcf6a --- /dev/null +++ b/firebird-ng/src/app/components/object-clipping/object-clipping.component.test.ts @@ -0,0 +1,84 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ObjectClippingComponent } from './object-clipping.component'; +import { MatCheckboxChange } from '@angular/material/checkbox'; +import { EventDisplayService } from '../../../services/event-display.service'; +import { PhoenixUIModule } from '../../phoenix-ui.module'; + +describe('ObjectClippingComponent', () => { + let component: ObjectClippingComponent; + let fixture: ComponentFixture; + + const mockUIManager = { + rotateStartAngleClipping: jest.fn(), + rotateOpeningAngleClipping: jest.fn(), + setClipping: jest.fn(), + }; + + const mockEventDisplay = { + getUIManager: jest.fn(() => mockUIManager), + getStateManager: () => ({ + clippingEnabled: { + onUpdate: jest.fn(), + }, + startClippingAngle: { + onUpdate: jest.fn(), + }, + openingClippingAngle: { + onUpdate: jest.fn(), + }, + }), + }; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [PhoenixUIModule], + providers: [ + { + provide: EventDisplayService, + useValue: mockEventDisplay, + }, + ], + declarations: [ObjectClippingComponent], + }).compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(ObjectClippingComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + it('should toggle clipping', () => { + expect(component.clippingEnabled).toBeUndefined(); + const matCheckboxChange = new MatCheckboxChange(); + + // Check for true + matCheckboxChange.checked = true; + component.toggleClipping(matCheckboxChange); + expect(component.clippingEnabled).toBe(true); + + // Check for false + matCheckboxChange.checked = false; + component.toggleClipping(matCheckboxChange); + expect(component.clippingEnabled).toBe(false); + }); + + it('should change clipping angle', () => { + const sliderValue = 10; + + component.changeStartClippingAngle(sliderValue); + expect( + mockEventDisplay.getUIManager().rotateStartAngleClipping, + ).toHaveBeenCalledWith(sliderValue); + + component.changeOpeningClippingAngle(sliderValue); + expect( + mockEventDisplay.getUIManager().rotateOpeningAngleClipping, + ).toHaveBeenCalledWith(sliderValue); + }); +}); diff --git a/firebird-ng/src/app/components/object-clipping/object-clipping.component.ts b/firebird-ng/src/app/components/object-clipping/object-clipping.component.ts new file mode 100644 index 0000000..480b504 --- /dev/null +++ b/firebird-ng/src/app/components/object-clipping/object-clipping.component.ts @@ -0,0 +1,54 @@ +import { Component } from '@angular/core'; +import {MatCheckbox, MatCheckboxChange} from '@angular/material/checkbox'; +import {EventDisplayService} from "phoenix-ui-components"; +import {MatMenu, MatMenuItem, MatMenuTrigger} from "@angular/material/menu"; +import {MatSlider, MatSliderThumb} from "@angular/material/slider"; +import {MenuToggleComponent} from "../menu-toggle/menu-toggle.component"; + +@Component({ + selector: 'app-custom-object-clipping', + templateUrl: './object-clipping.component.html', + styleUrls: ['./object-clipping.component.scss'], + imports: [ + MatMenu, + MatCheckbox, + MatMenuItem, + MatSlider, + MatSliderThumb, + MenuToggleComponent, + MatMenuTrigger + ], + standalone: true +}) +export class ObjectClippingComponent { + clippingEnabled!: boolean; + startClippingAngle!: number; + openingClippingAngle!: number; + + constructor(private eventDisplay: EventDisplayService) { + const stateManager = this.eventDisplay.getStateManager(); + stateManager.clippingEnabled.onUpdate( + (clippingValue) => (this.clippingEnabled = clippingValue), + ); + stateManager.startClippingAngle.onUpdate( + (value) => (this.startClippingAngle = value), + ); + stateManager.openingClippingAngle.onUpdate( + (value) => (this.openingClippingAngle = value), + ); + } + + changeStartClippingAngle(startingAngle: number) { + this.eventDisplay.getUIManager().rotateStartAngleClipping(startingAngle); + } + + changeOpeningClippingAngle(openingAngle: number) { + this.eventDisplay.getUIManager().rotateOpeningAngleClipping(openingAngle); + } + + toggleClipping(change: MatCheckboxChange) { + const value = change.checked; + this.eventDisplay.getUIManager().setClipping(value); + this.clippingEnabled = value; + } +} diff --git a/firebird-ng/src/app/components/tool-panel/tool-panel.component.html b/firebird-ng/src/app/components/tool-panel/tool-panel.component.html index 05d390b..cab3b4c 100644 --- a/firebird-ng/src/app/components/tool-panel/tool-panel.component.html +++ b/firebird-ng/src/app/components/tool-panel/tool-panel.component.html @@ -21,8 +21,8 @@ (touchcancel)="clearZoom()"> zoom_out - - + + diff --git a/firebird-ng/src/app/components/tool-panel/tool-panel.component.ts b/firebird-ng/src/app/components/tool-panel/tool-panel.component.ts index 0f4e2bc..50d49d5 100644 --- a/firebird-ng/src/app/components/tool-panel/tool-panel.component.ts +++ b/firebird-ng/src/app/components/tool-panel/tool-panel.component.ts @@ -3,6 +3,8 @@ import {NgIf} from "@angular/common"; import {MatIcon} from "@angular/material/icon"; import {EventDisplayService, PhoenixUIModule} from 'phoenix-ui-components'; import {PhoenixThreeFacade} from "../../utils/phoenix-three-facade"; +import {ViewOptionsComponent} from "../view-options/view-options.component"; +import {MainViewToggleComponent} from "../main-view-toggle/main-view-toggle.component"; @Component({ selector: 'app-tool-panel', @@ -10,7 +12,9 @@ import {PhoenixThreeFacade} from "../../utils/phoenix-three-facade"; imports: [ NgIf, MatIcon, - PhoenixUIModule + PhoenixUIModule, + ViewOptionsComponent, + MainViewToggleComponent ], templateUrl: './tool-panel.component.html', styleUrl: './tool-panel.component.scss' diff --git a/firebird-ng/src/app/components/view-options/cartesian-grid-config/cartesian-grid-config.component.html b/firebird-ng/src/app/components/view-options/cartesian-grid-config/cartesian-grid-config.component.html new file mode 100644 index 0000000..4a62424 --- /dev/null +++ b/firebird-ng/src/app/components/view-options/cartesian-grid-config/cartesian-grid-config.component.html @@ -0,0 +1,157 @@ + + Customize Cartesian Grid + + + + + x (cm) : + + + + + y (cm) : + + + + + z (cm) : + + + + + Save + + + + Shift Cartesian Grid on click + + Click on a point to shift the grid. Keep clicking at various points to + continue shifting. Right click to stop. + + + + + + + Show XY Planes + + + + + + + XY Plane ({{ calcPlanes(gridConfig.zDistance) }}) + + + Show YZ Planes + + + + + + + YZ Plane ({{ calcPlanes(gridConfig.xDistance) }}) + + + Show ZX Planes + + + + + + + ZX Plane ({{ calcPlanes(gridConfig.yDistance) }}) + + + + + + Sparsity ({{ gridConfig.sparsity }}) + + + + + + + Close + + + diff --git a/firebird-ng/src/app/components/view-options/cartesian-grid-config/cartesian-grid-config.component.scss b/firebird-ng/src/app/components/view-options/cartesian-grid-config/cartesian-grid-config.component.scss new file mode 100644 index 0000000..815210d --- /dev/null +++ b/firebird-ng/src/app/components/view-options/cartesian-grid-config/cartesian-grid-config.component.scss @@ -0,0 +1,38 @@ +.container { + display: flex; + flex-direction: row; +} + +.item-config-single { + display: flex; + flex-direction: column; + + .item-config-group { + margin: 0.1rem 2rem; + + .item-config-label { + display: inline; + margin: 1rem; + } + + .form-control { + width: 60%; + display: inline; + } + } + + button { + margin: 0.5rem 4rem; + } + + .explain-button { + padding-top: 1rem; + } + + .explain-text { + width: 20rem; + opacity: 0.7; + margin-top: 0.5rem; + font-size: 0.9rem; + } +} diff --git a/firebird-ng/src/app/components/view-options/cartesian-grid-config/cartesian-grid-config.component.test.ts b/firebird-ng/src/app/components/view-options/cartesian-grid-config/cartesian-grid-config.component.test.ts new file mode 100644 index 0000000..080f3c4 --- /dev/null +++ b/firebird-ng/src/app/components/view-options/cartesian-grid-config/cartesian-grid-config.component.test.ts @@ -0,0 +1,269 @@ +import { ComponentFixture, TestBed, tick } from '@angular/core/testing'; + +import { CartesianGridConfigComponent } from './cartesian-grid-config.component'; +import { EventDisplayService, PhoenixUIModule } from 'phoenix-ui-components'; +import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; +import { MatCheckboxChange } from '@angular/material/checkbox'; +import { Vector3 } from 'three'; +import { of } from 'rxjs/internal/observable/of'; + +describe('CartesianGridConfigComponent', () => { + let component: CartesianGridConfigComponent; + let fixture: ComponentFixture; + + const mockDialogRef = { + close: jest.fn().mockReturnThis(), + }; + + const gridOrigin = new Vector3(100, 200, 300); + + const mockEventDisplay = { + getUIManager: jest.fn().mockReturnThis(), + translateCartesianGrid: jest.fn().mockReturnThis(), + translateCartesianLabels: jest.fn().mockReturnThis(), + getCartesianGridConfig: jest.fn().mockReturnValue({ + showXY: true, + showYZ: true, + showZX: true, + xDistance: 300, + yDistance: 300, + zDistance: 300, + sparsity: 2, + }), + setShowCartesianGrid: jest.fn().mockReturnThis(), + shiftCartesianGridByPointer: jest.fn().mockReturnThis(), + getThreeManager: jest.fn().mockReturnThis(), + originChanged: of(gridOrigin), + stopShifting: of(true), + origin: new Vector3(0, 0, 0), + originChangedEmit: jest.fn().mockReturnThis(), + }; + + const mockData = { + gridVisible: true, + scale: 3000, + }; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [PhoenixUIModule], + declarations: [CartesianGridConfigComponent], + providers: [ + { + provide: MatDialogRef, + useValue: mockDialogRef, + }, + { + provide: EventDisplayService, + useValue: mockEventDisplay, + }, + { + provide: MAT_DIALOG_DATA, + useValue: mockData, + }, + ], + }).compileComponents(); + + fixture = TestBed.createComponent(CartesianGridConfigComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + it('should set initial configuration', (done) => { + const VALUE1 = component.data.gridVisible; + const VALUE2 = component.data.scale; + + component.ngOnInit(); + + expect(component.showCartesianGrid).toBe(VALUE1); + expect(component.scale).toBe(VALUE2); + + expect( + mockEventDisplay.getUIManager().getCartesianGridConfig, + ).toHaveBeenCalled(); + + const VALUE3 = component.gridConfig; + + expect( + mockEventDisplay.getUIManager().getCartesianGridConfig, + ).toHaveReturnedWith(VALUE3); + + expect(mockEventDisplay.getThreeManager).toHaveBeenCalled(); + + const VALUE4 = component.cartesianPos; + + expect(mockEventDisplay.getThreeManager().origin).toBe(VALUE4); + done(); + }); + + it('should close', () => { + component.onClose(); + + expect(mockDialogRef.close).toHaveBeenCalled(); + }); + + it('should save the updated grid origin', () => { + const VALUE1 = 10; + const VALUE2 = 20; + const VALUE3 = 30; + + const spy = jest.spyOn(component, 'shiftCartesianGridByValues'); + + component.onSave(VALUE1, VALUE2, VALUE3); + + expect(spy).toHaveBeenCalledWith( + new Vector3(VALUE1 * 10, VALUE2 * 10, VALUE3 * 10), + ); + }); + + it('should shift cartesian grid by a mouse click', () => { + component.shiftCartesianGridByPointer(); + + mockEventDisplay.getUIManager().shiftCartesianGridByPointer(true); + + mockEventDisplay.getThreeManager().originChanged.subscribe((intersect) => { + expect(component.translateGrid).toHaveBeenCalledWith(intersect); + }); + + const originChangedUnSpy = jest.spyOn( + component.originChangedSub, + 'unsubscribe', + ); + const stopShiftingUnSpy = jest.spyOn( + component.stopShiftingSub, + 'unsubscribe', + ); + + mockEventDisplay.getThreeManager().stopShifting.subscribe((stop) => { + if (stop) { + expect(originChangedUnSpy).toHaveBeenCalled(); + expect(stopShiftingUnSpy).toHaveBeenCalled(); + } + }); + }); + + it('should shift cartesian grid by values', () => { + const VALUE = new Vector3(100, 200, 300); + + const spy = jest.spyOn(component, 'translateGrid'); + + component.shiftCartesianGridByValues(VALUE); + + expect(spy).toHaveBeenCalledWith(VALUE); + expect( + mockEventDisplay.getThreeManager().originChangedEmit, + ).toHaveBeenCalledWith(VALUE); + }); + + it('should translate grid', () => { + const VALUE1 = new Vector3(100, 200, 300); + + const finalPos = VALUE1; + const initialPos = component.cartesianPos; + const difference = new Vector3( + finalPos.x - initialPos.x, + finalPos.y - initialPos.y, + finalPos.z - initialPos.z, + ); + + component['translateGrid'](VALUE1); + + expect( + mockEventDisplay.getUIManager().translateCartesianGrid, + ).toHaveBeenCalledWith(difference.clone()); + expect( + mockEventDisplay.getUIManager().translateCartesianLabels, + ).toHaveBeenCalledWith(difference.clone()); + expect(component.cartesianPos).toBe(finalPos); + }); + + it('should add XY Planes', () => { + const event = { target: { value: '600' } } as any; + const VALUE = Number(event.target.value); + + const spy = jest.spyOn(component, 'callSetShowCartesianGrid'); + + component.addXYPlanes(event); + + expect(component.gridConfig.zDistance).toBe(VALUE); + expect(spy).toHaveBeenCalled(); + }); + + it('should add YZ Planes', () => { + const event = { target: { value: '600' } } as any; + const VALUE = Number(event.target.value); + + const spy = jest.spyOn(component, 'callSetShowCartesianGrid'); + + component.addYZPlanes(event); + + expect(component.gridConfig.xDistance).toBe(VALUE); + expect(spy).toHaveBeenCalled(); + }); + + it('should add ZX Planes', () => { + const event = { target: { value: '600' } } as any; + const VALUE = Number(event.target.value); + + const spy = jest.spyOn(component, 'callSetShowCartesianGrid'); + + component.addZXPlanes(event); + + expect(component.gridConfig.yDistance).toBe(VALUE); + expect(spy).toHaveBeenCalled(); + }); + + it('should change sparsity', () => { + const event = { target: { value: '2' } } as any; + const VALUE = Number(event.target.value); + + const spy = jest.spyOn(component, 'callSetShowCartesianGrid'); + component.changeSparsity(event); + + expect(component.gridConfig.sparsity).toBe(VALUE); + expect(spy).toHaveBeenCalled(); + }); + + it('should show XY Planes', () => { + const VALUE = false; + const event = new MatCheckboxChange(); + event.checked = VALUE; + + component.showXYPlanes(event); + expect(component.gridConfig.showXY).toBe(VALUE); + }); + + it('should show YZ Planes', () => { + const VALUE = false; + const event = new MatCheckboxChange(); + event.checked = VALUE; + + component.showYZPlanes(event); + expect(component.gridConfig.showYZ).toBe(VALUE); + }); + + it('should show ZX Planes', () => { + const VALUE = false; + const event = new MatCheckboxChange(); + event.checked = VALUE; + + component.showZXPlanes(event); + expect(component.gridConfig.showZX).toBe(VALUE); + }); + + it('should call setShowCartesianGrid', () => { + component.callSetShowCartesianGrid(); + + expect( + mockEventDisplay.getUIManager().setShowCartesianGrid, + ).toHaveBeenCalledWith( + component.showCartesianGrid, + component.scale, + component.gridConfig, + ); + }); +}); diff --git a/firebird-ng/src/app/components/view-options/cartesian-grid-config/cartesian-grid-config.component.ts b/firebird-ng/src/app/components/view-options/cartesian-grid-config/cartesian-grid-config.component.ts new file mode 100644 index 0000000..8992e8a --- /dev/null +++ b/firebird-ng/src/app/components/view-options/cartesian-grid-config/cartesian-grid-config.component.ts @@ -0,0 +1,179 @@ +import { Component, Inject, type OnInit } from '@angular/core'; +import { Vector3 } from 'three'; +import {MatCheckbox, MatCheckboxChange} from '@angular/material/checkbox'; +import { + MAT_DIALOG_DATA, + MatDialogActions, + MatDialogContent, + MatDialogRef, + MatDialogTitle +} from '@angular/material/dialog'; +import {EventDisplayService} from "phoenix-ui-components"; +import { Subscription } from 'rxjs'; +import {DecimalPipe} from "@angular/common"; +import {MatButton} from "@angular/material/button"; +import {MatMenuItem} from "@angular/material/menu"; +import {FormsModule} from "@angular/forms"; +import {MatSlider, MatSliderThumb} from "@angular/material/slider"; + +@Component({ + selector: 'app-cartesian-grid-config', + templateUrl: './cartesian-grid-config.component.html', + styleUrls: ['./cartesian-grid-config.component.scss'], + imports: [ + MatDialogTitle, + MatDialogContent, + DecimalPipe, + MatButton, + MatMenuItem, + MatCheckbox, + FormsModule, + MatSlider, + MatSliderThumb, + MatDialogActions + ], + standalone: true +}) +export class CartesianGridConfigComponent implements OnInit { + cartesianPos = new Vector3(); + originChangedSub: Subscription | null = null; + stopShiftingSub: Subscription | null = null; + showCartesianGrid!: boolean; + gridConfig!: { + showXY: boolean; + showYZ: boolean; + showZX: boolean; + xDistance: number; + yDistance: number; + zDistance: number; + sparsity: number; + }; + scale!: number; + shiftGrid!: boolean; + + constructor( + @Inject(MAT_DIALOG_DATA) + public data: { gridVisible: boolean; scale: number }, + private dialogRef: MatDialogRef, + private eventDisplay: EventDisplayService, + ) {} + + ngOnInit(): void { + this.shiftGrid = this.eventDisplay.getThreeManager().shiftGrid; + this.showCartesianGrid = this.data.gridVisible; + this.scale = this.data.scale; + this.gridConfig = this.eventDisplay.getUIManager().getCartesianGridConfig(); + this.cartesianPos = this.eventDisplay.getThreeManager().origin; + } + + onClose() { + this.dialogRef.close(); + } + + onSave(x: string, y: string, z: string) { + const xNum = Number(x); + const yNum = Number(y); + const zNum = Number(z); + this.shiftCartesianGridByValues(new Vector3(xNum * 10, yNum * 10, zNum * 10)); + } + + shiftCartesianGridByPointer() { + this.shiftGrid = true; + this.eventDisplay.getUIManager().shiftCartesianGridByPointer(); + this.originChangedSub = this.eventDisplay + .getThreeManager() + .originChanged.subscribe((intersect) => { + this.translateGrid(intersect); + }); + this.stopShiftingSub = this.eventDisplay + .getThreeManager() + .stopShifting.subscribe((stop) => { + if (stop) { + this.originChangedSub?.unsubscribe(); + this.stopShiftingSub?.unsubscribe(); + } + }); + this.onClose(); + } + + shiftCartesianGridByValues(position: Vector3) { + this.translateGrid(position); + this.eventDisplay.getThreeManager().originChangedEmit(position); + } + + translateGrid(position: Vector3) { + const finalPos = position; + const initialPos = this.cartesianPos; + const difference = new Vector3( + finalPos.x - initialPos.x, + finalPos.y - initialPos.y, + finalPos.z - initialPos.z, + ); + this.eventDisplay.getUIManager().translateCartesianGrid(difference.clone()); + this.eventDisplay + .getUIManager() + .translateCartesianLabels(difference.clone()); + this.cartesianPos = finalPos; + } + + addXYPlanes(zDistance: Event) { + this.gridConfig.zDistance = Number( + (zDistance.target as HTMLInputElement).value, + ); + this.callSetShowCartesianGrid(); + } + + addYZPlanes(xDistance: Event) { + this.gridConfig.xDistance = Number( + (xDistance.target as HTMLInputElement).value, + ); + this.callSetShowCartesianGrid(); + } + + addZXPlanes(yDistance: Event) { + this.gridConfig.yDistance = Number( + (yDistance.target as HTMLInputElement).value, + ); + this.callSetShowCartesianGrid(); + } + + changeSparsity(sparsity: Event) { + this.gridConfig.sparsity = Number( + (sparsity.target as HTMLInputElement).value, + ); + this.callSetShowCartesianGrid(); + } + + showXYPlanes(change: MatCheckboxChange) { + this.gridConfig.showXY = change.checked; + this.callSetShowCartesianGrid(); + } + + showYZPlanes(change: MatCheckboxChange) { + this.gridConfig.showYZ = change.checked; + this.callSetShowCartesianGrid(); + } + + showZXPlanes(change: MatCheckboxChange) { + this.gridConfig.showZX = change.checked; + this.callSetShowCartesianGrid(); + } + + callSetShowCartesianGrid() { + this.eventDisplay + .getUIManager() + .setShowCartesianGrid( + this.showCartesianGrid, + this.scale, + this.gridConfig, + ); + } + + // helper function to calculate number of planes + calcPlanes(dis: number) { + return Math.max( + 0, + 1 + 2 * Math.floor((dis * 10) / (this.scale * this.gridConfig.sparsity)), + ); + } +} diff --git a/firebird-ng/src/app/components/view-options/view-options.component.html b/firebird-ng/src/app/components/view-options/view-options.component.html new file mode 100644 index 0000000..4619b3b --- /dev/null +++ b/firebird-ng/src/app/components/view-options/view-options.component.html @@ -0,0 +1,103 @@ + + + Show Cartesian Grid + + + + + + + + + Show Eta Phi Grid + + + + Show Axis + + + + Show Labels + + + + Show 3D Coordinates + + + + Show 3D Distance + + + + + + + {{ view.name }} + + + + + diff --git a/firebird-ng/src/app/components/view-options/view-options.component.scss b/firebird-ng/src/app/components/view-options/view-options.component.scss new file mode 100644 index 0000000..980a33d --- /dev/null +++ b/firebird-ng/src/app/components/view-options/view-options.component.scss @@ -0,0 +1,31 @@ +.view-icon { + width: 1.2rem; + height: 1.2rem; + margin-right: 0.5rem; +} + +.icon-wrapper { + display: inline-block; + width: 1.5rem; + height: 1.5rem; + padding: 0.23rem; + transition: all 0.4s; + transform: translateY(27%); + + &.icon-button:hover { + background: var(--phoenix-options-icon-bg); + border-radius: 40%; + cursor: pointer; + } + + svg { + width: 100%; + height: 100%; + vertical-align: top; + } +} + +.item-settings { + margin-right: 0.2rem; + margin-left: 0.5rem; +} diff --git a/firebird-ng/src/app/components/view-options/view-options.component.test.ts b/firebird-ng/src/app/components/view-options/view-options.component.test.ts new file mode 100644 index 0000000..62c40d6 --- /dev/null +++ b/firebird-ng/src/app/components/view-options/view-options.component.test.ts @@ -0,0 +1,193 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { PresetView } from 'phoenix-event-display'; +import { ViewOptionsComponent } from './view-options.component'; +import { + EventDisplayService, + PhoenixUIModule, + CartesianGridConfigComponent, +} from 'phoenix-ui-components'; +import { MatCheckboxChange } from '@angular/material/checkbox'; +import { MatDialog } from '@angular/material/dialog'; +import { Vector3 } from 'three'; +import { of } from 'rxjs/internal/observable/of'; +import { Subscription } from 'rxjs'; + +describe('ViewOptionsComponent', () => { + let component: ViewOptionsComponent; + let fixture: ComponentFixture; + + const origin = new Vector3(100, 200, 300); + + const mockEventDisplay = { + getUIManager: jest.fn().mockReturnThis(), + getThreeManager: jest.fn().mockReturnThis(), + getPresetViews: jest.fn().mockReturnValue([]), + displayView: jest.fn().mockReturnThis(), + setShowAxis: jest.fn().mockReturnThis(), + setShowEtaPhiGrid: jest.fn().mockReturnThis(), + setShowCartesianGrid: jest.fn().mockReturnThis(), + showLabels: jest.fn().mockReturnThis(), + show3DMousePoints: jest.fn().mockReturnThis(), + show3DDistance: jest.fn().mockReturnThis(), + originChanged: of(origin), + }; + + const mockMatDialog = { + open: jest.fn().mockReturnThis(), + }; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [PhoenixUIModule], + declarations: [ViewOptionsComponent, CartesianGridConfigComponent], + providers: [ + { + provide: EventDisplayService, + useValue: mockEventDisplay, + }, + { + provide: MatDialog, + useValue: mockMatDialog, + }, + ], + }).compileComponents(); + + fixture = TestBed.createComponent(ViewOptionsComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); + + it('should initially get preset views and set up the subscription', () => { + component.ngOnInit(); + + expect(mockEventDisplay.getUIManager().getPresetViews).toHaveBeenCalled(); + + mockEventDisplay.getThreeManager().originChanged.subscribe((intersect) => { + expect(component.origin).toBe(intersect); + }); + }); + + it('should open cartesian grid config dialog box', () => { + const mockParams = { + data: { + gridVisible: component.showCartesianGrid, + scale: component.scale, + }, + position: { + bottom: '5rem', + left: '3rem', + }, + }; + + component.openCartesianGridConfigDialog(); + + expect(mockMatDialog.open).toHaveBeenCalledWith( + CartesianGridConfigComponent, + mockParams, + ); + }); + + it('should display the chosen preset view', () => { + const mockEvent = { + stopPropagation: jest.fn(), + }; + const mockPresetView = new PresetView( + 'Test View', + [0, 0, -12000], + [0, 0, 0], + 'left-cube', + ); + component.displayView(mockEvent, mockPresetView); + + expect(mockEventDisplay.getUIManager().displayView).toHaveBeenCalledWith( + mockPresetView, + ); + }); + + it('should set axis', () => { + const VALUE = false; + const event = new MatCheckboxChange(); + event.checked = VALUE; + + component.setAxis(event); + + expect(mockEventDisplay.getUIManager().setShowAxis).toHaveBeenCalledWith( + VALUE, + ); + }); + + it('should set eta phi grid', () => { + const VALUE = false; + const event = new MatCheckboxChange(); + event.checked = VALUE; + + component.setEtaPhiGrid(event); + + expect( + mockEventDisplay.getUIManager().setShowEtaPhiGrid, + ).toHaveBeenCalledWith(VALUE); + }); + + it('should set cartesian grid', () => { + const VALUE = false; + const event = new MatCheckboxChange(); + event.checked = VALUE; + + component.setCartesianGrid(event); + + expect(component.showCartesianGrid).toBe(false); + expect( + mockEventDisplay.getUIManager().setShowCartesianGrid, + ).toHaveBeenCalledWith(VALUE, component.scale); + }); + + it('should show labels', () => { + const VALUE = false; + const event = new MatCheckboxChange(); + event.checked = VALUE; + + component.showLabels(event); + + expect(mockEventDisplay.getUIManager().showLabels).toHaveBeenCalledWith( + VALUE, + ); + }); + + it('should show 3D mouse points', () => { + const VALUE = true; + const event = new MatCheckboxChange(); + event.checked = VALUE; + + component.show3DMousePoints(event); + + expect(component.show3DPoints).toBe(VALUE); + expect( + mockEventDisplay.getUIManager().show3DMousePoints, + ).toHaveBeenCalledWith(component.show3DPoints); + }); + + it('should toggle the show-distance function', () => { + const VALUE = true; + const event = new MatCheckboxChange(); + event.checked = VALUE; + + component.toggleShowDistance(event); + + expect(mockEventDisplay.getUIManager().show3DDistance).toHaveBeenCalledWith( + VALUE, + ); + }); + + it('should unsubscribe the existing subscriptions', () => { + component.sub = new Subscription(); + const spy = jest.spyOn(component.sub, 'unsubscribe'); + + component.ngOnDestroy(); + + expect(spy).toHaveBeenCalledTimes(1); + }); +}); diff --git a/firebird-ng/src/app/components/view-options/view-options.component.ts b/firebird-ng/src/app/components/view-options/view-options.component.ts new file mode 100644 index 0000000..eb5eaee --- /dev/null +++ b/firebird-ng/src/app/components/view-options/view-options.component.ts @@ -0,0 +1,108 @@ +import { + Component, + type OnInit, + type OnDestroy, + ViewChild, +} from '@angular/core'; +import { PresetView } from 'phoenix-event-display'; +import {MatCheckbox, MatCheckboxChange} from '@angular/material/checkbox'; +import {EventDisplayService} from "phoenix-ui-components"; +import { MatDialog } from '@angular/material/dialog'; +import { CartesianGridConfigComponent } from './cartesian-grid-config/cartesian-grid-config.component'; +import { Subscription } from 'rxjs'; +import { Vector3 } from 'three'; +import {MatMenu, MatMenuItem, MatMenuTrigger} from '@angular/material/menu'; +import {NgForOf, NgIf} from "@angular/common"; +import {MenuToggleComponent} from "../menu-toggle/menu-toggle.component"; + +@Component({ + selector: 'app-custom-view-options', + templateUrl: './view-options.component.html', + styleUrls: ['./view-options.component.scss'], + imports: [ + MatMenu, + MatCheckbox, + MatMenuItem, + NgForOf, + MenuToggleComponent, + MatMenuTrigger, + NgIf + ], + standalone: true +}) +export class ViewOptionsComponent implements OnInit, OnDestroy { + @ViewChild(MatMenuTrigger) trigger!: MatMenuTrigger; + showCartesianGrid: boolean = false; + scale: number = 3000; + views!: PresetView[]; + show3DPoints!: boolean; + origin: Vector3 = new Vector3(0, 0, 0); + sub!: Subscription; + + constructor( + private eventDisplay: EventDisplayService, + private dialog: MatDialog, + ) {} + + ngOnInit(): void { + this.views = this.eventDisplay.getUIManager().getPresetViews(); + this.sub = this.eventDisplay + .getThreeManager() + .originChanged.subscribe((intersect) => { + this.origin = intersect; + }); + } + + openCartesianGridConfigDialog() { + this.dialog.open(CartesianGridConfigComponent, { + data: { + gridVisible: this.showCartesianGrid, + scale: this.scale, + }, + position: { + bottom: '5rem', + left: '3rem', + }, + }); + } + + displayView($event: any, view: PresetView) { + $event.stopPropagation(); + this.eventDisplay.getUIManager().displayView(view); + } + + setAxis(change: MatCheckboxChange) { + const value = change.checked; + this.eventDisplay.getUIManager().setShowAxis(value); + } + + setEtaPhiGrid(change: MatCheckboxChange) { + const value = change.checked; + this.eventDisplay.getUIManager().setShowEtaPhiGrid(value); + } + + setCartesianGrid(change: MatCheckboxChange) { + this.showCartesianGrid = change.checked; + this.eventDisplay + .getUIManager() + .setShowCartesianGrid(this.showCartesianGrid, this.scale); + } + + showLabels(change: MatCheckboxChange) { + this.eventDisplay.getUIManager().showLabels(change.checked); + } + + show3DMousePoints(change: MatCheckboxChange) { + this.show3DPoints = change.checked; + this.eventDisplay.getUIManager().show3DMousePoints(this.show3DPoints); + } + + toggleShowDistance(change: MatCheckboxChange) { + this.trigger.closeMenu(); + this.eventDisplay.getUIManager().show3DDistance(change.checked); + } + + ngOnDestroy(): void { + this.sub.unsubscribe(); + } +} diff --git a/firebird-ng/src/app/pages/main-display/main-display.component.html b/firebird-ng/src/app/pages/main-display/main-display.component.html index aaa711c..1f337bf 100644 --- a/firebird-ng/src/app/pages/main-display/main-display.component.html +++ b/firebird-ng/src/app/pages/main-display/main-display.component.html @@ -41,27 +41,27 @@ - + - + - + - + - - + + - - + + - - + + - - + + diff --git a/firebird-ng/src/app/pages/main-display/main-display.component.ts b/firebird-ng/src/app/pages/main-display/main-display.component.ts index a444f2a..89c8f1b 100644 --- a/firebird-ng/src/app/pages/main-display/main-display.component.ts +++ b/firebird-ng/src/app/pages/main-display/main-display.component.ts @@ -4,8 +4,7 @@ import {HttpClient, HttpClientModule} from '@angular/common/http'; import { EventDataFormat, EventDataImportOption, - EventDisplayService, - PhoenixUIModule + EventDisplayService } from 'phoenix-ui-components'; import {ClippingSetting, Configuration, PhoenixLoader, PhoenixMenuNode, PresetView} from 'phoenix-event-display'; import * as THREE from 'three'; @@ -50,6 +49,10 @@ import {AppComponent} from "../../app.component"; import {ToolPanelComponent} from "../../components/tool-panel/tool-panel.component"; import {NavConfigComponent} from "../../components/nav-config/nav-config.component"; import {UrlService} from "../../services/url.service"; +import {EventSelectorComponent} from "../../components/event-selector/event-selector.component"; +import {AutoRotateComponent} from "../../components/auto-rotate/auto-rotate.component"; +import {DarkThemeComponent} from "../../components/dark-theme/dark-theme.component"; +import {ObjectClippingComponent} from "../../components/object-clipping/object-clipping.component"; // import { LineMaterial } from 'three/addons/lines/LineMaterial.js'; @@ -58,7 +61,7 @@ import {UrlService} from "../../services/url.service"; @Component({ selector: 'app-test-experiment', templateUrl: './main-display.component.html', - imports: [PhoenixUIModule, IoOptionsComponent, MatSlider, MatIcon, MatButton, MatSliderThumb, DecimalPipe, MatTooltip, MatFormField, MatSelect, MatOption, NgForOf, AngularSplitModule, SceneTreeComponent, NgClass, MatIconButton, DisplayShellComponent, ToolPanelComponent, NavConfigComponent, NgIf], + imports: [IoOptionsComponent, MatSlider, MatIcon, MatButton, MatSliderThumb, DecimalPipe, MatTooltip, MatFormField, MatSelect, MatOption, NgForOf, AngularSplitModule, SceneTreeComponent, NgClass, MatIconButton, DisplayShellComponent, ToolPanelComponent, NavConfigComponent, NgIf, EventSelectorComponent, AutoRotateComponent, DarkThemeComponent, ObjectClippingComponent], standalone: true, styleUrls: ['./main-display.component.scss'] })
+ Click on a point to shift the grid. Keep clicking at various points to + continue shifting. Right click to stop. +