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-pyramid-sprite')
7export class PyramidSprite 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      // TODO(b/294574192): Uncommenting below causes significant render lag
87      // transform-style: preserve-3d;
88      transform: translateZ(calc(var(--posZ) * 1px));
89      cursor: move;
90    }
91    .pyramid {
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    .pyramid > div {
102      background-color: var(--color);
103      position: absolute;
104      width: 100%;
105      height: 100%;
106      clip-path: polygon(50% 0, 100% 100%, 0 100%);
107      box-shadow: 0 0 0.25em #000 inset;
108      opacity: 0.7;
109    }
110    .pyramid > div:nth-child(1) {
111      transform: rotateX(30deg);
112    }
113    .pyramid > div:nth-child(2) {
114      transform: translate3d(0.25em, 0, -0.25em) rotateY(90deg) rotateX(30deg);
115    }
116    .pyramid > div:nth-child(3) {
117      transform: translate3d(0, 0, -0.5em) rotateY(180deg) rotateX(30deg);
118    }
119    .pyramid > div:nth-child(4) {
120      transform: translate3d(-0.25em, 0, -0.25em) rotateY(270deg) rotateX(30deg);
121    }
122
123    .line {
124      position: absolute;
125      border-bottom: 5px dashed;
126      width: calc(var(--posZ) * 1px);
127      top: 50%;
128      left: 50%;
129      transform: rotateY(90deg) rotateX(90deg);
130      transform-origin: left;
131    }
132
133    .base {
134      position: absolute;
135      border: 5px solid;
136      border-radius: 50%;
137      background-color: black;
138      height: 5px;
139      width: 5px;
140      top: 50%;
141      left: 50%;
142      transform: translate3d(-50%, -50%, calc(var(--posZ) * -1px));
143    }
144  `;
145  private handleOrientationEvent = (e: Event) => {
146    const {detail} = e as CustomEvent;
147    if (detail.name === this.id && this.controls) {
148      if (detail.type === 'yaw') {
149        this.yaw = detail.value;
150      } else if (detail.type === 'pitch') {
151        this.pitch = detail.value;
152      } else {
153        this.roll = detail.value;
154      }
155    }
156  };
157
158  render() {
159    return html`
160      <style>
161        :host {
162          font-size: ${this.size};
163          --color: ${this.color};
164          --yaw: ${this.yaw};
165          --pitch: ${this.pitch};
166          --roll: ${this.roll};
167          --posZ: ${this.controls ? this.posZ : 0};
168        }
169        .pyramid > div {
170          outline: ${this.highlighted && this.controls ? css`dashed` : css``};
171        }
172      </style>
173      <div class="pyramid">
174        <div class="face"></div>
175        <div class="face"></div>
176        <div class="face"></div>
177        <div class="face"></div>
178      </div>
179      ${
180        this.controls ? html`
181            <div class="line"></div>
182            <div class="base"></div>
183          ` :
184                        html``}
185    `;
186  }
187}
188