- 1. Нүкте және вектор
- 2. Ұшақ
- 3. Түзу
- 4. Қорытынды
Осы мақала арқылы мен диссертацияммен жұмыс істеу барысында жинақтаған компьютерлік геометрияны қолдану тәжірибеммен бөліскім келеді. Qt Creator-де геометриямен жұмыс істеуге арналған құралдар (атап айтқанда QVector3D) бар екенін бәрі біле бермейді, олар үшін орыс тілінде толық нұсқаулық жоқ. Сондықтан бұл жерде мен қажетті теория мен оның жүзеге асуын қысқаша сипаттауға тырысамын.
Нүкте және вектор
Qt ішіндегі нүктені сипаттау үшін QVector2D, QVector3D және QVector4D кластары (QVector класымен шатастырмау керек) сәйкесінше екі өлшемді, үш өлшемді және төрт өлшемді кеңістіктер үшін берілген. Іс жүзінде бұл геометриялық функциялар жиыны бар екі, үш және төрт өзгермелі айнымалылардың массивтері. Сондықтан мен барлық мысалдарды іс жүзінде ең пайдалы QVector3D үшін ғана беремін.
QVector3D v1(1,1,1) v2(1,1,1), v3(1.1f,2.5f,3.8f);
Мысалда 3 вектор берілген. Жақшада x, y, z координаталары бар. Векторлық компоненттердің кез келгенін алуға немесе қайта анықтауға болады.
v3.setX(0.5); v3.setY(0.6); v3.setZ(0.7); qDebug() << v3.x() << v3.y() << v3.z() << v3;
> 0,5 0,6 0,7 QVector3D(0,5, 0,6, 0,7)
Векторларды сипаттау үшін бірдей класстар қолданылады. Сондықтан кейбір функциялардың векторлар мен нүктелер үшін әртүрлі мағыналары болады. Мысалы, нормалау дегеніміз берілген векторды бірлік ұзындықтағы векторға түрлендіру операциясы:
мұндағы - вектордың ұзындығы (модуль). Бұл үшін QVector3D екі функциясы бар:
v1.normalize(); // функция, изменяющая переменные объект qDebug() << v1 << v2.normalized(); // функция, не переменные объект
> QVector3D(0,57735, 0,57735, 0,57735) QVector3D(0,57735, 0,57735, 0,57735)
Мысалда ұзындық түбірі үшке тең болатын вектор бірлік векторға қайта есептелді (геометриядағы мұндай нормаланған векторлар әртүрлі геометриялық операциялар үшін қажет). Мұны вектордың ұзындығын қайтаратын length() функциясымен тексеру оңай.
float L = v1.length(); // длина вектора или расстояние от точки до начала координат
Кейбір есептер бізді вектордың ұзындығы емес, оның квадраты қызықтырады. Ол үшін longSquared() функциясы берілген, ол есептеулер санын азайтады
float L2 = v1.lengthSquared();
Көптеген есептеулерді жүргізбес бұрын, пайдаланылған векторлардың нөлге тең еместігін тексеру қажет (барлық құрамдас бөліктер нөлге тең). Әйтпесе, нәтиже жеткіліксіз болады (тіпті қателер болуы мүмкін).
bool b = v1.isNull(); // проверка: является ли вектор нулевым?
Векторларды қосуға және азайтуға болады (олардың сәйкес координаттарын қосу). Сондай-ақ, векторды санға көбейтуге немесе бөлуге болады (ұзындығы бойынша n есе үлкен немесе кіші векторды алады). Көбейту операциясымен абай болу керек, өйткені. v1*v2 өрнегі векторлардың сәйкес координаталарының көбейтіндісіне келтіріледі. v1/v2 бөлу операциясы ұқсас жұмыс істейді. Алдыңғы екі операцияның геометриялық интерпретациясы координаттар жүйесін v2-де көрсетілген мәндер бойынша сәйкес осьтер бойымен созу және кішірейту болып табылады. dotProduct(v1, v2) және crossProduct(v1, v2) функциялары нүктелік және векторлық өнімдерді жүзеге асыру үшін қамтамасыз етілген.
QVector3D v11(1,0,0), v12(0,1,0); qDebug() << v11*v12 << QVector3D::dotProduct(v11, v12) << QVector3D::crossProduct(v11, v12);
> QVector3D(0, 0, 0) 0 QVector3D(0, 0, 1)
Естеріңізге сала кетейін, екі вектордың скаляр көбейтіндісі ретінде анықталған сан
ал нормаль векторларды көбейту кезінде олардың арасындағы бұрыштың косинусына тең болады.
Айқас көбейтіндіні келесідей есептеуге болады
мұндағы i, j, k – декарттық координаталар жүйесінің ортылары, тура жақшалар (мұнда және төменде) матрицаның анықтаушысы болып табылады. Бұл операцияның нәтижесі екі факторлық вектор жатқан жазықтыққа перпендикуляр вектор немесе v1 және v2 коллинеар болса нөлдік вектор болып табылады. Векторлық көбейтіндінің модулі факторлардың модульдерінің көбейтіндісіне және олардың арасындағы бұрыштың синусына тең.
Векторлардың теңдігін тексеруге болады. Барлық сәйкес координаталар тең болғанда екі вектор тең болады. Дегенмен, күрделі есептеулер кезінде қате жиналуы мүмкін екенін есте ұстаған жөн. Сондықтан әзірлеушілер «жұмсақ» салыстыру мүмкіндігін берді.
v2 = v1 + QVector3D(0.000001f, 0.000001f, 0.000001f); qDebug() << (v1 == v2) << qFuzzyCompare(v1, v2);
> жалған ақиқат
Екі нүктенің арақашықтығы арқылы берілген
remoteToPoint(v2) функциясына ендірілген
QVector3D v1(0,1,1), v2(1,0,1); qDebug() << v1.distanceToPoint(v2);
> 1,41421
Векторлар үшін соңғы операцияның мәні олардың айырмасының векторының ұзындығы болып табылады.
qDebug() << (v1-v2).length();
> 1,41421
Ұшақ
Кез келген жазықтықты бір түзуде жатпайтын үш жұп сәйкес келмейтін T1, T2 және T3 нүктелері арқылы анықтауға болады. Тағы бір ыңғайлы сипаттама жазық теңдеуді пайдалану болып табылады:
мұндағы A, B, C және D – T1, T2 және T3 нүктелерінің координаталары арқылы есептелетін коэффициенттер,
(A, B, C) - жазықтыққа нормаль векторы, D - бос коэффициент. Алынған нәтижені қалыпқа келтіруге болады
(a, b, c) жазықтыққа дейінгі бірлік нормаль векторы, d - жазықтықтан координаталар басына дейінгі қашықтық (егер нормаль вектор координаталар координаттары бар жартылай жазықтыққа бағытталған болса, «плюс» белгісімен, әйтпесе – «минуспен»).
Жазықтықтың нормаль векторын аргументтері үш нүкте (жоғарыда көрсетілгендей) немесе екі коллинеар емес вектор болуы мүмкін normal(…) статикалық функциясы арқылы есептеуге болады. Екі векторды T1 – T2, T2 – T3, ал нормальды олардың векторлық сызығы ретінде беруге болатыны анық.
QVector3D v1(0,0,1), v2(0,1,0), v3(1,0,0); // корректно заданная плоскость qDebug() << QVector3D::normal(v1, v2, v3), QVector3D::normal(v1 - v2, v2 - v3), QVector3D::crossProduct(v1 - v2, v2 - v3).normalized();
> QVector3D(-0,57735, -0,57735, -0,57735) QVector3D(-0,57735, -0,57735, -0,57735) QVector3D(-0,57735, -0,577735, -35).
Егер жоғарыда сипатталған кіріс деректеріне қойылатын талаптар орындалмаса, нәтиже нөлдік вектор болады
QVector3D v01(0,0,10), v02(0,0,20), v03(0,0,30); // некорректно заданная плоскость qDebug() << QVector3D::normal(v01, v02, v03) << QVector3D::normal(v01 - v02, v02 - v03);
> QVector3D(0, 0, 0) QVector3D(0, 0, 0)
Ұшақтың жоғарыдағы сипаттамасы ең жинақы (4 сан). Бірақ ең қолайлысы - жазықтықты бір нүкте (T0) және қалыпты вектор (6 сан) арқылы сипаттау. Бұл көрініс келесі әрекеттерді ұйымдастыруды жеңілдетеді.
Тиісті ұшақ
Егер Т нүктесі жазықтықта жатса, оның координаталарын жазықтықтың теңдеуіне қойып, 0 аламыз. Нүкте және вектор арқылы көрсетуде
Басқаша айтқанда, T – T0 векторы n нормаль векторына перпендикуляр.
Бір жарты кеңістікте
Ұшақ кеңістікті екі жарты кеңістікке бөледі. Алдыңғы өрнек егер T нүктесі олардың біреуінде болса, оң мәндерді, ал екіншісінде болса теріс мәндерді қабылдайды. Демек, 2 нүкте T1 және T2 бірдей жарты кеңістікте жатыр, егер
Т нүктесінің жазықтыққа проекциясы
Нүктеден жазықтыққа дейінгі қашықтық
нүктеден оның жазықтыққа проекциясына дейінгі қашықтық
Ол аргументтері жазықтық параметрлері (нүкте және вектор немесе үш нүкте) болып табылатын remoteToPlane(...) функциясы арқылы анықталады.
QVector3D T(2,2,2);// точка QVector3D n(-1,-1,-1), T0(1,1,1);// плоскость qDebug() << T.distanceToPlane(T0,n) << QVector3D::dotProduct(T-T0, n);// неправильный расчёт: нормальный вектор не нормализован qDebug() << T.distanceToPlane(T0,n.normalized()) << QVector3D::dotProduct(T-T0, n)/n.length();//правильный расчёт
>-3 -3
-1,73205 -1,73205
Алынған мәнге T жарты жазықтықта жататынына сәйкес қол қойылғанын ескеріңіз.
Т нүктесінің жазықтықтан айнамен шағылуы
Түзу
Кеңістіктегі түзу екі (сәйкес келмейтін) T1 және T2 нүктелері арқылы толығымен сипатталады. Qt тілінде T1 нүктесі және K = T2 – T1 бағыт векторы бар түзуді сипаттау әдеттегідей.
немесе
мұндағы (ΔX, ΔY, ΔZ) = K. Бұл векторды нормалау да мақсатқа сай.
Т нүктесінің түзу сызыққа жатуы
(T – T1) және k векторларының коллинеарлылығын (айқас көбейтінді – нөл векторы) тексеру қажет.
Т нүктесінің түзу проекциясы
Нүктенің жазықтыққа проекциясы есебінің шешімдерін талдау арқылы келесі өрнекті оңай алуға болады.
Т нүктесінен түзу сызыққа дейінгі қашықтық
Жоғарыда айтылғандарға сәйкес
Нүктеден түзуге дейінгі қашықтық қашықтықToLine(...) функциясы арқылы анықталады, оның аргументтері нүкте және түзудің бағыт векторы болып табылады.
QVector3D T(2,2,2);// точка QVector3D k(1,1,0), T1(0,0,1);// плоскость QVector3D R = T - T1 - k*QVector3D::dotProduct(k, T - T1); qDebug() << T.distanceToLine(T1,k) << R.length(); // неправильный расчёт: направляющий вектор не нормализован R = T - T1 - k*QVector3D::dotProduct(k, T - T1)/k.lengthSquared(); qDebug() << T.distanceToLine(T1,k.normalized()) << R.length(); // правильный расчёт
>3 3
1 1
Түзу мен жазықтықтың қиылысуы
Түзу (k, T1) жазықтыққа параллель болуы мүмкін (n, T0). Бұл жағдайда анықтаушы векторлар перпендикуляр болады, бұл олардың скаляр көбейтіндісінің теңдігін тексеру арқылы тексеріледі. Әйтпесе, түзу мен жазықтықтың қиылысу нүктесі ретінде анықталған
Жоғарыда келтірілген формулада алдыңғылардан айырмашылығы нормаланған векторларды пайдалану қажет емес. Егер қиылыс болмаса, T1 нүктесінің жазықтыққа жататынын тексеріп, түзудің онда жатқанын анықтауға болады.
Қорытынды
Мақаланы соңына дейін оқығандардың барлығына рахмет. Болашақта осы тақырыпты дамытуды жоспарлап отырмын. Сондықтан, тақырып бойынша сұрақтарыңыз болса, оларды түсініктемелерде жазыңыз.
У меня руки так и не дошли написать статью, где показан пример движения круга с отражениями от условных границ в графической сцене с использованием скалярных векторов.
Вообще прлюс использования скалярной математики при расчётах углов отражений в том, что это менее затратный операции, чем использование тригонометрии (синусы/косинусы и т.д.)
Скалярная математика и тригонометрия - суть одно и тоже. Просто разные языки описания одного предмета. В конечном итоге для расчёта коэффициента отражения нужно знать косинус угла падения.
Подскажите, какой инструмент лучше использовать для визуализации 3D объектов.
Вы очень сильно заблуждаетесь.
Вот формула для расчёта отражённого вектора. Расчёт углов через косинусы и синусы не требуется от слова совсем .
Подробнее обсуждение в этом топике
При написании арканоида для медицинского оборудования я пользовался именно математикой скалярных векторов. Поэтому и утверждаю, что использование скалярной математики в разы разгружает процессорные мощности. Это основы написания игровых движков.
Визуализацию можно и в Qt делать. Насколько помню по новостям там есть поддержка импорта 3D объектов даже. Также есть примеры визуализации некоторых вещей в самом Qt Creator.
На нашем проекте используется OpenCascade - достаточно мощная вещь. Главное написать код для QWidget, чтобы отображать результат. Но сам я в той части кода не работаю. Не моя область проекта.
Не согласен. Произведение l на n - это косинус угла между ними умноженный на их длины. Если бы оба вектора были единичными, то остался бы "чистый" косинус. Просто при используемом описании тригонометрия скрыта от нас.
А в плане того, что здесь не нужно вычислять угол, а потом по нему считать косинус я согласен. Непосредственного использования пригонометрических функций не требуется.
Да, согласен. Освежил в памяти. Вот определение из лекций
Тем не менее, косинус здесь знать не нужно, вот к чему я это говорил
Естественно, что скалярное произведение векторов отражает явления тригономитреческих функций и может быть сопоставлено. Иначе это было бы странно. Насколько помню - это выводится через последовательное доказательство леммами, но не через прямые расчёты и формулы. Поэтому утверждение имеет доказательную базу, а знать косинус не нужно ;-)