Initial commit

This commit is contained in:
torsten committed 2026-08-13 18:33:10 +02:00
commit b7d90b9afe
16245 files changed
+3396666

No files matched your search

+30
View File
@@ -0,0 +1,30 @@
cmake_minimum_required(VERSION 3.16)
project(TrajectoryShader LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_AUTOUIC ON)
find_package(Qt6 REQUIRED COMPONENTS Widgets OpenGLWidgets OpenGL)
qt_add_executable(TrajectoryShader
main.cpp
mainwindow.h
mainwindow.cpp
trajectorywidget.h
trajectorywidget.cpp
trajectorywarp.h trajectorywarp.cpp
ballistiksimulator.h ballistiksimulator.cpp
)
target_link_libraries(TrajectoryShader
PRIVATE
Qt6::Widgets
Qt6::OpenGLWidgets
Qt6::OpenGL
)
qt_finalize_executable(TrajectoryShader)
@@ -0,0 +1,103 @@
#define _USE_MATH_DEFINES
#include "ballistiksimulator.h"
#include <cmath>
#include <QFile>
#include <QTextStream>
#include <QDebug>
BallistikSimulator::BallistikSimulator()
: gen(std::random_device{}())
{
}
QVector<TrajectoryPoint> BallistikSimulator::simuliereFlug(double v0,
double winkelGrad,
double masse,
double cw,
double flaeche,
double streuung)
{
QVector<TrajectoryPoint> trajektorie;
double winkelRad = winkelGrad * M_PI / 180.0;
std::normal_distribution<double> d_winkel(0.0, streuung * 0.01);
double variierterWinkel = winkelRad + d_winkel(gen);
double vx = v0 * std::cos(variierterWinkel);
double vy = v0 * std::sin(variierterWinkel);
double vz = d_winkel(gen) * v0;
double x = 0.0;
double y = 0.0;
double z = 0.0;
double t = 0.0;
double dt = 0.01;
while (y >= 0.0)
{
TrajectoryPoint trajectoryPoint;
trajectoryPoint.time = t;
trajectoryPoint.position.setX(x);
trajectoryPoint.position.setY(y);
trajectoryPoint.position.setZ(z);
trajektorie.push_back(trajectoryPoint);
double v = std::sqrt(vx * vx + vy * vy + vz * vz);
double Fw = 0.5 * rho * v * v * cw * flaeche;
vx += (-(Fw * (vx / v)) / masse) * dt;
vy += (-g - (Fw * (vy / v)) / masse) * dt;
vz += (-(Fw * (vz / v)) / masse) * dt;
x += vx * dt;
y += vy * dt;
z += vz * dt;
t += dt;
if (trajektorie.size() > 20000)
break;
}
return trajektorie;
}
void BallistikSimulator::speichereInCSV( const int pfadId,
const QVector<TrajectoryPoint>& pfad,
const QString& dateiname)
{
QFile datei(dateiname);
if (pfadId==0)
{
if (!datei.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) {
qDebug() << "Datei konnte nicht geöffnet werden";
return;
}
}
else
{
if (!datei.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Append)) {
qDebug() << "Datei konnte nicht geöffnet werden";
return;
}
}
QTextStream out(&datei);
if (pfadId == 0)
out << "id,x,y,z\n";
for (const auto& p : pfad)
{
out << pfadId << ","
<< p.position.x() << ","
<< p.position.y() << ","
<< p.position.z() << "\n";
}
datei.close();
}
@@ -0,0 +1,31 @@
#ifndef BALLISTIKSIMULATOR_H
#define BALLISTIKSIMULATOR_H
#include <random>
#include <QString>
#include <QVector>
#include "trajectorywarp.h"
class BallistikSimulator
{
public:
BallistikSimulator();
QVector<TrajectoryPoint> simuliereFlug(double v0,
double winkelGrad,
double masse,
double cw,
double flaeche,
double streuung);
void speichereInCSV(const int pfadId, const QVector<TrajectoryPoint> &pfad,
const QString &dateiname);
private:
const double g = 9.81;
const double rho = 1.225;
std::mt19937 gen;
};
#endif // BALLISTIKSIMULATOR_H
+69
View File
@@ -0,0 +1,69 @@
#include <QApplication>
#include "ballistiksimulator.h"
#include "trajectorywarp.h"
#include "trajectorywidget.h"
#include <QtMath>
Curve trajectoryPointsToCurve(const QVector<TrajectoryPoint>& trajectory,
const QVector3D& color)
{
Curve curve;
curve.color = color;
curve.points.reserve(trajectory.size());
for (const auto& p : trajectory) {
curve.points.append(p.position);
}
return curve;
}
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
BallistikSimulator sim;
double v0 = 400.0;
double winkel = 20.0;
double masse = 0.015;
double cw = 0.29;
double flaeche = 0.00006;
double praezision = 0.2;
QVector<TrajectoryPoint> original = sim.simuliereFlug(v0, winkel, masse, cw, flaeche, praezision);
sim.speichereInCSV(0,original, "flugbahn.csv");
//-------------------------------------------------------------
TrajectoryWidget widget;
widget.resize(800, 600);
widget.setWindowTitle("3D Trajectory Viewer");
QVector3D originalTarget = original.last().position;
// Ziel streuen
QVector3D newTarget = originalTarget + QVector3D( 5.0, 5.0, 5.0);
auto warped = TrajectoryWarp::warpToNewTarget(original, newTarget);
sim.speichereInCSV(1,warped, "flugbahn.csv");
// Kurve 1
Curve c1 = trajectoryPointsToCurve(original, QVector3D(1, 1, 0));
// Kurve 2
Curve c2 = trajectoryPointsToCurve(warped, QVector3D(0, 1, 1));
QVector<Curve> curves;
curves.append(c1);
curves.append(c2);
widget.setCurves(curves);
widget.show();
return app.exec();
}
+8
View File
@@ -0,0 +1,8 @@
#include "mainwindow.h"
#include "trajectorywidget.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
setCentralWidget(new TrajectoryWidget(this));
}
+11
View File
@@ -0,0 +1,11 @@
#pragma once
#include <QMainWindow>
class TrajectoryWidget;
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
};
+31
View File
@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>600</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralwidget"/>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>800</width>
<height>22</height>
</rect>
</property>
</widget>
<widget class="QStatusBar" name="statusbar"/>
</widget>
<resources/>
<connections/>
</ui>
+54
View File
@@ -0,0 +1,54 @@
#include "trajectorywarp.h"
// Algorithmus: Zeitgewichtete Bézier-Korrektur (empfohlen)
// Kerngedanke
// Die ursprüngliche Trajektorie bleibt die Basis
// Eine glatte Korrekturkurve wird addiert
// Die Korrektur ist:
// 0 am Start
// maximal am Ziel
// zeitlich C¹-stetig
// Formel
// Für jeden Punkt:
// Pneu(t) = Palt(t) + w(t) · ΔZ
// mit
// ΔZ = Zneu − Zalt
// w(t) = 3s² − 2s³ (smoothstep)
// s = (t − t0) / (tEnd − t0)
// Dadurch:
// Start bleibt exakt gleich
// Zielpunkt wird exakt erreicht
// Bewegung bleibt glatt
QVector<TrajectoryPoint> TrajectoryWarp::warpToNewTarget(
const QVector<TrajectoryPoint>& original,
const QVector3D& newTarget)
{
if (original.size() < 2)
return original;
QVector<TrajectoryPoint> result = original;
const QVector3D& oldTarget = original.last().position;
QVector3D delta = newTarget - oldTarget;
double t0 = original.first().time;
double tEnd = original.last().time;
double dt = tEnd - t0;
for (int i = 0; i < result.size(); ++i)
{
double s = (result[i].time - t0) / dt;
s = std::clamp(s, 0.0, 1.0);
double w = smoothStep(s);
result[i].position += delta * w;
}
return result;
}
double TrajectoryWarp::smoothStep(double x)
{
return x * x * (3.0 - 2.0 * x);
}
+22
View File
@@ -0,0 +1,22 @@
#pragma once
#include <QVector>
#include <QVector3D>
struct TrajectoryPoint
{
QVector3D position;
double time;
};
class TrajectoryWarp
{
public:
static QVector<TrajectoryPoint> warpToNewTarget(
const QVector<TrajectoryPoint>& original,
const QVector3D& newTarget
);
private:
static double smoothStep(double x);
};
@@ -0,0 +1,200 @@
#include "trajectorywidget.h"
#include <QMouseEvent>
#include <QMatrix4x4>
#include <QDebug>
#include <limits>
#include <algorithm>
TrajectoryWidget::TrajectoryWidget(QWidget *parent)
: QOpenGLWidget(parent)
{
}
/* ================= OpenGL ================= */
void TrajectoryWidget::initializeGL()
{
initializeOpenGLFunctions();
glEnable(GL_DEPTH_TEST);
const char *vertexShaderSrc = R"(
#version 130
uniform mat4 mvp;
in vec3 position;
void main()
{
gl_Position = mvp * vec4(position, 1.0);
}
)";
const char *fragmentShaderSrc = R"(
#version 130
uniform vec3 color;
out vec4 fragColor;
void main()
{
fragColor = vec4(color, 1.0);
}
)";
if (!m_program.addShaderFromSourceCode(QOpenGLShader::Vertex, vertexShaderSrc))
qDebug() << "Vertex shader error:" << m_program.log();
if (!m_program.addShaderFromSourceCode(QOpenGLShader::Fragment, fragmentShaderSrc))
qDebug() << "Fragment shader error:" << m_program.log();
if (!m_program.link())
qDebug() << "Shader link error:" << m_program.log();
}
void TrajectoryWidget::resizeGL(int w, int h)
{
glViewport(0, 0, w, h);
}
void TrajectoryWidget::paintGL()
{
glClearColor(0.15f, 0.15f, 0.2f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
if (m_curves.isEmpty())
return;
QMatrix4x4 projection;
projection.perspective(
45.0f,
float(width()) / float(height()),
0.01f,
100000.0f
);
QMatrix4x4 view;
view.translate(0, 0, m_zoom);
view.rotate(m_rotX, 1, 0, 0);
view.rotate(m_rotY, 0, 1, 0);
view.translate(-m_viewCenter);
QMatrix4x4 mvp = projection * view;
m_program.bind();
m_program.setUniformValue("mvp", mvp);
for (const auto &curve : m_curves) {
if (curve.points.isEmpty())
continue;
m_program.setUniformValue("color", curve.color);
m_program.enableAttributeArray(0);
m_program.setAttributeArray(
0,
GL_FLOAT,
curve.points.constData(),
3
);
glDrawArrays(GL_LINE_STRIP, 0, curve.points.size());
m_program.disableAttributeArray(0);
}
m_program.release();
}
/* ================= Public API ================= */
void TrajectoryWidget::setCurves(const QVector<Curve> &curves)
{
m_curves = curves;
fitViewToCurves();
update();
}
void TrajectoryWidget::addCurve(const Curve &curve)
{
m_curves.append(curve);
fitViewToCurves();
update();
}
void TrajectoryWidget::clearCurves()
{
m_curves.clear();
update();
}
/* ================= View fitting ================= */
void TrajectoryWidget::fitViewToCurves()
{
if (m_curves.isEmpty())
return;
QVector3D minP(
std::numeric_limits<float>::max(),
std::numeric_limits<float>::max(),
std::numeric_limits<float>::max()
);
QVector3D maxP(
std::numeric_limits<float>::lowest(),
std::numeric_limits<float>::lowest(),
std::numeric_limits<float>::lowest()
);
bool hasPoints = false;
for (const auto &curve : m_curves) {
for (const auto &p : curve.points) {
minP.setX(std::min(minP.x(), p.x()));
minP.setY(std::min(minP.y(), p.y()));
minP.setZ(std::min(minP.z(), p.z()));
maxP.setX(std::max(maxP.x(), p.x()));
maxP.setY(std::max(maxP.y(), p.y()));
maxP.setZ(std::max(maxP.z(), p.z()));
hasPoints = true;
}
}
if (!hasPoints)
return;
m_viewCenter = (minP + maxP) * 0.5f;
float dx = maxP.x() - minP.x();
float dy = maxP.y() - minP.y();
float dz = maxP.z() - minP.z();
float radius = std::max({dx, dy, dz}) * 0.5f;
m_zoom = -radius * 2.5f;
if (m_zoom > -1.0f)
m_zoom = -1.0f;
}
/* ================= Interaction ================= */
void TrajectoryWidget::mousePressEvent(QMouseEvent *event)
{
m_lastMousePos = event->pos();
}
void TrajectoryWidget::mouseMoveEvent(QMouseEvent *event)
{
QPoint delta = event->pos() - m_lastMousePos;
m_lastMousePos = event->pos();
if (event->buttons() & Qt::LeftButton) {
m_rotX += delta.y();
m_rotY += delta.x();
update();
}
}
void TrajectoryWidget::wheelEvent(QWheelEvent *event)
{
m_zoom += event->angleDelta().y() * 0.01f;
update();
}
+47
View File
@@ -0,0 +1,47 @@
#pragma once
#include <QOpenGLWidget>
#include <QOpenGLFunctions>
#include <QOpenGLShaderProgram>
#include <QVector3D>
#include <QVector>
#include <QPoint>
struct Curve {
QVector<QVector3D> points;
QVector3D color;
};
class TrajectoryWidget : public QOpenGLWidget, protected QOpenGLFunctions
{
Q_OBJECT
public:
explicit TrajectoryWidget(QWidget *parent = nullptr);
void setCurves(const QVector<Curve>& curves);
void addCurve(const Curve& curve);
void clearCurves();
protected:
void initializeGL() override;
void resizeGL(int w, int h) override;
void paintGL() override;
void mousePressEvent(QMouseEvent *event) override;
void mouseMoveEvent(QMouseEvent *event) override;
void wheelEvent(QWheelEvent *event) override;
private slots:
void fitViewToCurves();
private:
QVector<Curve> m_curves;
QOpenGLShaderProgram m_program;
float m_rotX = 20.0f;
float m_rotY = -30.0f;
float m_zoom = -10.0f;
QPoint m_lastMousePos;
QVector3D m_viewCenter{0, 0, 0};
};