1import {css, html, LitElement} from 'lit';
2import {customElement, property} from 'lit/decorators.js';
3
4import {Notifiable, SimulationInfo, simulationState,} from './device-observer.js';
5
6@customElement('ns-cube-sprite')
7export class CubeSprite extends LitElement implements Notifiable {
8  /**
9   * the yaw value in orientation for ns-cube-sprite
10   * unit: deg
11   */
12  @property({type: Number}) yaw = -15;
13
14  /**
15   * the pitch value in orientation for ns-cube-sprite
16   * unit: deg
17   */
18  @property({type: Number}) pitch = -15;
19
20  /**
21   * the roll value in orientation for ns-cube-sprite
22   * unit: deg
23   */
24  @property({type: Number}) roll = 0;
25
26  /**
27   * the z value in position for ns-cube-sprite
28   * unit: cm
29   */
30  @property({type: Number}) posZ = 0;
31
32  /**
33   * the css value for color
34   */
35  @property({type: css, attribute: 'color'}) color = css`red`;
36
37  /**
38   * the css value for size
39   */
40  @property({type: css, attribute: 'size'}) size = css`30px`;
41
42  /**
43   * A Boolean property; if set true, the user would
44   * be able to control the cube's pitch, yaw, and roll
45   * with the info panel.
46   */
47  @property({type: Boolean}) controls = false;
48
49  /**
50   * A Boolean property; if set true, the box is selected
51   * therefore the outline gets dotted.
52   */
53  @property({type: Boolean}) highlighted = false;
54
55  connectedCallback() {
56    super.connectedCallback();  // eslint-disable-line
57    simulationState.registerObserver(this);
58    window.addEventListener('orientationEvent', this.handleOrientationEvent);
59  }
60
61  disconnectedCallback() {
62    window.removeEventListener('orientationEvent', this.handleOrientationEvent);
63    simulationState.removeObserver(this);
64    super.disconnectedCallback();  // eslint-disable-line
65  }
66
67  onNotify(data: SimulationInfo) {
68    this.highlighted = data.selectedId === this.id;
69    for (const device of data.devices) {
70      if (device.name === this.id) {
71        this.posZ = device.position.z * 100;
72        return;
73      }
74    }
75  }
76
77  static styles = css`
78    :host {
79      /** all sizes are relative to font-size **/
80      display: block;
81      min-height: 1.5em;
82      min-width: 1.5em;
83      width: 1em;
84      /*  overflow: hidden; */
85      transform-origin: center;
86      transform-style: preserve-3d;
87      transform: translateZ(calc(var(--posZ) * 1px));
88      cursor: move;
89    }
90
91    .cube {
92      transform-style: preserve-3d;
93      transform: rotateX(calc(var(--yaw) * 1deg))
94        rotateY(calc(var(--pitch) * 1deg)) rotateZ(calc(var(--roll) * 1deg));
95      position: absolute;
96      left: 0.25em;
97      bottom: 0.25em;
98      width: 1em;
99      height: 1em;
100    }
101    .cube > div {
102      position: absolute;
103      background-color: var(--color);
104      width: 100%;
105      height: 100%;
106      box-shadow: 0 0 0.25em #000 inset;
107    }
108    .cube > div:nth-child(1) {
109      transform: translateZ(0.5em);
110    }
111    .cube > div:nth-child(2) {
112      transform: rotateY(180deg) translateZ(0.5em);
113    }
114    .cube > div:nth-child(3) {
115      right: 0;
116      width: 1em;
117      transform: rotateY(90deg) translateZ(0.5em);
118    }
119    .cube > div:nth-child(4) {
120      width: 1em;
121      transform: rotateY(270deg) translateZ(0.5em);
122    }
123    .cube > div:nth-child(5) {
124      bottom: -0.5em;
125      height: 1em;
126      transform: rotateX(90deg);
127      box-shadow: 0 0 0.25em #000 inset, 0 0 0.25em #000;
128    }
129    .cube div:nth-child(6) {
130      height: 1em;
131      transform: translateY(-0.5em) rotateX(90deg);
132      overflow: hidden;
133    }
134
135    .line {
136      position: absolute;
137      border-bottom: 5px dashed;
138      width: calc(var(--posZ) * 1px);
139      top: 50%;
140      left: 50%;
141      transform: rotateY(90deg) rotateX(90deg);
142      transform-origin: left;
143    }
144
145    .base {
146      position: absolute;
147      border: 5px solid;
148      border-radius: 50%;
149      background-color: black;
150      height: 5px;
151      width: 5px;
152      top: 50%;
153      left: 50%;
154      transform: translate3d(-50%, -50%, calc(var(--posZ) * -1px));
155    }
156  `;
157
158  private handleOrientationEvent = (e: Event) => {
159    const {detail} = e as CustomEvent;
160    if (detail.name === this.id && this.controls) {
161      if (detail.type === 'yaw') {
162        this.yaw = detail.value;
163      } else if (detail.type === 'pitch') {
164        this.pitch = detail.value;
165      } else {
166        this.roll = detail.value;
167      }
168    }
169  };
170
171  render() {
172    // TODO(b/255635486): Make cube easily scalable with user input size
173    return html`
174      <style>
175        :host {
176          font-size: ${this.size};
177          --color: ${this.color};
178          --yaw: ${this.yaw};
179          --pitch: ${this.pitch};
180          --roll: ${this.roll};
181          --posZ: ${this.controls ? this.posZ : 0};
182        }
183        .cube > div {
184          outline: ${this.highlighted && this.controls ? css`dashed` : css``};
185        }
186      </style>
187      <div class="cube">
188        <div></div>
189        <div></div>
190        <div></div>
191        <div></div>
192        <div></div>
193        <div></div>
194      </div>
195      ${
196        this.controls ? html`
197            <div class="line"></div>
198            <div class="base"></div>
199          ` :
200                        html``}
201    `;
202  }
203}
204