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