BornAgain  1.19.79
Simulate and fit neutron and x-ray scattering at grazing incidence
canvas.cpp
Go to the documentation of this file.
1 // ************************************************************************************************
2 //
3 // BornAgain: simulate and fit reflection and scattering
4 //
5 //! @file GUI/ba3d/view/canvas.cpp
6 //! @brief Implements Canvas class
7 //!
8 //! @homepage http://www.bornagainproject.org
9 //! @license GNU General Public License v3 or higher (see COPYING)
10 //! @copyright Forschungszentrum Jülich GmbH 2018
11 //! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS)
12 //
13 // ************************************************************************************************
14 
15 #include "GUI/ba3d/view/canvas.h"
16 #include "Base/Util/Assert.h"
18 #include "GUI/ba3d/model/model.h"
19 #include "GUI/ba3d/view/buffer.h"
20 #include "GUI/ba3d/view/program.h"
21 
22 #include <QMouseEvent>
23 #include <QSysInfo>
24 #include <cmath>
25 #include <cstdlib>
26 
27 namespace {
28 
29 float ZoomInScale()
30 {
31  if (QSysInfo::productType() == "osx")
32  return 1.02f;
33  return 1.25f;
34 }
35 float ZoomOutScale()
36 {
37  if (QSysInfo::productType() == "osx")
38  return 0.98f;
39  return 0.8f;
40 }
41 
42 const float rot_speed_h = 0.4f; // camera rotation speed in horizontal direction
43 const float rot_speed_v = 0.4f; // camera rotation speed in vertical direction
44 
45 // Default camera position in accordance with RealSpaceBuilder.h
46 const float cameraDefaultPosY = -200.0f; // default camera position on Y axis
47 const float cameraDefaultPosZ = 120.0f; // default camera position on Z axis
48 
49 } // namespace
50 
51 namespace GUI::RealSpace {
52 
54  : aspectRatio(1)
55  , colorBgR(1)
56  , colorBgG(1)
57  , colorBgB(1)
58  , currentZoomLevel(0)
59  , camera(nullptr)
60  , program(nullptr)
61  , model(nullptr)
62  , m_isInitializedGL(false)
63 {
65 }
66 
68 {
69  cleanup();
70 }
71 
72 void Canvas::setBgColor(QColor const& c)
73 {
74  colorBgR = float(c.redF());
75  colorBgG = float(c.greenF());
76  colorBgB = float(c.blueF());
77  update();
78 }
79 
81 {
82  camera = c;
83  setCamera();
84 }
85 
87 {
88  program = p;
89  if (program)
90  program->needsInit();
91  update();
92 }
93 
95 {
97 
98  disconnect(modelUpdated);
99  model = m;
100  modelUpdated = connect(model, &Model::updated, [this](bool withEye) {
101  if (withEye)
102  setCamera();
103  else
104  update();
105  });
106 
107  setCamera();
108  // connect(camera, &GUI::RealSpace::Camera::updated, model, &Model::cameraUpdated);
109  camera->set();
110 }
111 
113 {
114  return model;
115 }
116 
117 void Canvas::setCamera(bool full)
118 {
119  if (camera) {
121  if (full && model)
123  }
124 
125  update();
126 }
127 
129 {
130  setCamera((camera = new Camera));
131  setProgram((program = new Program));
132 
133  connect(context(), &QOpenGLContext::aboutToBeDestroyed, this, &Canvas::cleanup);
134 
135  initializeOpenGLFunctions();
136  glEnable(GL_DEPTH_TEST);
137  glEnable(GL_CULL_FACE);
138  m_isInitializedGL = true;
139 }
140 
141 void Canvas::resizeGL(int w, int h)
142 {
143  int w1 = qMax(1, w), h1 = qMax(1, h);
144  viewport.setRect(0, 0, w1, h1);
145  aspectRatio = float(w1) / float(h1);
146  setCamera(false);
147 }
148 
150 {
151  glClearColor(colorBgR, colorBgG, colorBgB, 1);
152  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
153 
154  if (camera && program && model) {
155  program->init();
156  program->bind();
157  program->set(*camera);
158  program->setAxis(false);
159 
160  // opaque objects
161  model->draw(*this);
162 
163  // transparent objects
164  glEnable(GL_BLEND);
165  glDepthMask(false);
166  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
167  model->drawBlend(*this);
168  glDisable(GL_BLEND);
169  glDepthMask(true);
170 
171  if (!model->modelIsEmpty()) {
172  // Draw 3D coordinate axes in lower left corner
173  glViewport(0, 0, viewport.width() / 9, viewport.height() / 5);
174  QMatrix4x4 matObject3DAxes;
175  matObject3DAxes.setToIdentity(); // 3D axes transformation matrix is Identity
176  program->set(matObject3DAxes);
178  program->setAxis(true);
179  std::unique_ptr<Buffer3DAxes> buf3DAxes(new Buffer3DAxes());
180  buf3DAxes->draw3DAxes();
181  }
182 
183  program->release();
184  }
185 }
186 
187 QVector3D Canvas::unproject(QPoint const& p)
188 {
189  float x = p.x(), y = viewport.height() - p.y();
190  return QVector3D(x, y, 1).unproject(matModel, matProj, viewport);
191 }
192 
193 void Canvas::mousePressEvent(QMouseEvent* e)
194 {
195  switch (e->button()) {
196  case Qt::LeftButton:
198  break;
199  case Qt::RightButton:
201  break;
202  default:
204  break;
205  }
206 
207  if (camera) {
210  e_last = e->pos();
211  }
212 }
213 
214 void Canvas::mouseMoveEvent(QMouseEvent* e)
215 {
216  if (camera) {
217  float delta_x = e->pos().x() - e_last.x();
218  float delta_y = e->pos().y() - e_last.y();
219 
220  switch (mouseButton) {
221  case btnTURN: {
222  if (delta_x != 0)
223  horizontalCameraTurn(-delta_x * rot_speed_h); // -ve for consistency with Blender
224 
225  if (delta_y != 0)
226  verticalCameraTurn(-delta_y * rot_speed_v); // -ve for consistency with Blender
227 
228  e_last = e->pos();
229  break;
230  }
231  case btnZOOM: {
232  float d = (e->y() - e_last.y()) / float(viewport.height());
233  camera->zoomBy(1 + d);
234  break;
235  }
236  default:
237  break;
238  }
239 
240  update();
241  }
242 }
243 
244 void Canvas::mouseReleaseEvent(QMouseEvent*)
245 {
246  if (camera) {
247  camera->endTransform(true);
248  update();
249  }
250 }
251 
252 void Canvas::wheelEvent(QWheelEvent* e)
253 {
254  if (camera) {
255  if (e->angleDelta().y() < 0) {
256  // Zoom in
257  camera->zoomBy(ZoomInScale());
258  currentZoomLevel += 1;
259  } else {
260  // Zoom out
261  camera->zoomBy(ZoomOutScale());
262  currentZoomLevel -= 1;
263  }
264  camera->endTransform(true);
265  update();
266  }
267  e->accept(); // disabling the event from propagating further to the parent widgets
268 }
269 
271 {
272  delete buffers.take(g);
273 }
274 
276 {
277  for (auto* b : buffers.values())
278  delete b;
279  buffers.clear();
280 }
281 
282 void Canvas::draw(QColor const& color, QMatrix4x4 const& mat, Geometry const& geo)
283 {
284  auto it = buffers.find(&geo);
285  Buffer* buf;
286  if (buffers.end() == it)
287  buffers.insert(&geo, buf = new Buffer(geo)); // created on demand
288  else
289  buf = *it;
290 
291  ASSERT(program);
292  program->set(color);
293  program->set(mat);
294  buf->draw();
295 }
296 
298 {
299  makeCurrent();
300 
301  releaseBuffers();
302 
303  delete camera;
304  camera = nullptr;
305  delete program;
306  program = nullptr;
307 
308  m_isInitializedGL = false;
309  doneCurrent();
310 }
311 
313 {
314  return m_isInitializedGL && model != nullptr;
315 }
316 
318 {
319  // Default view
320  if (isInitialized()) {
322  GUI::RealSpace::Vector3D(0, cameraDefaultPosY, cameraDefaultPosZ), // eye
323  GUI::RealSpace::Vector3D(0, 0, 0), // center
325 
326  // Default position of camera for 3D axes and object are the same
327  camera->lookAt3DAxes(defPos);
328  camera->lookAt(defPos);
329  camera->endTransform(true);
330 
331  currentZoomLevel = 0; // reset zoom level to default value
332  update();
333  }
334 }
335 
337 {
338  // Side view at current zoom level
339  if (isInitialized()) {
340  GUI::RealSpace::Vector3D eye(0, cameraDefaultPosY, 0);
341 
342  // Side view 3D axes is zoom scale independent
345  GUI::RealSpace::Vector3D(0, 0, 0), // center
347 
348  // Side view 3D object is zoom scale dependent
349  if (currentZoomLevel >= 0)
350  eye.y *= std::pow(ZoomInScale(), std::abs(currentZoomLevel));
351  else
352  eye.y *= std::pow(ZoomOutScale(), std::abs(currentZoomLevel));
353 
355  GUI::RealSpace::Vector3D(0, 0, 0), // center
357 
358  camera->endTransform(true);
359  update();
360  }
361 }
362 
364 {
365  // Top view at current zoom level
366  if (isInitialized()) {
367  // Setting a tiny offset in y value of eye such that eye and up vectors are not parallel
368  GUI::RealSpace::Vector3D eye(0, -0.5, -cameraDefaultPosY);
369 
370  // Top view 3D axes is zoom scale independent
373  GUI::RealSpace::Vector3D(0, 0, 0), // center
375 
376  // Top view 3D object is zoom scale dependent
377  if (currentZoomLevel >= 0)
378  eye.z *= std::pow(ZoomInScale(), std::abs(currentZoomLevel));
379  else
380  eye.z *= std::pow(ZoomOutScale(), std::abs(currentZoomLevel));
381 
383  GUI::RealSpace::Vector3D(0, 0, 0), // center
385 
386  camera->endTransform(true);
387  update();
388  }
389 }
390 
392 {
393  if (isInitialized()) {
394  float theta = angle * static_cast<float>(M_PI / 180.0); // in radians
395 
396  // Horizontal camera turn for 3D axes
397  Camera::Position initial_pos3DAxes = camera->getPos3DAxes();
398 
399  GUI::RealSpace::Vector3D v_eye3DAxes = initial_pos3DAxes.eye; // camera's position vector
400  GUI::RealSpace::Vector3D v_ctr3DAxes = initial_pos3DAxes.ctr;
401  GUI::RealSpace::Vector3D v_up3DAxes = initial_pos3DAxes.up;
402 
403  GUI::RealSpace::Vector3D v_axis3DAxes = v_up3DAxes.normalized(); // normalized rotation axis
404 
405  // Rotating camera's position (eye) about up vector
406  GUI::RealSpace::Vector3D v_rot_eye3DAxes =
407  v_up3DAxes * (1 - std::cos(theta)) * dot(v_axis3DAxes, v_eye3DAxes)
408  + v_eye3DAxes * std::cos(theta) + cross(v_axis3DAxes, v_eye3DAxes) * std::sin(theta);
409 
410  Camera::Position rotated_pos3DAxes(v_rot_eye3DAxes, v_ctr3DAxes, v_up3DAxes);
411 
412  camera->lookAt3DAxes(rotated_pos3DAxes);
413 
414  // Horizontal camera turn for 3D object
415  Camera::Position initial_pos = camera->getPos();
416 
417  GUI::RealSpace::Vector3D v_eye = initial_pos.eye; // camera's position vector
418  GUI::RealSpace::Vector3D v_ctr = initial_pos.ctr;
419  GUI::RealSpace::Vector3D v_up = initial_pos.up;
420 
421  GUI::RealSpace::Vector3D v_axis = v_up.normalized(); // normalized rotation axis
422 
423  // Rotating camera's position (eye) about up vector
424  GUI::RealSpace::Vector3D v_rot_eye = v_up * (1 - std::cos(theta)) * dot(v_axis, v_eye)
425  + v_eye * std::cos(theta)
426  + cross(v_axis, v_eye) * std::sin(theta);
427 
428  Camera::Position rotated_pos(v_rot_eye, v_ctr, v_up);
429 
430  camera->lookAt(rotated_pos);
431 
432  camera->endTransform(true);
433  }
434 }
435 
436 void Canvas::verticalCameraTurn(float angle)
437 {
438  if (isInitialized()) {
439  float theta = angle * static_cast<float>(M_PI / 180.0); // in radians
440 
441  // Vertical camera turn for 3D axes
442  Camera::Position initial_pos3DAxes = camera->getPos3DAxes();
443 
444  GUI::RealSpace::Vector3D v_eye3DAxes = initial_pos3DAxes.eye; // camera's position vector
445  GUI::RealSpace::Vector3D v_ctr3DAxes = initial_pos3DAxes.ctr;
446  GUI::RealSpace::Vector3D v_up3DAxes = initial_pos3DAxes.up;
447 
448  GUI::RealSpace::Vector3D v_axis3DAxes =
449  cross(v_up3DAxes, v_eye3DAxes).normalized(); // normalized rotation axis
450 
451  // Rotating camera's position (eye) about an axis perpendicular to up and eye vectors
452  GUI::RealSpace::Vector3D v_rot_eye3DAxes =
453  v_up3DAxes * (1 - std::cos(theta)) * dot(v_axis3DAxes, v_eye3DAxes)
454  + v_eye3DAxes * std::cos(theta) + cross(v_axis3DAxes, v_eye3DAxes) * std::sin(theta);
455 
456  Camera::Position rotated_pos3DAxes(v_rot_eye3DAxes, v_ctr3DAxes, v_up3DAxes);
457 
458  camera->lookAt3DAxes(rotated_pos3DAxes);
459 
460  // Vertical camera turn for 3D object
461  Camera::Position initial_pos = camera->getPos();
462 
463  GUI::RealSpace::Vector3D v_eye = initial_pos.eye; // camera's position vector
464  GUI::RealSpace::Vector3D v_ctr = initial_pos.ctr;
465  GUI::RealSpace::Vector3D v_up = initial_pos.up;
466 
467  GUI::RealSpace::Vector3D v_axis =
468  cross(v_up, v_eye).normalized(); // normalized rotation axis
469 
470  // Rotating camera's position (eye) about an axis perpendicular to up and eye vectors
471  GUI::RealSpace::Vector3D v_rot_eye = v_up * (1 - std::cos(theta)) * dot(v_axis, v_eye)
472  + v_eye * std::cos(theta)
473  + cross(v_axis, v_eye) * std::sin(theta);
474 
475  Camera::Position rotated_pos(v_rot_eye, v_ctr, v_up);
476 
477  camera->lookAt(rotated_pos);
478 
479  camera->endTransform(true);
480  }
481 }
482 
483 } // namespace GUI::RealSpace
Defines Buffer class.
Defines Canvas class.
Buffer for drawing 3D Coordinate Axes on canvas.
Definition: buffer.h:40
GL buffer.
Definition: buffer.h:28
void endTransform(bool keep)
Definition: camera.cpp:105
QMatrix4x4 matModel
Definition: camera.h:78
void lookAt(const Position &)
camera position for 3D object
Definition: camera.cpp:51
void zoomBy(float)
Definition: camera.cpp:99
QMatrix4x4 matModel3DAxes
Definition: camera.h:79
const Position & getPos() const
Definition: camera.h:50
void lookAt3DAxes(const Position &)
camera position for 3D axes
Definition: camera.cpp:58
QMatrix4x4 matProj
Definition: camera.h:78
const Position & getPos3DAxes() const
Definition: camera.h:51
void setAspectRatio(float)
Definition: camera.cpp:87
void mouseMoveEvent(QMouseEvent *) override
Definition: canvas.cpp:214
void mouseReleaseEvent(QMouseEvent *) override
Definition: canvas.cpp:244
Program * program
Definition: canvas.h:83
void wheelEvent(QWheelEvent *) override
Definition: canvas.cpp:252
QVector3D unproject(QPoint const &)
Definition: canvas.cpp:187
QHash< Geometry const *, Buffer * > buffers
Definition: canvas.h:88
void draw(QColor const &, QMatrix4x4 const &, Geometry const &)
Definition: canvas.cpp:282
enum GUI::RealSpace::Canvas::@0 mouseButton
void mousePressEvent(QMouseEvent *) override
Definition: canvas.cpp:193
void initializeGL() override
Definition: canvas.cpp:128
void releaseBuffer(Geometry const *)
Definition: canvas.cpp:270
void setCamera(Camera *)
Definition: canvas.cpp:80
bool isInitialized() const
Definition: canvas.cpp:312
QMetaObject::Connection modelUpdated
Definition: canvas.h:86
QMatrix4x4 matModel
Definition: canvas.h:73
void paintGL() override
Definition: canvas.cpp:149
void setBgColor(QColor const &)
Definition: canvas.cpp:72
void setProgram(Program *)
Definition: canvas.cpp:86
void verticalCameraTurn(float angle)
Definition: canvas.cpp:436
void horizontalCameraTurn(float angle)
Definition: canvas.cpp:391
void setModel(Model *)
Definition: canvas.cpp:94
~Canvas() override
Definition: canvas.cpp:67
void resizeGL(int, int) override
Definition: canvas.cpp:141
QMatrix4x4 matProj
Definition: canvas.h:73
void deletingGeometry(Geometry const *)
void draw(Canvas &) const
Definition: model.cpp:170
void drawBlend(Canvas &) const
Definition: model.cpp:176
void updated(bool withEye)
Camera::Position defCamPos
default camera params
Definition: model.h:52
void set(Camera const &)
Definition: program.cpp:77
void setAxis(bool const &)
Definition: program.cpp:101
void setMatModel(QMatrix4x4 const &)
Definition: program.cpp:96
Defines Geometry class.
Defines Model class.
Vector3D cross(const Vector3D &v1, const Vector3D &v2)
Definition: def.cpp:67
float dot(const Vector3D &v1, const Vector3D &v2)
Definition: def.cpp:72
GeometryStore & geometryStore()
Definition: geometry.cpp:176
Defines Program class.
static Vector3D const _z
Definition: def.h:46
Vector3D normalized() const
Definition: def.cpp:49