Итак, проблема сводится к следующему:
Я знаю 4 балла A,B,C,D
и хочу вычислить матрицу преобразования, которая преобразует A,B
в C,D
.
Это можно сделать так. Предположим, мы конвертируем точки следующим образом:
M * A = C
M * B = D
Где M
- это матрица преобразования, которую мы хотим вычислить. Существует бесконечное количество возможных решений (так как линия AB
может иметь любое вращение вокруг своей оси)
Если вы рассекаете M немного , это просто вопрос знания положения, ориентация и масштаб.
Масштаб - это самое простое
, это просто отношение длины линии после и до преобразования.
scale = |CD|/|AB|
ориентация
это представлено единицами базисных векторов. Мы можем использовать тот факт, что AB и CD имеют только одно вращение (все остальные просто создают бесконечное число решений), поэтому мы можем просто повернуть AB
на угол между AB
, CD
вокруг оси, перпендикулярной обоим AB
CD
. Угол, который мы можем получить через ако точечного произведения между единичными векторами, параллельными AB
, CD
. Единственная проблема заключается в том, что мы не будем указывать направление вращения, поэтому нам нужно проверить две возможности (CW, CCW).
так:
axis = cross(B-A,D-C)
angle = +/- acos(dot(B-A,D-C) / |B-A|*|D-C|)
перевод
это просто, мы просто преобразуем A
с M
без перевода, давайте назовем его A'
, а затем просто исправим полученную позицию, чтобы она перешла на C
.
M_origin += C-A'
Помните, что перевод должен быть установлен напрямую, без применения матрицы перевода. Они обычно переводятся в локальную систему координат [LCS]
, которая сначала преобразует разницу в нее. В таком случае используйте
translate(Inverse(M)*(C-A'))
или
translate(M*(C-A'))
в зависимости от используемых обозначений.
Здесь маленький C ++ / VCL / old GL пример:
//---------------------------------------------------------------------------
#include <vcl.h>
#include <math.h>
#pragma hdrstop
#include "Unit1.h"
#include "gl_simple.h"
#include "OpenGLrep4d_double.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
double arot=0.0; // just animation angle
//---------------------------------------------------------------------------
const int pnts=8;
double pnt[pnts*3]= // Vertexes for 10x10x10 cube centered at (0,0,0)
{
-5.0,-5.0,-5.0,
-5.0,+5.0,-5.0,
+5.0,+5.0,-5.0,
+5.0,-5.0,-5.0,
-5.0,-5.0,+5.0,
-5.0,+5.0,+5.0,
+5.0,+5.0,+5.0,
+5.0,-5.0,+5.0,
};
const int lins=12;
int lin[lins*2]= // lines (index of point used) no winding rule
{
0,1,1,2,2,3,3,0,
4,5,5,6,6,7,7,4,
0,4,1,5,2,6,3,7,
};
double A[3]={-5.0,-5.0,-5.0}; // cube diagonal
double B[3]={+5.0,+5.0,+5.0};
double C[3]={-4.5, 2.0, 0.0}; // wanted cube diagonal
double D[3]={+4.5, 5.0, 0.0};
double M[16]; // our transform matrix
//---------------------------------------------------------------------------
void compute_M()
{
double scale,p[3],q[3],n[3],a;
const double deg=180.0/M_PI;
const double rad=M_PI/180.0;
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
// scale
vector_sub(p,B,A); // p=B-A
vector_sub(q,D,C); // q=D-C
scale=vector_len(q)/vector_len(p); // =|q|/|p|
// rotation between AB and CD
vector_mul(n,p,q); // n = (p x q) ... cross product
vector_one(p,p); // p = p/|p|
vector_one(q,q); // q = q/|q|
a=acos(vector_mul(p,q)); // angle between AB and CD in [rad]
glLoadIdentity(); // unit matrix
glRotated(+a*deg,n[0],n[1],n[2]); // rotate by angle around normal to AB,CD
glScaled(scale,scale,scale); // apply scale
glGetDoublev(GL_MODELVIEW_MATRIX,M); // get the M from OpenGL
// translation
matrix_mul_vector(p,M,A); // p = M*A
vector_sub(p,C,p); // p = C-p
M[12]=p[0];
M[13]=p[1];
M[14]=p[2];
M[15]=1.0;
// verify
matrix_mul_vector(p,M,B); // p = M*B
vector_sub(p,p,D); // p = p-C
if (vector_len(p)>1e-3) // if |p| too big use other direction to rotate
{
glLoadIdentity(); // unit matrix
glRotated(-a*deg,n[0],n[1],n[2]); // rotate by angle around normal to AB,CD
glScaled(scale,scale,scale); // apply scale
glGetDoublev(GL_MODELVIEW_MATRIX,M); // get the M from OpenGL
}
glPopMatrix();
}
//---------------------------------------------------------------------------
void gl_draw() // main rendering code
{
int i;
double m0[16],m1[16],m[16],x[3],y[3],z[3],t2[3][3];
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glDisable(GL_CULL_FACE);
glEnable(GL_DEPTH_TEST);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslated(0.0,0.0,-50.0);
glRotated(15.0,1.0,0.0,0.0);
glRotated(arot,0.0,1.0,0.0);
glBegin(GL_LINES);
glColor3f(1.0,0.0,0.0); for (i=0;i<lins*2;i++) glVertex3dv(pnt+(lin[i]*3)); // render original cube
glColor3f(0.0,1.0,0.0); glVertex3dv(A); glVertex3dv(B); // render original diagonal AB
glColor3f(1.0,1.0,0.0); glVertex3dv(C); glVertex3dv(D); // render wanted diagonal CD
glEnd();
// render transformed cube
glMatrixMode(GL_MODELVIEW);
glMultMatrixd(M);
glBegin(GL_LINES);
glColor3f(0.0,0.0,1.0); for (i=0;i<lins*2;i++) glVertex3dv(pnt+(lin[i]*3)); // render transformed cube
glEnd();
glFlush();
SwapBuffers(hdc);
}
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner):TForm(Owner)
{
// application init
gl_init(Handle);
compute_M();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormDestroy(TObject *Sender)
{
// application exit
gl_exit();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormResize(TObject *Sender)
{
// window resize
gl_resize(ClientWidth,ClientHeight);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormPaint(TObject *Sender)
{
// window repaint
gl_draw();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Timer1Timer(TObject *Sender)
{
arot+=1.5; if (arot>=360.0) arot-=360.0;
gl_draw();
}
//---------------------------------------------------------------------------
Просто игнорируйте материал, связанный с VCL. Функции поддержки GL вы можете найти здесь:
Единственное важное здесь - compute_M()
вместе с глобальными переменными.
Векторные математические функции комментируются (так что вы можете перевести это в GLM), если вам нужны реализации, вы можете найти их в связанном QA выше. Это в основном занимает. Для простоты я использовал собственные вращения GL (будьте осторожны, они в градусах, а не в радианах).
Вот превью:
red
является оригинальным кубом green
является оригинальным диагональ AB
blue
преобразуется кубом M
yellow
- это искомая диагональ CD
Как видно, он соответствует .
Если вам нужно выровнять больше, чем просто линию, вам нужно добавить больше информации для выравнивания (например, 2 линии (3 точки)) et c. Для получения дополнительной информации см .: