1// Copyright 2021 The Cirq Developers
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//      https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15import {Group, Vector3} from 'three';
16import {
17  BoxGate3DSymbol,
18  ConnectionLine,
19  Control3DSymbol,
20  Swap3DSymbol,
21  X3DSymbol,
22} from './meshes';
23
24export interface SymbolInformation {
25  readonly wire_symbols: string[];
26  readonly location_info: Coord[];
27  readonly color_info: string[];
28  readonly moment: number;
29}
30
31export interface Coord {
32  readonly row: number;
33  readonly col: number;
34}
35
36/**
37 * Builds a 3D symbol representing a Cirq `Operation`.
38 */
39export class Symbol3D extends Group {
40  readonly moment: number;
41  private padding_factor: number;
42
43  /**
44   * Class constructor.
45   * @param symbol_info A typed object with information instructing
46   * the class on how to build the mesh.
47   * @param padding_factor A number scaling the distance between meshes.
48   */
49  constructor(symbol_info: SymbolInformation, padding_factor = 1) {
50    super();
51    this.padding_factor = padding_factor;
52    this.moment = symbol_info.moment;
53    this.buildAndAddMeshesToGroup(symbol_info);
54
55    // If this is a multi-qubit operation, we automatically
56    // add lines connecting the symbols.
57    this.addConnectionLines(symbol_info);
58  }
59
60  private buildAndAddMeshesToGroup(symbol_info: SymbolInformation) {
61    const locationInfo = symbol_info.location_info;
62
63    symbol_info.wire_symbols.forEach((symbol, index) => {
64      let mesh;
65      switch (symbol) {
66        case 'X':
67          mesh = new X3DSymbol(symbol_info.color_info[index]);
68          break;
69        case '@':
70          mesh = new Control3DSymbol();
71          break;
72        case '×':
73          mesh = new Swap3DSymbol();
74          break;
75        default:
76          mesh = new BoxGate3DSymbol(symbol, symbol_info.color_info[index]);
77      }
78
79      mesh.position.set(
80        locationInfo[index].row * this.padding_factor,
81        symbol_info.moment * this.padding_factor,
82        locationInfo[index].col * this.padding_factor
83      );
84      this.add(mesh);
85    });
86  }
87
88  private addConnectionLines(symbol_info: SymbolInformation) {
89    const locationInfo = symbol_info.location_info;
90
91    if (locationInfo.length > 1) {
92      let i = 0;
93      while (i < locationInfo.length - 1) {
94        const coords = [
95          new Vector3(
96            locationInfo[i].row * this.padding_factor,
97            symbol_info.moment * this.padding_factor,
98            locationInfo[i].col * this.padding_factor
99          ),
100          new Vector3(
101            locationInfo[i + 1].row * this.padding_factor,
102            symbol_info.moment * this.padding_factor,
103            locationInfo[i + 1].col * this.padding_factor
104          ),
105        ];
106        this.add(new ConnectionLine(coords[0], coords[1]));
107        i++;
108      }
109    }
110  }
111}
112