-
-
Notifications
You must be signed in to change notification settings - Fork 8
/
Arrow3D.ts
176 lines (144 loc) · 5.48 KB
/
Arrow3D.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
import { Object3D, Color, Vector3, ArrowHelper, Sphere } from 'three';
import {
Object,
isMinimized,
FileCalculationContext,
} from '@casual-simulation/aux-common';
import { AuxFile3D } from './AuxFile3D';
import { ContextGroup3D } from './ContextGroup3D';
import { BuilderGroup3D } from './BuilderGroup3D';
import { disposeMaterial } from './SceneUtils';
export class Arrow3D extends Object3D {
public static DefaultColor: Color = new Color(1, 1, 1);
public static DefaultHeadWidth = 0.15;
public static DefaultHeadLength = 0.3;
/**
* Three JS helper that draws arrows.
*/
private _arrowHelper: ArrowHelper;
/**
* The file that this arrow is coming from.
*/
private _sourceFile3d: AuxFile3D;
/**
* Determines weather to draw the arrow with an arrow tip or not
*/
private _hasArrowTip: boolean;
/**
* The file that this arrow is pointing towards.
*/
private _targetFile3d: AuxFile3D;
public get sourceFile3d() {
return this._sourceFile3d;
}
public get targetFile3d() {
return this._targetFile3d;
}
constructor(sourceFile3d: AuxFile3D, targetFile3d: AuxFile3D) {
super();
this._sourceFile3d = sourceFile3d;
this._targetFile3d = targetFile3d;
// Create the arrow mesh.
this._arrowHelper = new ArrowHelper(
new Vector3(0, 0, 0),
new Vector3(0, 0, 0),
0,
Arrow3D.DefaultColor.getHex()
);
this.add(this._arrowHelper);
}
/**
* Set the origin of the arrow.
*/
public setOrigin(origin: Vector3, isWorldspace?: boolean) {
if (isWorldspace) {
this._arrowHelper.position.copy(this.worldToLocal(origin.clone()));
} else {
this._arrowHelper.position.copy(origin);
}
}
public setColor(color?: Color) {
if (!this._arrowHelper) return;
if (color) {
this._arrowHelper.setColor(color);
} else {
this._arrowHelper.setColor(Arrow3D.DefaultColor);
}
}
public setTipState(hasTip: boolean) {
this._hasArrowTip = hasTip;
}
public setLength(length: number) {
if (!this._arrowHelper) return;
let headLength = Arrow3D.DefaultHeadLength;
let headWidth = Arrow3D.DefaultHeadWidth;
if (length < headLength) {
headLength = undefined;
headWidth = undefined;
}
this._arrowHelper.setLength(length, headLength, headWidth);
}
public update(calc: FileCalculationContext) {
if (!this._arrowHelper) return;
let sourceWorkspace = this._getWorkspace(this._sourceFile3d);
let targetWorkspace = this._getWorkspace(this._targetFile3d);
const sourceMinimized =
sourceWorkspace && isMinimized(calc, sourceWorkspace.file);
const targetMinimized =
targetWorkspace && isMinimized(calc, targetWorkspace.file);
if (sourceMinimized && targetMinimized) {
// The workspace of both the source file and target file are minimized. Hide arrow and do nothing else.
this._arrowHelper.visible = false;
} else {
this._arrowHelper.visible = true;
// Update arrow origin.
if (sourceWorkspace instanceof BuilderGroup3D && sourceMinimized) {
let miniHexSphere =
sourceWorkspace.surface.miniHex.boundingSphere;
this.setOrigin(miniHexSphere.center, true);
} else {
let sourceSphere = this._sourceFile3d.boundingSphere;
this.setOrigin(sourceSphere.center, true);
}
// Update arrow direction and length.
let targetSphere: Sphere;
// Lets get the bounding sphere of the target.
// This could be either the sphere of the file itself or the sphere of the minimized workspace the file is on.
if (targetWorkspace instanceof BuilderGroup3D && targetMinimized) {
targetSphere = targetWorkspace.surface.miniHex.boundingSphere;
} else {
targetSphere = this._targetFile3d.boundingSphere;
}
let targetCenterLocal = this.worldToLocal(
targetSphere.center.clone()
);
let dir = targetCenterLocal.clone().sub(this._arrowHelper.position);
if (!this._hasArrowTip) {
this._arrowHelper.cone.visible = false;
dir.setLength(dir.length() + 0.2);
} else {
this._arrowHelper.cone.visible = true;
// Decrease length of direction vector so that it only goes
// as far as the hull of the target bounding sphere.
dir.setLength(dir.length() - targetSphere.radius);
}
let length = dir.length();
this._arrowHelper.setDirection(dir.normalize());
this.setLength(length);
}
this.updateMatrixWorld(true);
}
public dispose() {
this.remove(this._arrowHelper);
this._arrowHelper.line.geometry.dispose();
disposeMaterial(this._arrowHelper.line.material);
this._arrowHelper.cone.geometry.dispose();
disposeMaterial(this._arrowHelper.cone.material);
this._arrowHelper = null;
this._sourceFile3d = null;
this._targetFile3d = null;
}
private _getWorkspace(file3d: AuxFile3D): ContextGroup3D {
return file3d.contextGroup;
}
}