问题
The title is probably wrong because I don't know enough math to actually describe my problem in a small sentence.
- I have a closed loop of 3D vectors, which I will call '3D polygon'.
- I need to perform a 2D-only operation on it, which will return me a different set of 2D points
- I need to convert those new 2D points back to 3D.
My current attempt is as follow:
- Get a 'best fit' plane that minimizes the chances of making the '3D' polygon self intersect when converted to 2D.
- Get the two perpendicular planes by shifting the plane normal's coordinates
- For each 3D point, get the distances to the planes to get 'axis aligned coordinates'
- Save the Y coordinate for later in a separate variable, use X and Z to do the 2D operations
- Perform the 2D operations
- Get the new 2D points, and get a weighted average of the closest 3 original points to assume the height of the new 2D points
- Multiply the 'axis aligned coordinates' + the assumed height by the respective planes normals to return the 2D point to 3D space.
The issue is, this is not working, the culprit seems to be the part where I get the 'axis aligned coordinates', as reverting them back immediately gives the wrong result
public static List<Vector2> Planify3Dto2DPoints2(Vector3[] points, Vector3 centroid, Plane ply, out Vector3[] oldHeights) {
var pz = ply.normal.z;
var px = ply.normal.x;
var py = ply.normal.y;
Plane plx = new Plane(new Vector3(pz, px, py), 0);
Plane plz = new Plane(new Vector3(py, pz, px), 0);
oldHeights = new Vector3[points.Length];
List<Vector2> m_points = new List<Vector2>();
int i = 0;
foreach (Vector3 v3 in points) {
Vector3 v4 = v3 - centroid;
float x = plx.GetDistanceToPoint(v4);//this part is wrong, attempting to get the v4
float z = plz.GetDistanceToPoint(v4);//vector back from the x, z, y coordinates is not
float y = ply.GetDistanceToPoint(v4);//working. removing x * plx.Normal from v4 before
m_points.Add(new Vector2(x, z));// extracting the z coordinate reduces the error, but does not remove it
oldHeights[i++] = new Vector3(x, z, y);
}
return m_points;
}
public static List<Vector3> Spacefy2Dto3DPoints(Vector2[] points, Vector3 centroid, Plane ply, Vector3[] oldHeights = null) {
List<Vector3> m_points = new List<Vector3>();
var pn = new Vector3(ply.normal.x, ply.normal.y, ply.normal.z);
for (int i = 0; i < points.Length; i++) {
Vector3 mp = MoveInPlane(ply, points[i]);
if (oldHeights != null) {
mp += pn * oldHeights[i].z;//AverageOf3ClosestHeight(points[i], oldHeights); not needed yet, but working fine, it's weighted average
}
mp += centroid;
m_points.Add(mp);
}
return m_points;
}
private static Vector3 MoveInPlane(Plane plane, Vector2 vector2) {
var z = plane.normal.z;
var x = plane.normal.x;
var y = plane.normal.y;
return new Vector3(z, x, y) * vector2.x + new Vector3(y, z, x) * vector2.y;
}
回答1:
The issue lies in this step:
- Get the two perpendicular planes by shifting the plane normal's coordinates
This does not give perpendicular planes.
You may have mistakenly thought this would work due to a simple specific example, e.g. (1, 0, 0) => (0, 1, 0) & (0, 0, 1)
, or that switching around the coordinates effectively switches the roles of the axes, which would be equivalent to rotation by 90 degrees. But try it with e.g. (1, 1, 0)
and you immediately see this does not work.
One approach would be this:
- Take the dot-product of the normal
P
with theX
axis (arbitrary choice). - If this is close to 1 or -1 (set a threshold, e.g.
abs(dot(X, P)) > 0.5
), then set a vector variableQ <- Z
axis (again, arbitrary). Otherwise, setQ <- X
. - The two perpendicular planes' normals are therefore given by
U = P ^ Q
andV = P ^ U
. Note that they are not normalized, and{U, V, P}
give a set of right-handed axes.
Another minor optimization you could make is to incorporate the - centeroid
into the plane equations themselves to avoid having to do so for every point explicitly.
Vector3 Q = (Math.Abs(ply.normal.x) > 0.5) ? new Vector3D(0.0, 1.0, 0.0)
: new Vector3D(1.0, 0.0, 0.0);
Vector3 U = Vector3.Normalize(Vector3.CrossProduct(ply.normal, Q));
Vector3 V = Vector3.CrossProduct(ply.normal, U);
// no need to normalize V because U and P are already orthonormal
Plane plx = new Plane(U, Vector3.DotProduct(U, centeroid));
Plane plz = new Plane(V, Vector3.DotProduct(V, centeroid));
// ...
foreach (Vector3 v3 in points) {
/* Vector3 v4 = v3 - centroid; // erase this line */
float x = plx.GetDistanceToPoint(v3); // v4 -> v3 for all code following
来源:https://stackoverflow.com/questions/49152154/getting-axis-aligned-coordinates-from-generic-plane