File

src/app/modules/content/content.component.ts

Description

Main content component

Implements

OnInit OnDestroy

Metadata

Index

Properties
Methods
Inputs
HostBindings

Constructor

constructor(model: ModelState, page: PageState, registration: RegistrationState, scene: SceneState, rootRef: ElementRef, cdr: ChangeDetectorRef)

Creates an instance of content component.

Parameters :
Name Type Optional Description
model ModelState No

The model state

page PageState No

The page state

registration RegistrationState No

The registration state

scene SceneState No
rootRef ElementRef<HTMLElement> No

Component's root element

cdr ChangeDetectorRef No

Change detector

Inputs

disablePositionChange
Type : boolean
Default value : false

HostBindings

class
Type : "ccf-content"
Default value : 'ccf-content'

HTML class name

Methods

handleNodeDrag
handleNodeDrag(event: NodeDragEvent)
Parameters :
Name Type Optional
event NodeDragEvent No
Returns : void
resetStage
resetStage()

Method to reset registration block, crosshairs, and x,y,z information. Resets to initial registration state if provided

Returns : void
setViewType
setViewType(is3DView: boolean)

Sets view type

Parameters :
Name Type Optional Description
is3DView boolean No

Set view type to '3d' if this is true otherwise set it to 'register'

Returns : void

Properties

Readonly bounds$
Default value : this.model.organDimensions$.pipe( map((dims) => ({ x: Math.max(dims.x, this.model.defaultPosition.x + 40) / 1000, y: Math.max(dims.y, this.model.defaultPosition.y + 40) / 1000, z: Math.max(dims.z, this.model.defaultPosition.z + 40) / 1000, })), distinctUntilKeyChanged('x'), distinctUntilKeyChanged('y'), )
Readonly clsName
Type : string
Default value : 'ccf-content'
Decorators :
@HostBinding('class')

HTML class name

debugMode
Default value : false

Shows / hides the state debug component for testing purposes.

Readonly is3DView$
Default value : this.model.viewType$.pipe(map((type) => type === '3d'))

Whether the view type is 3d or register

isNarrowView
Default value : false

Whether the content area is very narrow

Readonly position$
Default value : this.model.position$.pipe( map((p) => ({ x: Math.floor(p.x), y: Math.floor(p.y), z: Math.floor(p.z) })), )
showDebugButtons
Default value : !environment.production

Show debug buttons of content component

import {
  ChangeDetectionStrategy,
  ChangeDetectorRef,
  Component,
  ElementRef,
  HostBinding,
  Input,
  OnDestroy,
  OnInit,
} from '@angular/core';
import { NodeDragEvent } from 'ccf-body-ui';
import { ResizeSensor } from 'css-element-queries';
import { distinctUntilKeyChanged, map } from 'rxjs/operators';

import { environment } from '../../../environments/environment';
import { ModelState } from '../../core/store/model/model.state';
import { PageState } from '../../core/store/page/page.state';
import { RegistrationState } from '../../core/store/registration/registration.state';
import { SceneState } from '../../core/store/scene/scene.state';

/**
 * Main content component
 */
@Component({
  selector: 'ccf-content',
  templateUrl: './content.component.html',
  styleUrls: ['./content.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ContentComponent implements OnInit, OnDestroy {
  /** HTML class name */
  @HostBinding('class') readonly clsName = 'ccf-content';

  @Input() disablePositionChange = false;

  readonly position$ = this.model.position$.pipe(
    map((p) => ({ x: Math.floor(p.x), y: Math.floor(p.y), z: Math.floor(p.z) })),
  );

  /** Whether the view type is 3d or register */
  readonly is3DView$ = this.model.viewType$.pipe(map((type) => type === '3d'));

  readonly bounds$ = this.model.organDimensions$.pipe(
    map((dims) => ({
      x: Math.max(dims.x, this.model.defaultPosition.x + 40) / 1000,
      y: Math.max(dims.y, this.model.defaultPosition.y + 40) / 1000,
      z: Math.max(dims.z, this.model.defaultPosition.z + 40) / 1000,
    })),
    distinctUntilKeyChanged('x'),
    distinctUntilKeyChanged('y'),
  );

  /** Whether the content area is very narrow */
  isNarrowView = false;

  /**
   * Shows / hides the state debug component for testing purposes.
   */
  debugMode = false;

  /**
   * Show debug buttons of content component
   */
  showDebugButtons = !environment.production;

  /** Resize detection */
  private sensor!: ResizeSensor;

  /**
   * Creates an instance of content component.
   *
   * @param model The model state
   * @param page The page state
   * @param registration The registration state
   * @param rootRef Component's root element
   * @param cdr Change detector
   */
  constructor(
    readonly model: ModelState,
    readonly page: PageState,
    readonly registration: RegistrationState,
    readonly scene: SceneState,
    private readonly rootRef: ElementRef<HTMLElement>,
    private readonly cdr: ChangeDetectorRef,
  ) {}

  /**
   * Sets up the resize sensor
   */
  ngOnInit(): void {
    this.sensor = new ResizeSensor(this.rootRef.nativeElement, ({ width }) => {
      const isNarrowView = width < 440; // 27.5rem
      if (this.isNarrowView !== isNarrowView) {
        this.isNarrowView = isNarrowView;
        this.cdr.markForCheck();
      }
    });
  }

  /**
   * Detaches the resize sensor
   */
  ngOnDestroy(): void {
    this.sensor.detach();
  }

  /**
   * Sets view type
   *
   * @param is3DView Set view type to '3d' if this is true otherwise set it to 'register'
   */
  setViewType(is3DView: boolean): void {
    this.model.setViewType(is3DView ? '3d' : 'register');
  }

  /**
   * Method to reset registration block, crosshairs, and x,y,z information.
   * Resets to initial registration state if provided
   */
  resetStage(): void {
    if (this.registration.snapshot.initialRegistration) {
      this.registration.setToInitialRegistration();
    } else {
      this.model.setOrganDefaults();
    }
    this.model.setViewSide('anterior');
    this.model.setViewType('register');
  }

  handleNodeDrag(event: NodeDragEvent): void {
    if (event.node['@id'] === '#DraftPlacement') {
      if (event.info.coordinate) {
        const [a, b] = (event.info.coordinate as number[]).map((n) => n * 1000) as [number, number];
        const { position, viewSide, organDimensions } = this.model.snapshot;
        const dims = [organDimensions.x, organDimensions.y, organDimensions.z].map((n) => n / 2);
        let newPosition = position;
        switch (viewSide) {
          case 'anterior':
            newPosition = { x: a + dims[0], y: b + dims[1], z: position.z };
            break;
          case 'posterior':
            newPosition = { x: -a + dims[0], y: b + dims[1], z: position.z };
            break;
          case 'left':
            newPosition = { x: position.x, y: b + dims[1], z: -a + dims[2] };
            break;
          case 'right':
            newPosition = { x: position.x, y: b + dims[1], z: a + dims[2] };
            break;
        }
        this.model.setPosition(newPosition);
      }
    }
  }
}
<div class="top-bar">
  <ccf-stage-nav
    [useDropdownMenu]="isNarrowView"
    [view3D]="(is3DView$ | async) ?? false"
    [side]="(model.viewSide$ | async)!"
    (view3DChange)="setViewType($event)"
    (sideChange)="model.setViewSide($any($event))"
  >
  </ccf-stage-nav>
  <mat-icon
    matRipple
    [matRippleCentered]="true"
    [matRippleUnbounded]="true"
    matRippleColor="rgba(204, 204, 204, 0.25)"
    class="icon reset"
    (click)="resetStage()"
    >refresh</mat-icon
  >
</div>

<div class="main-content">
  <ccf-body-ui
    class="body-ui"
    *ngIf="(model.viewType$ | async) === '3d'"
    [scene]="(scene.nodes$ | async) ?? []"
    [rotation]="(scene.rotation$ | async) ?? 0"
    (rotationChange)="gizmo.rotation = $event[0]; gizmo.rotationX = $event[1]"
    [bounds]="(bounds$ | async)!"
    [zoom]="11.5"
    [interactive]="true"
    camera="perspective"
  ></ccf-body-ui>

  <ccf-body-ui
    class="body-ui"
    *ngIf="(model.viewType$ | async) === 'register'"
    [scene]="(scene.rotatedNodes$ | async) ?? []"
    [interactive]="false"
    [bounds]="(bounds$ | async)!"
    [zoom]="11.5"
    (nodeDrag)="handleNodeDrag($event)"
    camera="orthographic"
  ></ccf-body-ui>
  <div class="gizmo-area">
    <ccf-body-ui
      class="gizmo"
      [scene]="(scene.gizmo$ | async)!"
      [rotation]="(scene.rotation$ | async) ?? 0"
      [interactive]="false"
      [zoom]="9.5"
      #gizmo
    ></ccf-body-ui>
    <ccf-spatial-search-keyboard-ui-behavior
      *ngIf="page.registrationStarted$ | async"
      [delta]="1"
      [shiftDelta]="2"
      [position]="(position$ | async)!"
      [disablePositionChange]="disablePositionChange"
    ></ccf-spatial-search-keyboard-ui-behavior>
    <div class="position-display">
      <div class="position">
        <div class="x">X: {{ (position$ | async)?.x }}</div>
        <div class="y">Y: {{ (position$ | async)?.y }}</div>
        <div class="z">Z: {{ (position$ | async)?.z }}</div>
      </div>
    </div>
  </div>
</div>

<ccf-store-debug *ngIf="showDebugButtons && debugMode" class="debug"> </ccf-store-debug>
<div
  *ngIf="showDebugButtons"
  style="
    position: absolute;
    bottom: 1rem;
    display: flex;
    flex-direction: row;
    justify-content: space-between;
    width: 7rem;
  "
>
  <button style="width: 3rem; font-size: 0.7rem" (click)="debugMode = !debugMode">State</button>
  <button (click)="registration.setUseRegistrationCallback(true)">Use callback</button>
  <button (click)="registration.setUseRegistrationCallback(false)">Use download</button>
</div>

./content.component.scss

:host {
  display: block;
  width: 100%;
  height: 100%;
  padding: 0.5rem;
  overflow: hidden;

  .top-bar {
    display: flex;
    justify-content: space-between;
    align-items: center;

    .reset {
      transform: scaleX(-1);
      cursor: pointer;
      transition: 0.6s;
      font-size: 2rem;
      height: 2rem;
      width: 2rem;
    }
  }

  .sidebar {
    .minimap-scene {
      margin: 1.5rem;
      width: 12.75rem;
      height: 11rem;
      ::ng-deep .body-ui {
        background-color: #232f3a;
      }
    }

    ccf-spatial-search-keyboard-ui-behavior {
      margin: 1.5rem;
      display: flex;
      justify-content: center;
    }

    ccf-xyz-position {
      margin: 1.5rem;
      padding-left: 5rem;
    }
  }

  .main-content {
    display: flex;
    height: calc(100% - 1rem);

    .body-ui {
      flex: auto;
      transition: opacity 1s;
    }

    .gizmo-area {
      width: 7rem;
      margin-top: 1.5rem;
      margin-left: 1.5rem;
      z-index: 10;
      right: 1.5rem;

      .position-display {
        display: flex;
        justify-content: center;
        margin-bottom: 1.5rem;
      }

      .gizmo {
        height: 5.5rem;
        .hidden {
          opacity: 0;
          z-index: 0;
        }
      }
    }
  }

  // Temporary for displaying dev information
  .ccf-store-debug {
    color: white;
    position: absolute;
    top: 0.5rem;
    max-width: 55rem;
  }
}
Legend
Html element
Component
Html element with directive

results matching ""

    No results matching ""