-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcoordinate.js
More file actions
170 lines (147 loc) · 6.05 KB
/
Copy pathcoordinate.js
File metadata and controls
170 lines (147 loc) · 6.05 KB
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
// CoordinateSystem
class CoordinateSystem {
// A CoordinateSystem has 3 numbers describing how much its x, y, and z axes
// have been rotated relative to the world system's axes, and a Coordinate
// describing its origin's coordinate in the world system.
// NOTES:
// - Mathematically, the CoordinateSystem is created by rotating the axes
// first, then translating the origin. The arguments' order reflects this.
// - CoordinateSystems are right-handed.
// - The origin Coordinate must be specified relative to the world system.
constructor(xRot, yRot, zRot, origin) {
this.xRot = xRot;
this.yRot = yRot;
this.zRot = zRot;
if (origin.system != null) {
throw new Error("CoordinateSystem's origin Coordinate isn't given relative to the world system.");
}
this.origin = origin;
}
// Rotate the CoordinateSystem about its own x axis by the specified angle
// (positive direction given by the right-hand rule).
rotateX(angle) {
this.yRot += angle;
this.zRot += angle;
}
// Rotate the CoordinateSystem about its own y axis by the specified angle
// (positive direction given by the right-hand rule).
rotateY(angle) {
this.xRot += angle;
this.zRot += angle;
}
// Rotate the CoordinateSystem about its own z axis by the specified angle
// (positive direction given by the right-hand rule).
rotateZ(angle) {
this.xRot += angle;
this.yRot += angle;
}
// Find where the line segment between two Coordinates in the current
// CoordinateSystem intersects the plane z = -planeZ. Return the Coordinate
// of that intersection if there is a single intersection point; otherwise
// return null.
findLinePlaneIntersection(c1, c2, planeZ) {
// both Coordinates must lie in the current CoordinateSystem
if (c1.system != this) {
throw new Error("The first coordinate passed into" +
" CoordinateSystem.findLinePlaneIntersection is not expressed" +
" in terms of the correct CoordinateSystem.");
}
if (c2.system != this) {
throw new Error("The second coordinate passed into" +
" CoordinateSystem.findLinePlaneIntersection is not expressed" +
" in terms of the correct CoordinateSystem.");
}
// if line segment doesn't intersect plane in exactly 1 place...
if ((c1.z > 0 && c2.z > 0) || (c1.z == 0 && c2.z == 0) ||
(c1.z < 0 && c2.z < 0)) {
return null;
} else { // line segment intersects plane in exactly 1 place
// Call the intersection point cMid.
// The equations relating the 3 Coordinates' positions are:
// 1: cMid.x = c1.x + m(c2.x - c1.x)
// 2: cMid.y = c1.y + m(c2.y - c1.y)
// 3: cMid.z = c1.z + m(c2.z - c1.z)
// First, rearrange 3 to solve for m, noting that cMid.z = -planeZ
const m = (-planeZ - c1.z) / (c2.z - c1.z);
// Then plug m into 1 and 2 to solve for its x and y coordinates
return new Coordinate(c1.x + m*(c2.x - c1.x),
c1.y + m*(c2.y - c1.y), -planeZ, this)
}
}
class Coordinate {
// A Coordinate has a position (x, y, z) in some CoordinateSystem.
// If the CoordinateSystem is not specified, it is assumed to be the
// world system.
constructor(x, y, z, system = null) {
this.x = x;
this.y = y;
this.z = z;
this.system = system;
}
// Return the Coordinate's position in its current CoordinateSystem
// as a PositionVector.
// This format is useful for switching between coordinate systems.
getPositionVector() {
return new PositionVector(this.x, this.y, this.z);
}
// Return the Coordinate's position in the specified CoordinateSystem
// as a PositionVector.
getPositionVectorInSystem(otherSystem) {
if (this.system == otherSystem) {
return this.getPositionVector();
}
// Since all CoordinateSystems are defined relative to the world system,
// to transform from our system to the other one, we can first transform
// from ours to the world system, then transform from the world system
// to the other system.
// This could be done with one big matrix multiplication, but for
// clarity we break it up into steps.
let otherVector = this.getPositionVector();
// if we're not already in the world system, switch to it
if (this.system != null) {
// When we set up a CoordinateSystem, we start with a copy of the
// world system. We rotate its x, y, and z axes, then we translate
// its origin.
// So, to undo this, we translate its origin back, then we rotate
// the z, y, and x axes the opposite direction as before.
// Performing the 4 undo operations on our PositionVector will get
// switch it to the world system.
otherVector.translate(-this.system.origin.x, -this.system.origin.y,
-this.system.origin.z);
otherVector.rotateZ(-this.system.zRot);
otherVector.rotateY(-this.system.yRot);
otherVector.rotateX(-this.system.xRot);
}
// if we don't want to stay in the world system, switch out of it
if (otherSystem != null) {
// We're in the world system, so to move out of it we perform the
// exact same operations we would when creating a new system from
// scratch: rotate x, rotate y, rotate z, translate origin.
otherVector.rotateX(otherSystem.xRot);
otherVector.rotateY(otherSystem.yRot);
otherVector.rotateZ(otherSystem.zRot);
otherVector.translate(otherSystem.origin.x, otherSystem.origin.y,
otherSystem.origin.z);
}
return otherVector;
}
// Return a copy of the current Coordinate.
// The x/y/z in the copied Item are distinct from the original Item
// (so moving around the copy won't affect the original). However the
// CoordinateSpace is the same as the original (as opposed to an identical
// copy).
copy() {
return new Coordinate(this.x, this.y, this.z, this.system);
}
// Return a copy of the current Coordinate, re-expressed in the given
// CoordinateSystem.
// The x/y/z in the copied Item are distinct from the original Item
// (so moving around the copy won't affect the original). However the
// CoordinateSpace is the same as the original (as opposed to an identical
// copy).
copyIntoCoordinateSystem(otherSystem) {
const newPosition = this.getPositionVectorInSystem(otherSystem);
return new Coordinate(newPosition.x, newPosition.y, newPosition.z,
otherSystem);
}
}