summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorStanislaw Halik <sthalik@misaki.pl>2020-11-20 02:22:09 +0000
committerGitHub <noreply@github.com>2020-11-20 02:22:09 +0000
commitf0e7870d66bbaf42ec0f1cd03dcc2da0dee6dd56 (patch)
tree93fe9db7a734d49b398fd876c73c82daf507a9e6
parent3cc3ef246ad71c463c8952bcc96984b25d85b516 (diff)
parent354fff96ffa06753c69a721ad017d621e95927d0 (diff)
Merge pull request #1173 from GO63-samara/unstable
Fix display of the Octopus pose in the Pose-widget.
-rw-r--r--pose-widget/ReadMe.txt26
-rw-r--r--pose-widget/pose-widget.cpp131
-rw-r--r--pose-widget/pose-widget.hpp10
3 files changed, 137 insertions, 30 deletions
diff --git a/pose-widget/ReadMe.txt b/pose-widget/ReadMe.txt
new file mode 100644
index 00000000..c504ff41
--- /dev/null
+++ b/pose-widget/ReadMe.txt
@@ -0,0 +1,26 @@
+Hi everyone!
+
+In the extreme version of OpenTrack-2.3.12, the Octopus pose is still displayed incorrectly.
+The pose-widget is responsible for displaying the Octopus pose in OpenTrack. I have fixed this widget.
+
+Fixed bugs:
+- The turns and movements of the Octopussy are now performed truly independently of each other, as it should be in 6DOF.
+- When cornering, there is no "gimbal lock" at Pitch = +/- 90 degrees.
+- Fixed directions of axes of rotations and positions. Now the Octopus pose displays the actual direction of view on the plane.
+- Fixed display of the back (green) surface of the Octopus. Previously, it was displayed mirrored.
+
+Additional features:
+- Applied "perspective projection", the picture becomes more voluminous.
+- Added lighting effect from above, with the same purpose.
+- Added background fill for the widget. This makes it possible to see the borders of the widget.
+- Added X and Y axes. This helps to estimate how far the Octopus is deviated from the center.
+- Added [Mirror] checkbox, mirroring positions and rotations. It is often more convenient to observe the Octopussy's mirror pose.
+- If before compilation in the file "pose-widget.hpp" include line 19: "#define TEST", then a rectangular frame will be drawn around the Octopus. This is useful when testing a pose-widget to assess distortion and size.
+
+The corrected pose-widget now displays the Octopus pose correctly and can be used to check OpenTrack settings, sometimes even without launching the flight simulator.
+
+A video of the corrected pose-widget is available here:
+
+https://youtu.be/my4_VOwGmq4
+
+fixed by GO63-samara <go1@list.ru> <github.com/GO63-samara> 2020
diff --git a/pose-widget/pose-widget.cpp b/pose-widget/pose-widget.cpp
index 3ad475cf..c5a6b78e 100644
--- a/pose-widget/pose-widget.cpp
+++ b/pose-widget/pose-widget.cpp
@@ -13,12 +13,40 @@
#include <QtEvents>
#include <QDebug>
-#include <QImage>
+#include <QQuaternion>
+#include <QMatrix4x4>
namespace pose_widget_impl {
pose_widget::pose_widget(QWidget* parent) : QWidget(parent)
{
+ QPainter p;
+#ifdef TEST
+ //draw rectangle frame around of Octopus, only if TEST defined
+ p.begin(&front);
+ p.setPen(QPen(Qt::red, 3, Qt::SolidLine));
+ p.drawRect(0, 0, front.width()-1, front.height()-1);
+ p.end();
+
+ p.begin(&back);
+ p.setPen(QPen(Qt::darkGreen, 3, Qt::SolidLine));
+ p.drawRect(0, 0, back.width()-1, back.height()-1);
+ p.end();
+#endif
+
+ //draw Octopus shine
+ shine.fill(QColor(255,255,255));
+ p.begin(&shine);
+ p.setCompositionMode(QPainter::CompositionMode_DestinationIn);
+ p.drawImage(QPointF(0,0), front);
+ p.end();
+
+ //draw Octopus shadow
+ shadow.fill(QColor(0,0,0));
+ p.begin(&shadow);
+ p.setCompositionMode(QPainter::CompositionMode_DestinationIn);
+ p.drawImage(QPointF(0,0), front);
+ p.end();
}
void pose_widget::present(double yaw, double pitch, double roll, double x, double y, double z)
@@ -29,40 +57,87 @@ void pose_widget::present(double yaw, double pitch, double roll, double x, doubl
repaint();
}
+void pose_widget::resizeEvent(QResizeEvent *event)
+{
+ // adapt to widget size
+ float w = event->size().width();
+ float h = event->size().height();
+
+ // fill background by color
+ constexpr int clr = 220;
+ QImage background(QImage(w, h, QImage::Format_ARGB32));
+ background.fill(QColor(clr,clr,clr));
+
+ // draw axes
+ QPainter p(&background);
+ p.setPen(QPen(Qt::gray, 1, Qt::SolidLine));
+ p.drawLine(0.5*w, 0 , 0.5*w, h );
+ p.drawLine( 0 , 0.5*h, w , 0.5*h);
+
+ // set AutoFillBackground
+ QPalette palette;
+ palette.setBrush(this->backgroundRole(), QBrush(background));
+ setPalette(palette);
+ setAutoFillBackground(true);
+
+ // move the mirror checkbox in the lower right corner of the widget
+ mirror.setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
+ mirror.move(w - mirror.width(), h - mirror.height());
+}
+
void pose_widget::paintEvent(QPaintEvent*)
{
+ // widget settings:
+ constexpr float scale = 0.5; // scale of Octopus height, when x = y = z = 0.0
+ constexpr float XYZmax = 50.0; // -XYZmax < x,y,z < +XYZmax (offset the Octopus by one body)
+ constexpr float Kz = 0.25; // Z scale change limit (simulate camera focus length)
+
+ // get a local copy of input data
auto [ yaw, pitch, roll ] = R;
auto [ x, y, z ] = T;
- const QImage& img = (std::fabs(pitch) > 90) ^ (std::fabs(yaw) > 90)
- ? back
- : front;
-
- constexpr double scale = .75;
- int w = img.width(), h = iround(img.height()*scale);
-
- QTransform t;
-
- t.translate(w*.5, h*.5);
-
- constexpr double z_scale = 1./250;
- constexpr double xy_scale = .0075;
- double xy = std::sqrt(w*w + h*h) * xy_scale;
- double sx = clamp(.4 + -z * z_scale, .1, 2), sy = sx * 1/scale;
-
- t.scale(sx, sy);
-
- t.rotate(pitch, Qt::XAxis);
- t.rotate(yaw, Qt::YAxis);
- t.rotate(roll, Qt::ZAxis);
-
- t.translate(x * xy / sx, y * xy / sy);
-
- t.translate(w*-.5, h*-.5);
-
QPainter p(this);
+ #ifdef TEST
+ // use antialiasing for correct frame around the Octopus, only if TEST defined
+ p.setRenderHint(QPainter::Antialiasing, true);
+ #endif
+
+ // check mirror state
+ if (mirror.checkState() == Qt::Checked) x = -x;
+ else { yaw = -yaw; roll = -roll; }
+ y = -y;
+
+ // rotations
+ QQuaternion q = QQuaternion::fromEulerAngles(pitch, yaw, roll);
+ QMatrix4x4 m = QMatrix4x4(q.toRotationMatrix());
+
+ // x and y positions
+ const float Kxy = (float)front.height() / XYZmax;
+ QVector3D v(Kxy*x, Kxy*y, 0.0);
+ v = m.transposed().map(v);
+ m.translate(v);
+
+ // perspective projection to x-y plane
+ QTransform t = m.toTransform(1024).translate(-.5 * front.width(), -.5 * front.height());
+
+ // z position by setViewport
+ const float mz = scale * height()/front.height()/exp(1.0) * exp(1.0 - z * (Kz/XYZmax));
+ p.setViewport(QRect(.5 * width(), .5 * height(), width()*mz, height()*mz));
+
+ // define forward or backward side by cross product of mapped x and y axes
+ QPointF point0 = t.map(QPointF(0, 0));
+ QPointF x_dir = (t.map(QPointF(1, 0)) -= point0);
+ QPointF y_dir = (t.map(QPointF(0, 1)) -= point0);
+ const bool forward = x_dir.ry()*y_dir.rx() - x_dir.rx()*y_dir.ry() < 0 ? true : false;
+
+ // draw red or green Octopus
p.setTransform(t);
- p.drawImage(rect(), img);
+ p.drawImage(QPointF(0,0), forward ? front : back);
+
+ // top lighting simulation
+ const float alpha = sin(pitch * M_PI / 180.0);
+ p.setOpacity(0.333 * fabs(alpha));
+ p.drawImage(QPointF(0,0), forward == (alpha >= 0.0) ? shine : shadow);
}
QSize pose_widget::sizeHint() const
diff --git a/pose-widget/pose-widget.hpp b/pose-widget/pose-widget.hpp
index b3267ff9..49044d93 100644
--- a/pose-widget/pose-widget.hpp
+++ b/pose-widget/pose-widget.hpp
@@ -14,7 +14,9 @@
#include <QWidget>
#include <QImage>
+#include <QCheckBox>
+//#define TEST
namespace pose_widget_impl {
using namespace euler;
@@ -25,13 +27,17 @@ public:
pose_widget(QWidget *parent = nullptr);
void present(double xAngle, double yAngle, double zAngle, double x, double y, double z);
QSize sizeHint() const override;
-
+ QCheckBox mirror{QCheckBox{"Mirror", this}};
private:
+ void resizeEvent(QResizeEvent *event) override;
void paintEvent(QPaintEvent*) override;
Pose_ R, T;
QImage front{QImage{":/images/side1.png"}.convertToFormat(QImage::Format_ARGB32)};
- QImage back{QImage{":/images/side6.png"}.convertToFormat(QImage::Format_ARGB32)};
+ QImage back {QImage{":/images/side6.png"}.convertToFormat(QImage::Format_ARGB32)
+ .mirrored(true,false)};
+ QImage shine {QImage{front.width(), front.height(), QImage::Format_ARGB32}};
+ QImage shadow{QImage{front.width(), front.height(), QImage::Format_ARGB32}};
};
}