java - ecuacion - como calcular el circulo osculador
Punto 3d en la circunferencia de un cĂrculo con un centro, radio y vector normal (2)
Mi pregunta es similar a Cómo hacer un punto Orbitar una línea, 3D, pero la respuesta no pareció resolver mi problema. Y lo que estoy buscando es una solución general.
Para el registro, estoy tratando de resolver un problema en OpenGL ES (Java / Android).
Tengo un círculo con un punto 3D para su centro, un radio y un vector 3D que especifica la normal al plano en el que se encuentra el círculo.
Necesito encontrar el punto 3D que representa el punto en la circunferencia en un ángulo dado desde el eje X "girado" (rotado según el vector normal).
Ya tengo una implementación en una clase Circle
de una función miembro, pointAt
, que funciona en circunstancias limitadas. Específicamente, en mi implementación actual, supongo que el círculo está en el plano XY y devuelvo un punto en consecuencia y, dado que sé que el círculo está realmente en el plano XZ, simplemente cambio los valores Y y Z en el punto devuelto y funciona . Sin embargo, esta no es una solución general y eso es lo que voy a necesitar.
Cuando probé el algoritmo dado en Cómo hacer una órbita de punto en una línea, 3D , obtuve puntos bastante alejados de donde deberían haber estado.
Entonces, ¿cómo puedo calcular un punto en la circunferencia de dicho círculo?
[Editar] Supongo que mi explicación no fue suficiente. Mi suposición es que un círculo es ''normalmente'' en el plano XY con un vector normal de (0, 0, 1) - 1 en la dirección Z. Si se necesita un punto en la circunferencia, el punto se define por:
x = R*cos(a) + Cx
y = R*sin(a) + Cy
donde R
es el radio, Cx
y Cy
son las coordenadas X
e Y
del centro del círculo, y a
es el ángulo desde un vector a través del punto central del círculo y paralelo con el eje X.
Ahora, si el círculo no tiene un vector normal apuntando a lo largo del eje Z pero, en cambio, es un vector arbitrario (x, y, z), ¿cómo puedo encontrar el mismo punto?
Lo que necesitas es un nuevo sistema de coordenadas para colocar el círculo. Como cualquier sistema de coordenadas común, querremos que los vectores base sean ortogonales entre sí y tengan una longitud de 1 cada uno. Voy a nombrar los vectores base v1
, v2
y v3
, que corresponden a x, y y z en orden.
El nuevo vector base que reemplaza a z, que es v3
viene dado por el vector normal del círculo. Si todavía no está normalizado, querrá normalizarlo aquí:
[ v3x ]
v3 = [ v3y ] = normalize(circleNormal)
[ v3z ]
A continuación, elegiremos v1
. Este puede ser un vector arbitrario que es ortogonal a v3
. Como queremos que tome el lugar del eje x, podemos elegir que tenga un componente y de 0:
[ v3z ]
v1 = normalize([ 0 ])
[ -v3x]
Tenga en cuenta que el producto de punto de este vector con v3
es 0, lo que significa que los dos vectores son de hecho ortogonales. El vector será degenerado si el vector normal del círculo apunta exactamente en la dirección y. Dejaré que descubras cómo manejar eso si es una preocupación en tu uso.
Ahora solo necesitamos el último vector, que puede calcularse como el producto cruzado de los otros dos:
v2 = v3 x v1
Esto ya se normalizará ya que v1
y v3
se normalizaron y son ortogonales.
Con esta nueva base, los puntos en el círculo ahora se pueden calcular como:
p = centerPoint + R * (cos(a) * v1 + sin(a) * v2)
Acercando todo a la forma del código (no probado):
// Only needed if normal vector (nx, ny, nz) is not already normalized.
float s = 1.0f / (nx * nx + ny * ny + nz * nz);
float v3x = s * nx;
float v3y = s * ny;
float v3z = s * nz;
// Calculate v1.
s = 1.0f / (v3x * v3x + v3z * v3z);
float v1x = s * v3z;
float v1y = 0.0f;
float v1z = s * -v3x;
// Calculate v2 as cross product of v3 and v1.
// Since v1y is 0, it could be removed from the following calculations. Keeping it for consistency.
float v2x = v3y * v1z - v3z * v1y;
float v2y = v3z * v1x - v3x * v1z;
float v2z = v3x * v1y - v3y * v1x;
// For each circle point.
px = cx + r * (v1x * cos(a) + v2x * sin(a))
py = cy + r * (v1y * cos(a) + v2y * sin(a))
pz = cz + r * (v1z * cos(a) + v2z * sin(a))
Entonces, al trabajar con @Timothy Shields en comentarios sobre cómo hacer una órbita de punto en una línea, 3D obtuve mi respuesta. Aquí hay un extracto de mi clase Circle resultante si alguien está interesado. El miembro normalized
en la clase Vector
simplemente divide cada una de las componentes del vector por la longitud del vector para devolver un vector unitario. Circle
, Vector
, and Point
son todas las clases que he creado para mi aplicación.
public class Circle {
public final Point center;
public final float radius;
public final Vector normal;
....
public Point pointAt(float angle) {
float xv = (float) Math.cos(angle);
float yv = (float) Math.sin(angle);
Vector v = findV();
Vector w = v.crossProduct(normal);
// Return center + r * (V * cos(a) + W * sin(a))
Vector r1 = v.scale(radius*xv);
Vector r2 = w.scale(radius*yv);
return new Point(center.x + r1.x + r2.x,
center.y + r1.y + r2.y,
center.z + r1.z + r2.z);
}
private Vector findV() {
Vector vp = new Vector(0f, 0f, 0f);
if (normal.x != 0 || normal.y != 0) {
vp = new Vector(0f, 0f, 1f);
} else if (normal.x != 0 || normal.z != 0) {
vp = new Vector(0f, 1f, 0f);
} else if (normal.y != 0 || normal.z != 0) {
vp = new Vector(1f, 0f, 0f);
} else {
return null; // will cause an exception later.
}
Vector cp = normal.crossProduct(vp);
return cp.normalized();
}
}