-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathMotionMatrix.cs
More file actions
255 lines (218 loc) · 9.86 KB
/
Copy pathMotionMatrix.cs
File metadata and controls
255 lines (218 loc) · 9.86 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
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
// This class holds information necessary to transformation point in 3D space
// The types of transformations that it supports are as follows
// - Scaling in 3 dimensions independently
// - Rotating about each of the 3 axes independently
// - Translation to a different center in 3 dimensions independently
//
// This also has the ability to undo this transition, this allows us to take the intersection points that and map them back
// to where they should have landed without any of the transformation
//
// Some things to note
// Order of operations is important, we could have use 4d transformation matrixes
// Doing so that would allow us to simply apply each change to the matrix as given and we could undo them by computing the inverse matrix
// Commuting the inverse matrix of an arbitrary 4x4 matrix should be doable (assuming it determinate is not 0, which this should not)
// But that can be computationally expensive.
// Plus b/c of order of operating matters it get tricky
// Basically translate laterally along the X axis, then rotate about the Y axis is very different if those operations are reversed
// So instead we ...
// - Track Scaling, which is applied first
// - Keep one 3x3 matrix for all hte rotations and these matrix are easy to compute the inverse of, its just the transpose
// - Track translation
// Regardless of the order the mutation methods are call we will apply them in this order, and undo them in the reverse
public class MotionMatrix
{
public Point3D Apply(Point3D point)
{
if (Version == default)
{
return point;
}
point = new(
point.X * Scale.X,
point.Y * Scale.Y,
point.Z * Scale.Z
);
return new(
point.X * m_rotationMatrix[0, 0] + point.Y * m_rotationMatrix[0, 1] + point.Z * m_rotationMatrix[0, 2] + Translation.X,
point.X * m_rotationMatrix[1, 0] + point.Y * m_rotationMatrix[1, 1] + point.Z * m_rotationMatrix[1, 2] + Translation.Y,
point.X * m_rotationMatrix[2, 0] + point.Y * m_rotationMatrix[2, 1] + point.Z * m_rotationMatrix[2, 2] + Translation.Z
);
}
public Point3D Unapply(Point3D point)
{
if (Version == default)
{
return point;
}
point -= Translation;
point = new(
point.X * m_rotationMatrix[0, 0] + point.Y * m_rotationMatrix[1, 0] + point.Z * m_rotationMatrix[2, 0],
point.X * m_rotationMatrix[0, 1] + point.Y * m_rotationMatrix[1, 1] + point.Z * m_rotationMatrix[2, 1],
point.X * m_rotationMatrix[0, 2] + point.Y * m_rotationMatrix[1, 2] + point.Z * m_rotationMatrix[2, 2]
);
return new (
point.X / Scale.X,
point.Y / Scale.Y,
point.Z / Scale.Z
);
}
public bool IsIdentity => Version == default;
public MotionMatrix MoveTo(Point3D point)
{
Version++;
Translation = point;
return this;
}
public MotionMatrix MoveBy(Point3D point)
{
Version++;
Translation += point;
return this;
}
public MotionMatrix SetScale(Point3D scale)
{
Version++;
Scale = scale;
return this;
}
// Given two from vectors (from and up, which should be perpendicular) update the rotation matrix so
// those vectors should get translated to the new normal and up
public MotionMatrix Alight(Point3D fromNormal, Point3D fromUp, Point3D toNormal, Point3D toUp)
{
// TODO We should assert that:
// norma1, up1 are perpendicular
// norma2, up2 are perpendicular
// all of these are normalized
Version++;
// TODO: this is assuming normal1 is the Z unit Vector
// soh-cah-toa
//rotate around the Y, to shift it into X/Z o = x, a = z,
var angle = Math.Atan2(toNormal.X, toNormal.Z);
// TODO, why is the "-"
RotateByY(-angle);
var intimidate = Apply(toNormal);
// rotate around the X, to shift into the Y/Z o =y, a = z
angle = Math.Atan2(intimidate.Y, intimidate.Z);
RotateByX(angle);
// TODO: we should continue to align up1 and up2
// TODO: this transpose is really only valid b/c If we started with Identity matrix
m_rotationMatrix = new double[,]
{
{ m_rotationMatrix[0, 0], m_rotationMatrix[1, 0], m_rotationMatrix[2, 0] },
{ m_rotationMatrix[0, 1], m_rotationMatrix[1, 1], m_rotationMatrix[2, 1] },
{ m_rotationMatrix[0, 2], m_rotationMatrix[1, 2], m_rotationMatrix[2, 2] },
};
return this;
}
public MotionMatrix RotateByX(double angle)
{
Version++;
/* https://en.wikipedia.org/wiki/Rotation_matrix
1, 0, 0
0, c, -s
0, s, c
*/
var c = Math.Cos(angle);
var s = Math.Sin(angle);
m_rotationMatrix = new double[,]
{
{
m_rotationMatrix[0, 0], // 1[0,0] + 0[1,0] + 0[2, 0]
m_rotationMatrix[0, 1], // 1[0,1] + 0[1,1] + 0[2, 1]
m_rotationMatrix[0, 2], // 1[0,2] + 0[1,2] + 0[2, 2]
},
{
c * m_rotationMatrix[1, 0] - s * m_rotationMatrix[2, 0], // 0[0,0] + c[1,0] - s[2, 0]
c * m_rotationMatrix[1, 1] - s * m_rotationMatrix[2, 1], // 0[0,1] + c[1,1] - s[2, 1]
c * m_rotationMatrix[1, 2] - s * m_rotationMatrix[2, 2], // 0[0,2] + c[1,2] - s[2, 2]
},
{
s * m_rotationMatrix[1, 0] + c * m_rotationMatrix[2, 0], // 0[0,0] + s[1,0] + c[2, 0]
s * m_rotationMatrix[1, 1] + c * m_rotationMatrix[2, 1], // 0[0,1] + s[1,1] + c[2, 1]
s * m_rotationMatrix[1, 2] + c * m_rotationMatrix[2, 2], // 0[0,2] + s[1,2] + c[2, 2]
},
};
return this;
}
public MotionMatrix RotateByY(double angle)
{
Version++;
/* https://en.wikipedia.org/wiki/Rotation_matrix
c, 0, s
0, 1, 0
-s, 0, c
*/
var c = Math.Cos(angle);
var s = Math.Sin(angle);
m_rotationMatrix = new double[,]
{
{
c * m_rotationMatrix[0, 0] + s * m_rotationMatrix[2, 0], // c[0,0] + 0[1,0] + s[2, 0]
c * m_rotationMatrix[0, 1] + s * m_rotationMatrix[2, 1], // c[0,1] + 0[1,1] + s[2, 1]
c * m_rotationMatrix[0, 2] + s * m_rotationMatrix[2, 2], // c[0,2] + 0[1,2] + s[2, 2]
},
{
m_rotationMatrix[1, 0], // 0[0,0] + 1[1,0] + 0[2, 0]
m_rotationMatrix[1, 1], // 0[0,1] + 1[1,1] + 0[2, 1]
m_rotationMatrix[1, 2], // 0[0,2] + 1[1,2] + 0[2, 2]
},
{
// TODO: would c - s be faster than -s + c... I would hope the compiler can do optimizations
-s * m_rotationMatrix[0, 0] + c * m_rotationMatrix[2, 0], // -s[0,0] + 0[1,0] + c[2, 0]
-s * m_rotationMatrix[0, 1] + c * m_rotationMatrix[2, 1], // -s[0,1] + 0[1,1] + c[2, 1]
-s * m_rotationMatrix[0, 2] + c * m_rotationMatrix[2, 2], // -s[0,2] + 0[1,2] + c[2, 2]
},
};
return this;
}
public MotionMatrix RotateByZ(double angle)
{
Version++;
/* https://en.wikipedia.org/wiki/Rotation_matrix
c, -s, 0
s, c, 0
0, 0, 1
*/
var c = Math.Cos(angle);
var s = Math.Sin(angle);
m_rotationMatrix = new double[,]
{
{
c * m_rotationMatrix[0, 0] - s * m_rotationMatrix[1, 0], // c[0,0] - s[1,0] + 0[2, 0]
c * m_rotationMatrix[0, 1] - s * m_rotationMatrix[1, 1], // c[0,1] - s[1,1] + 0[2, 1]
c * m_rotationMatrix[0, 2] - s * m_rotationMatrix[1, 2], // c[0,2] - s[1,2] + 0[2, 2]
},
{
s * m_rotationMatrix[0, 0] + c * m_rotationMatrix[1, 0], // s[0,0] + c[1,0] + 0[2, 0]
s * m_rotationMatrix[0, 1] + c * m_rotationMatrix[1, 1], // s[0,1] + c[1,1] + 0[2, 1]
s * m_rotationMatrix[0, 2] + c * m_rotationMatrix[1, 2], // s[0,2] + c[1,2] + 0[2, 2]
},
{
m_rotationMatrix[2, 0], // 0[0,0] + 0[1,0] + 1[2, 0]
m_rotationMatrix[2, 1], // 0[0,1] + 0[1,1] + 1[2, 1]
m_rotationMatrix[2, 2], // 0[0,2] + 0[1,2] + 1[2, 2]
},
};
return this;
}
public Point3D Translation { get; private set; }
public Point3D Scale { get; private set; } = Point3D.Identity;
/// <summary>
/// Helps outsiders track if things have changed, simply save the value
/// Wait for stuff to happen, and then check the value later
/// If the value has changed then it is likely the content has changed
/// This approach is blind to something like Move +1 follow by Move -1
/// </summary>
public int Version {get; private set; } = default;
// This should really only be used by tests that are attempting to do validation.
public double[,] RawRotationMatrix => (double[,])m_rotationMatrix.Clone();
public const int VersionUninitialized = -1;
// TODO: should we make changes to help limit the number of arrays that allocate, should we change this to only allocate
// this one on the heep and all the "temp" ons the stack?
private double[,] m_rotationMatrix = new double[,]
{
{Point3D.XUnit.X, Point3D.XUnit.Y, Point3D.XUnit.Z},
{Point3D.YUnit.X, Point3D.YUnit.Y, Point3D.YUnit.Z},
{Point3D.ZUnit.X, Point3D.ZUnit.Y, Point3D.ZUnit.Z},
};
}