jfjoch_viewer: Work in progress on reciprocal space viewer
This commit is contained in:
@@ -8,6 +8,7 @@
|
||||
#include <QPushButton>
|
||||
#include <QSurfaceFormat>
|
||||
#include <QtMath>
|
||||
#include <algorithm>
|
||||
|
||||
// ============================================================================
|
||||
// Shaders
|
||||
@@ -33,10 +34,13 @@ static const char *kFragSrc = R"(
|
||||
#version 330 core
|
||||
in vec3 vColor;
|
||||
out vec4 fragColor;
|
||||
uniform bool uPointShape;
|
||||
void main() {
|
||||
// Discard corners to draw a circle instead of a square point.
|
||||
vec2 c = gl_PointCoord - vec2(0.5);
|
||||
if (dot(c, c) > 0.25) discard;
|
||||
if (uPointShape) {
|
||||
// Discard corners to draw a circle instead of a square point.
|
||||
vec2 c = gl_PointCoord - vec2(0.5);
|
||||
if (dot(c, c) > 0.25) discard;
|
||||
}
|
||||
fragColor = vec4(vColor, 1.0);
|
||||
}
|
||||
)";
|
||||
@@ -82,6 +86,14 @@ void ReciprocalSpaceGLView::initializeGL() {
|
||||
// Create empty VAO/VBOs so they are valid before the first data arrives.
|
||||
setupVAO(spotsVAO_, spotsVBO_);
|
||||
setupVAO(linesVAO_, linesVBO_);
|
||||
|
||||
glReady_ = true;
|
||||
|
||||
uploadBuffer(spotsVBO_, spotsVAO_, pendingSpots_);
|
||||
spotsCount_ = static_cast<int>(pendingSpots_.size());
|
||||
|
||||
uploadBuffer(linesVBO_, linesVAO_, pendingLines_);
|
||||
linesCount_ = static_cast<int>(pendingLines_.size());
|
||||
}
|
||||
|
||||
void ReciprocalSpaceGLView::resizeGL(int w, int h) {
|
||||
@@ -89,22 +101,30 @@ void ReciprocalSpaceGLView::resizeGL(int w, int h) {
|
||||
proj_.perspective(45.0f, float(w) / float(h ? h : 1), 0.1f, 10000.0f);
|
||||
}
|
||||
|
||||
void ReciprocalSpaceGLView::paintGL() {
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
// Build view matrix from orbit camera state.
|
||||
QMatrix4x4 ReciprocalSpaceGLView::currentViewMatrix() const {
|
||||
QMatrix4x4 view;
|
||||
view.translate(0, 0, -zoom_);
|
||||
view.rotate(pitch_, 1, 0, 0);
|
||||
view.rotate(yaw_, 0, 1, 0);
|
||||
view.translate(-target_);
|
||||
return view;
|
||||
}
|
||||
|
||||
const QMatrix4x4 mvp = proj_ * view;
|
||||
QMatrix4x4 ReciprocalSpaceGLView::currentMvpMatrix() const {
|
||||
return proj_ * currentViewMatrix();
|
||||
}
|
||||
|
||||
void ReciprocalSpaceGLView::paintGL() {
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
const QMatrix4x4 mvp = currentMvpMatrix();
|
||||
|
||||
shader_.bind();
|
||||
shader_.setUniformValue("uMVP", mvp);
|
||||
|
||||
// Draw axes + cell vectors
|
||||
if (linesCount_ > 0) {
|
||||
shader_.setUniformValue("uPointShape", false);
|
||||
linesVAO_.bind();
|
||||
glDrawArrays(GL_LINES, 0, linesCount_);
|
||||
linesVAO_.release();
|
||||
@@ -112,6 +132,7 @@ void ReciprocalSpaceGLView::paintGL() {
|
||||
|
||||
// Draw spots
|
||||
if (spotsCount_ > 0) {
|
||||
shader_.setUniformValue("uPointShape", true);
|
||||
spotsVAO_.bind();
|
||||
glDrawArrays(GL_POINTS, 0, spotsCount_);
|
||||
spotsVAO_.release();
|
||||
@@ -123,18 +144,28 @@ void ReciprocalSpaceGLView::paintGL() {
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
void ReciprocalSpaceGLView::setSpots(const std::vector<Vertex> &spots) {
|
||||
makeCurrent();
|
||||
uploadBuffer(spotsVBO_, spotsVAO_, spots);
|
||||
spotsCount_ = static_cast<int>(spots.size());
|
||||
doneCurrent();
|
||||
pendingSpots_ = spots;
|
||||
spotsCount_ = static_cast<int>(pendingSpots_.size());
|
||||
|
||||
if (glReady_) {
|
||||
makeCurrent();
|
||||
uploadBuffer(spotsVBO_, spotsVAO_, pendingSpots_);
|
||||
doneCurrent();
|
||||
}
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
void ReciprocalSpaceGLView::setLines(const std::vector<Vertex> &lines) {
|
||||
makeCurrent();
|
||||
uploadBuffer(linesVBO_, linesVAO_, lines);
|
||||
linesCount_ = static_cast<int>(lines.size());
|
||||
doneCurrent();
|
||||
pendingLines_ = lines;
|
||||
linesCount_ = static_cast<int>(pendingLines_.size());
|
||||
|
||||
if (glReady_) {
|
||||
makeCurrent();
|
||||
uploadBuffer(linesVBO_, linesVAO_, pendingLines_);
|
||||
doneCurrent();
|
||||
}
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
@@ -185,6 +216,15 @@ void ReciprocalSpaceGLView::mousePressEvent(QMouseEvent *e) {
|
||||
lastMousePos_ = e->pos();
|
||||
}
|
||||
|
||||
void ReciprocalSpaceGLView::mouseDoubleClickEvent(QMouseEvent *e) {
|
||||
if (e->button() == Qt::LeftButton && focusNearestSpot(e->pos())) {
|
||||
e->accept();
|
||||
return;
|
||||
}
|
||||
|
||||
QOpenGLWidget::mouseDoubleClickEvent(e);
|
||||
}
|
||||
|
||||
void ReciprocalSpaceGLView::mouseMoveEvent(QMouseEvent *e) {
|
||||
const QPoint delta = e->pos() - lastMousePos_;
|
||||
lastMousePos_ = e->pos();
|
||||
@@ -194,6 +234,19 @@ void ReciprocalSpaceGLView::mouseMoveEvent(QMouseEvent *e) {
|
||||
pitch_ += delta.y() * 0.5f;
|
||||
pitch_ = qBound(-89.0f, pitch_, 89.0f);
|
||||
update();
|
||||
} else if (e->buttons() & Qt::MiddleButton) {
|
||||
const float panScale = zoom_ * 0.0015f;
|
||||
|
||||
QMatrix4x4 rot;
|
||||
rot.rotate(pitch_, 1, 0, 0);
|
||||
rot.rotate(yaw_, 0, 1, 0);
|
||||
|
||||
const QVector3D right = rot.inverted().mapVector(QVector3D(1, 0, 0));
|
||||
const QVector3D up = rot.inverted().mapVector(QVector3D(0, 1, 0));
|
||||
|
||||
target_ -= right * float(delta.x()) * panScale;
|
||||
target_ += up * float(delta.y()) * panScale;
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -203,6 +256,49 @@ void ReciprocalSpaceGLView::wheelEvent(QWheelEvent *e) {
|
||||
update();
|
||||
}
|
||||
|
||||
bool ReciprocalSpaceGLView::focusNearestSpot(const QPoint &screenPos) {
|
||||
if (pendingSpots_.empty() || width() <= 0 || height() <= 0)
|
||||
return false;
|
||||
|
||||
const QMatrix4x4 mvp = currentMvpMatrix();
|
||||
|
||||
float bestDist2 = std::numeric_limits<float>::max();
|
||||
QVector3D bestPos;
|
||||
bool found = false;
|
||||
|
||||
for (const auto &v : pendingSpots_) {
|
||||
const QVector4D clip = mvp * QVector4D(v.x, v.y, v.z, 1.0f);
|
||||
if (clip.w() <= 0.0f)
|
||||
continue;
|
||||
|
||||
const QVector3D ndc = clip.toVector3DAffine();
|
||||
if (ndc.z() < -1.0f || ndc.z() > 1.0f)
|
||||
continue;
|
||||
|
||||
const float sx = (ndc.x() * 0.5f + 0.5f) * float(width());
|
||||
const float sy = (0.5f - ndc.y() * 0.5f) * float(height());
|
||||
|
||||
const float dx = sx - float(screenPos.x());
|
||||
const float dy = sy - float(screenPos.y());
|
||||
const float dist2 = dx * dx + dy * dy;
|
||||
|
||||
if (dist2 < bestDist2) {
|
||||
bestDist2 = dist2;
|
||||
bestPos = QVector3D(v.x, v.y, v.z);
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Roughly 15 px picking radius.
|
||||
if (!found || bestDist2 > 15.0f * 15.0f)
|
||||
return false;
|
||||
|
||||
target_ = bestPos;
|
||||
zoom_ = qMax(5.0f, zoom_ * 0.35f);
|
||||
update();
|
||||
return true;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// JFJochViewerReciprocalSpaceWindow
|
||||
// ============================================================================
|
||||
@@ -271,9 +367,7 @@ void JFJochViewerReciprocalSpaceWindow::datasetLoaded(
|
||||
rebuildGL();
|
||||
}
|
||||
|
||||
void JFJochViewerReciprocalSpaceWindow::imageLoaded(
|
||||
std::shared_ptr<const JFJochReaderImage> image)
|
||||
{
|
||||
void JFJochViewerReciprocalSpaceWindow::imageLoaded(std::shared_ptr<const JFJochReaderImage> image) {
|
||||
if (!accumulateCheck->isChecked()) {
|
||||
spots_.clear();
|
||||
indexed_lattice_.reset();
|
||||
@@ -347,7 +441,7 @@ void JFJochViewerReciprocalSpaceWindow::rebuildGL() {
|
||||
|
||||
// --- Spots ---------------------------------------------------------------
|
||||
std::vector<ReciprocalSpaceGLView::Vertex> spotVerts;
|
||||
spotVerts.reserve(spots_.size());
|
||||
spotVerts.reserve(spots_.size() + 1);
|
||||
|
||||
// Precompute the three possible colors as floats — avoids QColor in the loop.
|
||||
auto toF = [](const QColor &c, float &r, float &g, float &b) {
|
||||
@@ -370,6 +464,8 @@ void JFJochViewerReciprocalSpaceWindow::rebuildGL() {
|
||||
r, g, b });
|
||||
}
|
||||
|
||||
spotVerts.push_back({ 0, 0, 0, 1.0, 1.0, 1.0 });
|
||||
|
||||
// --- Lines: axes + optional cell vectors ---------------------------------
|
||||
// Each segment = 2 vertices (GL_LINES draws pairs).
|
||||
std::vector<ReciprocalSpaceGLView::Vertex> lineVerts;
|
||||
@@ -383,10 +479,10 @@ void JFJochViewerReciprocalSpaceWindow::rebuildGL() {
|
||||
};
|
||||
|
||||
// Reference axes
|
||||
const float len = 10.0f;
|
||||
addLine({0,0,0}, {len,0,0}, QColor(180, 60, 60));
|
||||
addLine({0,0,0}, {0,len,0}, QColor(60, 180, 60));
|
||||
addLine({0,0,0}, {0,0,len}, QColor(60, 60, 180));
|
||||
const float len = 20.0f;
|
||||
addLine({0,0,0}, {len,0,0}, QColor(255, 60, 60));
|
||||
addLine({0,0,0}, {0,len,0}, QColor(60, 255, 60));
|
||||
addLine({0,0,0}, {0,0,len}, QColor(60, 60, 255));
|
||||
|
||||
// Reciprocal cell vectors
|
||||
if (showCellCheck->isChecked() && indexed_lattice_) {
|
||||
|
||||
@@ -46,6 +46,7 @@ protected:
|
||||
void paintGL() override;
|
||||
|
||||
void mousePressEvent(QMouseEvent *e) override;
|
||||
void mouseDoubleClickEvent(QMouseEvent *e) override;
|
||||
void mouseMoveEvent(QMouseEvent *e) override;
|
||||
void wheelEvent(QWheelEvent *e) override;
|
||||
|
||||
@@ -54,21 +55,29 @@ private:
|
||||
const std::vector<Vertex> &data);
|
||||
void setupVAO(QOpenGLVertexArrayObject &vao, QOpenGLBuffer &vbo);
|
||||
|
||||
QMatrix4x4 currentViewMatrix() const;
|
||||
QMatrix4x4 currentMvpMatrix() const;
|
||||
bool focusNearestSpot(const QPoint &screenPos);
|
||||
|
||||
QOpenGLShaderProgram shader_;
|
||||
bool glReady_ = false;
|
||||
|
||||
QOpenGLVertexArrayObject spotsVAO_;
|
||||
QOpenGLBuffer spotsVBO_;
|
||||
int spotsCount_ = 0;
|
||||
std::vector<Vertex> pendingSpots_;
|
||||
|
||||
QOpenGLVertexArrayObject linesVAO_;
|
||||
QOpenGLBuffer linesVBO_;
|
||||
int linesCount_ = 0;
|
||||
std::vector<Vertex> pendingLines_;
|
||||
|
||||
// Camera state
|
||||
QMatrix4x4 proj_;
|
||||
float yaw_ = 0.0f; // degrees
|
||||
float pitch_ = 20.0f; // degrees
|
||||
float zoom_ = 80.0f; // distance from origin
|
||||
float zoom_ = 80.0f; // distance from camera target
|
||||
QVector3D target_{0.0f, 0.0f, 0.0f};
|
||||
QPoint lastMousePos_;
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user