Дмитрий
Дмитрий28. Juni 2020 08:03

Computergeometrie mit Qt Creator

Mit diesem Artikel möchte ich meine Erfahrungen im Umgang mit der Computergeometrie teilen, die ich während der Arbeit an meiner Dissertation gesammelt habe. Nicht jeder weiß, dass Qt Creator Tools zum Arbeiten mit Geometrie enthält (insbesondere QVector3D), für die es kein detailliertes Handbuch in russischer Sprache gibt. Daher versuche ich hier, die notwendige Theorie und deren Umsetzung kurz zu skizzieren.


Punkt und Vektor

Um einen Punkt in Qt zu beschreiben, werden die Klassen QVector2D, QVector3D und QVector4D (nicht zu verwechseln mit der Klasse QVector) für zweidimensionale, dreidimensionale bzw. vierdimensionale Räume bereitgestellt. Tatsächlich handelt es sich dabei um Arrays aus zwei, drei und vier Float-Variablen mit einem Satz geometrischer Funktionen. Daher werde ich alle Beispiele nur für die nützlichsten QVector3D in der Praxis geben.

QVector3D v1(1,1,1) v2(1,1,1), v3(1.1f,2.5f,3.8f);

Im Beispiel sind 3 Vektoren gegeben. Die Klammern enthalten x-, y-, z-Koordinaten. Jede der Vektorkomponenten kann erhalten oder neu definiert werden.

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)

Dieselben Klassen werden verwendet, um Vektoren zu beschreiben. Daher haben einige der Funktionen unterschiedliche Bedeutungen für Vektoren und Punkte. So ist beispielsweise die Normalisierung eine Operation, die einen gegebenen Vektor in einen Vektor der Einheitslänge umwandelt:

wobei die Länge (Modul) des Vektors ist. QVector3D hat dafür zwei Funktionen:

v1.normalize(); // функция, изменяющая переменные объект
qDebug() << v1 << v2.normalized(); // функция, не переменные объект

> QVector3D(0.57735, 0.57735, 0.57735) QVector3D(0.57735, 0.57735, 0.57735)

Im Beispiel wurde der Vektor, der eine Längenwurzel von drei hatte, in einen Einheitsvektor umgerechnet (solche normierten Vektoren in der Geometrie sind für verschiedene geometrische Operationen notwendig). Dies lässt sich leicht mit der Funktion length() überprüfen, die die Länge des Vektors zurückgibt.

float L = v1.length(); // длина вектора или расстояние от точки до начала координат

Bei manchen Aufgaben interessiert uns nicht die Länge des Vektors, sondern sein Quadrat. Dafür steht die Funktion lengthSquared() zur Verfügung, die die Zahl der Berechnungen reduziert

float L2 = v1.lengthSquared();

Vor vielen Berechnungen muss überprüft werden, ob die verwendeten Vektoren nicht Null sind (alle Komponenten sind Null). Andernfalls ist das Ergebnis unzureichend (sogar Fehler sind möglich).

bool b = v1.isNull(); // проверка: является ли вектор нулевым?

Vektoren können addiert und subtrahiert werden (Addieren ihrer jeweiligen Koordinaten). Ein Vektor kann auch mit einer Zahl multipliziert oder dividiert werden (erhält einen Vektor, der n-mal größer oder kleiner in der Länge ist). Sie sollten mit der Multiplikationsoperation vorsichtiger sein, weil. der Ausdruck v1*v2 wird auf die Multiplikation der entsprechenden Koordinaten der Vektoren reduziert. Die Divisionsoperation v1/v2 funktioniert ähnlich. Die geometrische Interpretation der beiden vorherigen Operationen dehnt und schrumpft das Koordinatensystem entlang der entsprechenden Achsen um die in v2 angegebenen Werte. Die Funktionen dotProduct(v1, v2) und crossProduct(v1, v2) werden bereitgestellt, um die Skalar- und Vektorprodukte zu implementieren.

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)

Ich möchte Sie daran erinnern, dass das Skalarprodukt zweier Vektoren eine Zahl ist, die definiert ist als

und im Fall der Multiplikation von Normalenvektoren gleich dem Kosinus des Winkels zwischen ihnen ist.
Das Kreuzprodukt kann wie folgt berechnet werden

wobei i, j, k die Orte des kartesischen Koordinatensystems sind, direkte Klammern (hier und unten) sind die Determinante der Matrix. Das Ergebnis dieser Operation ist ein Vektor senkrecht zu der Ebene, in der zwei Faktorvektoren liegen, oder ein Nullvektor, wenn v1 und v2 kollinear sind. Der Modul des Vektorprodukts ist gleich dem Produkt der Module der Faktoren und dem Sinus des Winkels zwischen ihnen.
Vektoren können auf Gleichheit geprüft werden. Zwei Vektoren sind gleich, wenn alle ihre entsprechenden Koordinaten gleich sind. Es sollte jedoch beachtet werden, dass sich bei komplexen Berechnungen ein Fehler häufen kann. Daher haben die Entwickler die Möglichkeit eines „weichen“ Vergleichs vorgesehen.

v2 = v1 + QVector3D(0.000001f, 0.000001f, 0.000001f);
qDebug() << (v1 == v2) << qFuzzyCompare(v1, v2);

> falsch wahr

Der Abstand zwischen zwei Punkten ist gegeben durch

eingebettet in die Funktion distanceToPoint(v2).

QVector3D v1(0,1,1), v2(1,0,1);
qDebug() << v1.distanceToPoint(v2);

> 1.41421

Bei Vektoren ist die Bedeutung der letzten Operation die Länge des Vektors ihrer Differenz.

qDebug() << (v1-v2).length();

> 1.41421

Ebene

Jede Ebene kann durch drei paarweise nicht zusammenfallende Punkte T1, T2 und T3 definiert werden, die nicht auf derselben Geraden liegen. Eine andere bequeme Beschreibung ist die Verwendung der Ebenengleichung:

wobei A, B, C und D Koeffizienten sind, die durch die Koordinaten der Punkte T1, T2 und T3 berechnet werden,

(A, B, C) ist der Normalenvektor zur Ebene, D ist der freie Koeffizient. Das erhaltene Ergebnis kann normalisiert werden

(a, b, c) ist der Einheitsnormalenvektor zur Ebene, d ist der Abstand von der Ebene zum Koordinatenursprung (mit „plus“, wenn der Normalenvektor auf die den Ursprung enthaltende Halbebene gerichtet ist, sonst – mit „minus“).
Der Normalenvektor zur Ebene kann mit der statischen Funktion normal(…) berechnet werden, deren Argumente entweder drei Punkte (wie oben gezeigt) oder zwei nicht kollineare Vektoren sein können. Es ist offensichtlich, dass zwei Vektoren als T1 – T2, T2 – T3 und die Normale als ihre Vektorlinie angegeben werden können.

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.57735, -0.57735)

Wenn die oben beschriebenen Eingabedatenanforderungen nicht erfüllt sind, ist das Ergebnis ein Nullvektor

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)

Die obige Beschreibung des Flugzeugs ist die kompakteste (4 Zahlen). Am bequemsten ist jedoch die Beschreibung der Ebene durch einen Punkt (T0) und einen Normalenvektor (6 Zahlen). Diese Ansicht erleichtert die Organisation der folgenden Vorgänge.

Zugehöriges Flugzeug

Wenn der Punkt T auf einer Ebene liegt und seine Koordinaten in die Gleichung der Ebene eingesetzt werden, erhalten wir 0. In der Darstellung durch einen Punkt und einen Vektor

Mit anderen Worten, der Vektor T - T0 steht senkrecht auf dem Normalenvektor n.

In einem Halbraum

Die Ebene teilt den Raum in zwei Halbräume. Der vorherige Ausdruck nimmt positive Werte an, wenn der Punkt T in einem von ihnen liegt, und negative Werte, wenn er in dem anderen liegt. Daher liegen 2 Punkte T1 und T2 im gleichen Halbraum if

Projektion des Punktes T auf eine Ebene

Entfernung von Punkt zu Ebene

ist der Abstand von einem Punkt zu seiner Projektion auf eine Ebene

Sie wird mit der Funktion distanceToPlane(...) definiert, deren Argumente die Ebenenparameter sind (ein Punkt und ein Vektor oder drei Punkte).

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

Beachten Sie, dass der resultierende Wert je nachdem, in welcher Halbebene T liegt, vorzeichenbehaftet ist.

Spiegelreflexion des T-Punktes von der Ebene

Gerade

Eine Linie im Raum wird vollständig durch zwei (nicht zusammenfallende) Punkte T1 und T2 beschrieben. In Qt ist es üblich, eine Gerade mit einem Punkt T1 und einem Richtungsvektor K = T2 – T1 zu beschreiben.

oder

wobei (ΔX, ΔY, ΔZ) = K. Es ist auch sinnvoll, diesen Vektor zu normieren

Zugehörigkeit des Punktes T zu einer Geraden

Die Vektoren (T – T1) und k müssen auf Kollinearität (Kreuzprodukt – Nullvektor) überprüft werden.

Projektion von Punkt T gerade

Der folgende Ausdruck kann leicht durch Analysieren der Lösungen des Problems der Projektion eines Punktes auf eine Ebene erhalten werden

Abstand vom Punkt T zur geraden Linie

In Übereinstimmung mit dem oben Gesagten

Der Abstand von einem Punkt zu einer Linie wird durch die Funktion distanceToLine(...) bestimmt, deren Argumente ein Punkt und ein Richtungsvektor der Linie sind.

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

Schnittpunkt einer Geraden und einer Ebene

Die Linie (k, T1) kann parallel zur Ebene (n, T0) sein. In diesem Fall stehen die definierenden Vektoren senkrecht, was durch Prüfung auf Gleichheit ihres Skalarprodukts überprüft wird. Andernfalls haben die Linie und die Ebene einen Schnittpunkt, der als definiert ist

In der obigen Formel ist es im Gegensatz zu den vorherigen nicht erforderlich, normalisierte Vektoren zu verwenden. Wenn es keinen Schnittpunkt gibt, kann überprüft werden, ob der Punkt T1 zu der Ebene gehört, und festgestellt werden, ob die Linie darauf liegt.

Fazit

Danke an alle, die den Artikel bis zum Ende gelesen haben. In Zukunft plane ich, dieses Thema zu entwickeln. Wenn Sie also Fragen zum Thema haben, schreiben Sie diese in die Kommentare.

Рекомендуємо хостинг TIMEWEB
Рекомендуємо хостинг TIMEWEB
Stabiles Hosting des sozialen Netzwerks EVILEG. Wir empfehlen VDS-Hosting für Django-Projekte.

Magst du es? In sozialen Netzwerken teilen!

Evgenii Legotckoi
  • 29. Juni 2020 04:05

У меня руки так и не дошли написать статью, где показан пример движения круга с отражениями от условных границ в графической сцене с использованием скалярных векторов.

Вообще прлюс использования скалярной математики при расчётах углов отражений в том, что это менее затратный операции, чем использование тригонометрии (синусы/косинусы и т.д.)

Дмитрий
  • 1. Juli 2020 05:56

Скалярная математика и тригонометрия - суть одно и тоже. Просто разные языки описания одного предмета. В конечном итоге для расчёта коэффициента отражения нужно знать косинус угла падения.

Подскажите, какой инструмент лучше использовать для визуализации 3D объектов.

Evgenii Legotckoi
  • 1. Juli 2020 06:09
  • (bearbeitet)

Вы очень сильно заблуждаетесь.

Вот формула для расчёта отражённого вектора. Расчёт углов через косинусы и синусы не требуется от слова совсем .

Подробнее обсуждение в этом топике

При написании арканоида для медицинского оборудования я пользовался именно математикой скалярных векторов. Поэтому и утверждаю, что использование скалярной математики в разы разгружает процессорные мощности. Это основы написания игровых движков.

Визуализацию можно и в Qt делать. Насколько помню по новостям там есть поддержка импорта 3D объектов даже. Также есть примеры визуализации некоторых вещей в самом Qt Creator.

На нашем проекте используется OpenCascade - достаточно мощная вещь. Главное написать код для QWidget, чтобы отображать результат. Но сам я в той части кода не работаю. Не моя область проекта.

Дмитрий
  • 2. Juli 2020 06:16

Не согласен. Произведение l на n - это косинус угла между ними умноженный на их длины. Если бы оба вектора были единичными, то остался бы "чистый" косинус. Просто при используемом описании тригонометрия скрыта от нас.

Дмитрий
  • 2. Juli 2020 06:19

А в плане того, что здесь не нужно вычислять угол, а потом по нему считать косинус я согласен. Непосредственного использования пригонометрических функций не требуется.

Evgenii Legotckoi
  • 2. Juli 2020 06:50

Да, согласен. Освежил в памяти. Вот определение из лекций

Скалярным произведением ненулевых векторов a и b называется число, равное произведению длин этих
векторов на косинус угла между ними. Если среди векторов a и b
есть хотя бы один нулевой, то скалярное произведение равно нулю.

Тем не менее, косинус здесь знать не нужно, вот к чему я это говорил

В конечном итоге для расчёта коэффициента отражения нужно знать косинус угла падения.

Естественно, что скалярное произведение векторов отражает явления тригономитреческих функций и может быть сопоставлено. Иначе это было бы странно. Насколько помню - это выводится через последовательное доказательство леммами, но не через прямые расчёты и формулы. Поэтому утверждение имеет доказательную базу, а знать косинус не нужно ;-)

Kommentare

Nur autorisierte Benutzer können Kommentare posten.
Bitte Anmelden oder Registrieren
Letzte Kommentare
ИМ
Игорь Максимов5. Oktober 2024 07:51
Django – Lektion 064. So schreiben Sie eine Python-Markdown-Erweiterung Приветствую Евгений! У меня вопрос. Можно ли вставлять свои классы в разметку редактора markdown? Допустим имея стандартную разметку: <ul> <li></li> <li></l…
d
dblas55. Juli 2024 11:02
QML - Lektion 016. SQLite-Datenbank und das Arbeiten damit in QML Qt Здравствуйте, возникает такая проблема (я новичок): ApplicationWindow неизвестный элемент. (М300) для TextField и Button аналогично. Могу предположить, что из-за более новой верси…
k
kmssr8. Februar 2024 18:43
Qt Linux - Lektion 001. Autorun Qt-Anwendung unter Linux как сделать автозапуск для флэтпака, который не даёт создавать файлы в ~/.config - вот это вопрос ))
Qt WinAPI - Lektion 007. Arbeiten mit ICMP-Ping in Qt Без строки #include <QRegularExpressionValidator> в заголовочном файле не работает валидатор.
EVA
EVA25. Dezember 2023 10:30
Boost - statisches Verknüpfen im CMake-Projekt unter Windows Ошибка LNK1104 часто возникает, когда компоновщик не может найти или открыть файл библиотеки. В вашем случае, это файл libboost_locale-vc142-mt-gd-x64-1_74.lib из библиотеки Boost для C+…
Jetzt im Forum diskutieren
J
JacobFib17. Oktober 2024 03:27
добавить qlineseries в функции Пользователь может получить любые разъяснения по интересующим вопросам, касающимся обработки его персональных данных, обратившись к Оператору с помощью электронной почты https://topdecorpro.ru…
JW
Jhon Wick1. Oktober 2024 15:52
Indian Food Restaurant In Columbus OH| Layla’s Kitchen Indian Restaurant If you're looking for a truly authentic https://www.laylaskitchenrestaurantohio.com/ , Layla’s Kitchen Indian Restaurant is your go-to destination. Located at 6152 Cleveland Ave, Colu…
КГ
Кирилл Гусарев27. September 2024 09:09
Не запускается программа на Qt: точка входа в процедуру не найдена в библиотеке DLL Написал программу на C++ Qt в Qt Creator, сбилдил Release с помощью MinGW 64-bit, бинарнику напихал dll-ки с помощью windeployqt.exe. При попытке запуска моей сбилженной программы выдаёт три оши…
F
Fynjy22. Juli 2024 04:15
при создании qml проекта Kits есть но недоступны для выбора Поставил Qt Creator 11.0.2. Qt 6.4.3 При создании проекта Qml не могу выбрать Kits, они все недоступны, хотя настроены и при создании обычного Qt Widget приложения их можно выбрать. В чем может …

Folgen Sie uns in sozialen Netzwerken