diff --git a/src/lay/lay/layResources.qrc b/src/lay/lay/layResources.qrc index abaeec830..195eb763e 100644 --- a/src/lay/lay/layResources.qrc +++ b/src/lay/lay/layResources.qrc @@ -127,6 +127,12 @@ images/folder_12.png images/file_12.png images/empty_12.png + images/fit_front.png + images/fit_back.png + images/fit_left.png + images/fit_right.png + images/fit_top.png + images/fit_bottom.png syntax/ruby.xml diff --git a/src/plugins/tools/view_25d/lay_plugin/D25View.ui b/src/plugins/tools/view_25d/lay_plugin/D25View.ui index 3e8f0fc83..efdbd6365 100644 --- a/src/plugins/tools/view_25d/lay_plugin/D25View.ui +++ b/src/plugins/tools/view_25d/lay_plugin/D25View.ui @@ -6,8 +6,8 @@ 0 0 - 576 - 649 + 854 + 665 @@ -29,17 +29,249 @@ 9 + + + + + 0 + 0 + + + + QFrame::NoFrame + + + QFrame::Raised + + + + 6 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::Horizontal + + + + + + + + 0 + 0 + + + + + 60 + 0 + + + + % + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 5 + 20 + + + + + + + + ... + + + + :/fit_left.png:/fit_left.png + + + + 24 + 24 + + + + true + + + + + + + ... + + + + :/fit_front.png:/fit_front.png + + + + 24 + 24 + + + + true + + + + + + + ... + + + + :/fit_right.png:/fit_right.png + + + + 24 + 24 + + + + true + + + + + + + ... + + + + :/fit_back.png:/fit_back.png + + + + 24 + 24 + + + + true + + + + + + + ... + + + + :/fit_top.png:/fit_top.png + + + + 24 + 24 + + + + true + + + + + + + ... + + + + :/fit_bottom.png:/fit_bottom.png + + + + 24 + 24 + + + + true + + + + + + - - - Qt::Horizontal + + + + 0 + 0 + - - QDialogButtonBox::Close + + QFrame::NoFrame + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Press and hold SHIFT for top view + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Close + + + + @@ -51,10 +283,9 @@
layD25ViewWidget.h
- - buttonBox - - + + + buttonBox diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25Camera.cc b/src/plugins/tools/view_25d/lay_plugin/layD25Camera.cc new file mode 100644 index 000000000..3d8fb12ef --- /dev/null +++ b/src/plugins/tools/view_25d/lay_plugin/layD25Camera.cc @@ -0,0 +1,113 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2020 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#include "layD25Camera.h" + +#include + +#include "math.h" + +namespace lay +{ + +D25Camera::D25Camera () +{ + init (); +} + +D25Camera::~D25Camera () +{ + // .. nothing yet .. +} + +void +D25Camera::init () +{ + m_fov = 90.0; + m_cam_azimuth = m_cam_elevation = 0.0; + m_top_view = false; +} + +void +D25Camera::camera_reset () +{ + init (); + camera_changed (); +} + +double +D25Camera::cam_fov () const +{ + return m_fov; // @@@ +} + +double +D25Camera::cam_dist () const +{ + return 4.0; // @@@ +} + +QVector3D +D25Camera::cam_direction () const +{ + return cam_trans ().inverted ().map (QVector3D (0, 0, -1)); +} + +QVector3D +D25Camera::cam_position () const +{ + return cam_direction () * -cam_dist (); +} + +double +D25Camera::cam_azimuth () const +{ + return m_cam_azimuth; +} + +double +D25Camera::cam_elevation () const +{ + return m_top_view ? -90.0 : m_cam_elevation; +} + +QMatrix4x4 +D25Camera::cam_perspective () const +{ + QMatrix4x4 t; + t.perspective (cam_fov (), aspect_ratio (), 0.1f, 10000.0f); + t.translate (QVector3D (0.0, 0.0, -cam_dist ())); + return t; +} + +QMatrix4x4 +D25Camera::cam_trans () const +{ + QMatrix4x4 t; + t.rotate (-cam_elevation (), 1.0, 0.0, 0.0); + t.rotate (cam_azimuth (), 0.0, 1.0, 0.0); + return t; +} + +} + diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25Camera.h b/src/plugins/tools/view_25d/lay_plugin/layD25Camera.h new file mode 100644 index 000000000..22dd780e4 --- /dev/null +++ b/src/plugins/tools/view_25d/lay_plugin/layD25Camera.h @@ -0,0 +1,142 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2020 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + +#ifndef HDR_layD25Camera +#define HDR_layD25Camera + +#include "layPluginCommon.h" + +#include +#include + +namespace lay +{ + +class LAY_PLUGIN_PUBLIC D25Camera +{ +public: + D25Camera (); + virtual ~D25Camera (); + + /** + * @brief Gets the position of the camera objective in the scene coordinate system + */ + QVector3D cam_position () const; + + /** + * @brief Gets the direction the camera looks into in the scene coordinate system + */ + QVector3D cam_direction () const; + + /** + * @brief Gets the perspective part of the transformation applied transform scene coordinates into the image plane + * The full transformation for scene to image plane is cam_perspective * cam_trans. + */ + QMatrix4x4 cam_perspective () const; + + /** + * @brief Gets the azimuth/elevation part of the transformation applied transform scene coordinates into the image plane + * The full transformation for scene to image plane is cam_perspective * cam_trans. + */ + QMatrix4x4 cam_trans () const; + + /** + * @brief Gets the distance of the objective in scene coordinates + */ + double cam_dist () const; + + /** + * @brief Gets the field of view of the camera + * The field of view is the objective opening angle. + */ + double cam_fov () const; + + /** + * @brief Gets a flag indicating whether top view is enabled + * In "top view" mode, the elevation is fixed to -90 degree. + */ + bool top_view () const + { + return m_top_view; + } + + /** + * @brief Sets a flag indicating whether top view is enabled + */ + void set_top_view (bool f) + { + m_top_view = f; + camera_changed (); + } + + /** + * @brief Gets the elevation angle + * A negative angle means the camera looks down, a positive angle means it looks up. + */ + double cam_elevation () const; + + /** + * @brief Sets the elevation angle + */ + void set_cam_elevation (double e) + { + m_cam_elevation = e; + camera_changed (); + } + + /** + * @brief Gets the azimuth angle + * ... + */ + double cam_azimuth () const; + + /** + * @brief Sets the azimuth angle + */ + void set_cam_azimuth (double a) + { + m_cam_azimuth = a; + camera_changed (); + } + + /** + * @brief Resets the camera's orientation + */ + void camera_reset (); + +protected: + virtual void camera_changed () { } + virtual double aspect_ratio () const { return 1.0; } + +private: + double m_cam_azimuth, m_cam_elevation; + bool m_top_view; + QVector3D m_displacement; + double m_fov; + + void init (); +}; + +} + +#endif + diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25MemChunks.cc b/src/plugins/tools/view_25d/lay_plugin/layD25MemChunks.cc index c80a2d5a7..dfe8fb146 100644 --- a/src/plugins/tools/view_25d/lay_plugin/layD25MemChunks.cc +++ b/src/plugins/tools/view_25d/lay_plugin/layD25MemChunks.cc @@ -21,3 +21,4 @@ */ #include "layD25MemChunks.h" + diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25MemChunks.h b/src/plugins/tools/view_25d/lay_plugin/layD25MemChunks.h index 034aa5377..945d1338d 100644 --- a/src/plugins/tools/view_25d/lay_plugin/layD25MemChunks.h +++ b/src/plugins/tools/view_25d/lay_plugin/layD25MemChunks.h @@ -24,6 +24,7 @@ #define HDR_layD25MemChunks #include +#include #include "tlObject.h" #include // for memcpy @@ -31,6 +32,19 @@ namespace lay { +template +struct gl_type2enum +{ + GLenum operator() () const; +}; + +template <> +struct gl_type2enum +{ + GLenum operator() () const { return GL_FLOAT; } +}; + + /** * @brief Provides a semi-contiguous array of objects * @@ -201,6 +215,25 @@ public: mp_last_chunk->m_objects [mp_last_chunk->m_len++] = element; } + /** + * @brief Adds two elements + */ + void add (const Obj &e1, const Obj &e2) + { + add (e1); + add (e2); + } + + /** + * @brief Adds three elements + */ + void add (const Obj &e1, const Obj &e2, const Obj &e3) + { + add (e1); + add (e2); + add (e3); + } + /** * @brief begin iterator */ @@ -211,6 +244,17 @@ public: */ iterator end () const { return iterator (); } + /** + * @brief Draw to the given context + */ + void draw_to (QOpenGLFunctions *ctx, GLuint location, GLenum mode) const + { + for (iterator c = begin (); c != end (); ++c) { + ctx->glVertexAttribPointer (location, 3, gl_type2enum () (), GL_FALSE, 0, c->front ()); + ctx->glDrawArrays (mode, 0, c->size () / 3); + } + } + private: chunk *mp_chunks; chunk *mp_last_chunk; diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25View.cc b/src/plugins/tools/view_25d/lay_plugin/layD25View.cc index fdb5230d9..aa6c587a4 100644 --- a/src/plugins/tools/view_25d/lay_plugin/layD25View.cc +++ b/src/plugins/tools/view_25d/lay_plugin/layD25View.cc @@ -31,6 +31,8 @@ namespace lay { +const double initial_elevation = 3.0; + D25View::D25View (QWidget *parent) : QDialog (parent) { @@ -41,6 +43,13 @@ D25View::D25View (QWidget *parent) mp_ui->d25_view->setFocusPolicy (Qt::StrongFocus); mp_ui->d25_view->setFocus (); // @@@ + + connect (mp_ui->fit_back, SIGNAL (clicked ()), this, SLOT (fit_button_clicked ())); + connect (mp_ui->fit_front, SIGNAL (clicked ()), this, SLOT (fit_button_clicked ())); + connect (mp_ui->fit_left, SIGNAL (clicked ()), this, SLOT (fit_button_clicked ())); + connect (mp_ui->fit_right, SIGNAL (clicked ()), this, SLOT (fit_button_clicked ())); + connect (mp_ui->fit_top, SIGNAL (clicked ()), this, SLOT (fit_button_clicked ())); + connect (mp_ui->fit_bottom, SIGNAL (clicked ()), this, SLOT (fit_button_clicked ())); } D25View::~D25View () @@ -55,6 +64,10 @@ D25View::exec_dialog (lay::LayoutView *view) mp_view.reset (view); mp_ui->d25_view->attach_view (view); + mp_ui->d25_view->reset (); + mp_ui->d25_view->set_cam_azimuth (0.0); + mp_ui->d25_view->set_cam_elevation (initial_elevation); + int ret = QDialog::exec (); mp_ui->d25_view->attach_view (0); @@ -62,6 +75,36 @@ D25View::exec_dialog (lay::LayoutView *view) return ret; } +void +D25View::fit_button_clicked () +{ + double azimuth = mp_ui->d25_view->cam_azimuth (); + double elevation = mp_ui->d25_view->cam_elevation (); + + if (sender () == mp_ui->fit_back) { + azimuth = -180.0; + elevation = -initial_elevation; + } else if (sender () == mp_ui->fit_front) { + azimuth = 0.0; + elevation = -initial_elevation; + } else if (sender () == mp_ui->fit_left) { + azimuth = 90.0; + elevation = -initial_elevation; + } else if (sender () == mp_ui->fit_right) { + azimuth = -90.0; + elevation = -initial_elevation; + } else if (sender () == mp_ui->fit_top) { + elevation = -90; + } else if (sender () == mp_ui->fit_bottom) { + elevation = 90; + } + + mp_ui->d25_view->set_cam_azimuth (azimuth); + mp_ui->d25_view->set_cam_elevation (elevation); + + mp_ui->d25_view->fit (); +} + void D25View::accept () { diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25View.h b/src/plugins/tools/view_25d/lay_plugin/layD25View.h index 78526bd36..207ca7751 100644 --- a/src/plugins/tools/view_25d/lay_plugin/layD25View.h +++ b/src/plugins/tools/view_25d/lay_plugin/layD25View.h @@ -54,6 +54,9 @@ public: protected: void accept (); +private slots: + void fit_button_clicked (); + private: Ui::D25View *mp_ui; tl::weak_ptr mp_view; diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25ViewUtils.cc b/src/plugins/tools/view_25d/lay_plugin/layD25ViewUtils.cc index 23cb6c951..3fb6e8dca 100644 --- a/src/plugins/tools/view_25d/lay_plugin/layD25ViewUtils.cc +++ b/src/plugins/tools/view_25d/lay_plugin/layD25ViewUtils.cc @@ -65,7 +65,7 @@ cutpoint_line_with_face (const QVector3D &line, const QVector3D &dir, const QVec static bool somewhat_perpendicular (const QVector3D &a, const QVector3D &b) { - // returns true if a and b are perpendicular within 30 degree + // returns true if a and b are perpendicular within +/-30 degree return fabs (QVector3D::dotProduct (a, b)) < 0.5 * a.length () * b.length (); } diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc index fa6edca8f..1a077e786 100644 --- a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc +++ b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.cc @@ -43,6 +43,146 @@ namespace lay // ------------------------------------------------------------------------------ +D25InteractionMode::D25InteractionMode (D25ViewWidget *view) + : mp_view (view) +{ + // .. nothing yet .. +} + +D25InteractionMode::~D25InteractionMode () +{ + // .. nothing yet .. +} + + +// ------------------------------------------------------------------------------ + +class D25PanInteractionMode + : public D25InteractionMode +{ +public: + D25PanInteractionMode (D25ViewWidget *widget, const QPoint &pos) + : D25InteractionMode (widget), m_start_pos (pos) + { + m_start_displacement = widget->displacement (); + + double px = (pos.x () - widget->width () / 2) * 2.0 / widget->width (); + double py = -(pos.y () - widget->height () / 2) * 2.0 / widget->height (); + + // compute vector of line of sight + std::pair ray = camera_normal (view ()->cam_perspective () * view ()->cam_trans (), px, py); + + // by definition the ray goes through the camera position + QVector3D hp = widget->hit_point_with_scene (ray.second); + + m_focus_dist = (widget->cam_position () - hp).length (); + } + + virtual ~D25PanInteractionMode () + { + // .. nothing yet .. + } + + virtual void mouse_move (QMouseEvent *event) + { + QPoint d = event->pos () - m_start_pos; + double f = tan ((view ()->cam_fov () / 2) / 180.0 * M_PI) * m_focus_dist * 2.0 / double (view ()->height ()); + double dx = d.x () * f; + double dy = -d.y () * f; + + QVector3D xv (cos (view ()->cam_azimuth () * M_PI / 180.0), 0.0, sin (view ()->cam_azimuth () * M_PI / 180.0)); + double re = sin (view ()->cam_elevation () * M_PI / 180.0); + QVector3D yv (-re * xv.z (), cos (view ()->cam_elevation () * M_PI / 180.0), re * xv.x ()); + QVector3D drag = xv * dx + yv * dy; + + view ()->set_displacement (m_start_displacement + drag / view ()->scale_factor ()); + } + +private: + QPoint m_start_pos; + double m_focus_dist; + QVector3D m_start_displacement; +}; + +// ------------------------------------------------------------------------------ + +class D25Rotate2DInteractionMode + : public D25InteractionMode +{ +public: + D25Rotate2DInteractionMode (D25ViewWidget *widget, const QPoint &pos) + : D25InteractionMode (widget), m_start_pos (pos) + { + m_start_cam_azimuth = widget->cam_azimuth (); + m_start_cam_elevation = widget->cam_elevation (); + } + + virtual ~D25Rotate2DInteractionMode () + { + // .. nothing yet .. + } + + virtual void mouse_move (QMouseEvent *event) + { + // fixed focus point for rotation + double focus_dist = 2.0; + + QPoint d = event->pos () - m_start_pos; + double f = tan ((view ()->cam_fov () / 2) / 180.0 * M_PI) * focus_dist * 2.0 / double (view ()->height ()); + double dx = d.x () * f; + double dy = -d.y () * f; + + double da = dx / (view ()->cam_dist () - focus_dist) * 180.0 / M_PI; + view ()->set_cam_azimuth (m_start_cam_azimuth + da); + + double de = dy / (view ()->cam_dist () - focus_dist) * 180.0 / M_PI; + view ()->set_cam_elevation (m_start_cam_elevation + de); + } + +private: + QPoint m_start_pos; + double m_start_cam_azimuth, m_start_cam_elevation; +}; + +// ------------------------------------------------------------------------------ + +class D25RotateAzimuthInteractionMode + : public D25InteractionMode +{ +public: + D25RotateAzimuthInteractionMode (D25ViewWidget *widget, const QPoint &pos) + : D25InteractionMode (widget), m_start_pos (pos) + { + // .. nothing yet .. + } + + virtual ~D25RotateAzimuthInteractionMode () + { + // .. nothing yet .. + } + + virtual void mouse_move (QMouseEvent *event) + { + // simple change of azimuth only - with center in the middle + + QPoint m = event->pos () - m_start_pos; + QVector3D p (m_start_pos.x () - view ()->width () / 2, -m_start_pos.y () + view ()->height () / 2, 0); + QVector3D d (m.x (), -m.y (), 0); + + double cp = QVector3D::crossProduct (p, p + d).z () / p.length () / (p + d).length (); + cp = std::max (-1.0, std::min (1.0, cp)); + double da = asin (cp) * 180.0 / M_PI; + + view ()->set_cam_azimuth (view ()->cam_azimuth () + da); + m_start_pos = event->pos (); + } + +private: + QPoint m_start_pos; +}; + +// ------------------------------------------------------------------------------ + D25ViewWidget::D25ViewWidget (QWidget *parent) : QOpenGLWidget (parent), m_shapes_program (0) @@ -74,13 +214,9 @@ void D25ViewWidget::reset () { m_scale_factor = 1.0; - m_focus_dist = 0.0; - m_fov = 90.0; - m_cam_azimuth = m_cam_elevation = 0.0; - m_top_view = false; - m_dragging = m_rotating = false; + mp_mode.reset (0); - refresh (); + camera_reset (); } void @@ -93,40 +229,55 @@ D25ViewWidget::wheelEvent (QWheelEvent *event) double px = (event->pos ().x () - width () / 2) * 2.0 / width (); double py = -(event->pos ().y () - height () / 2) * 2.0 / height (); - // compute vector of line of sight - std::pair ray = camera_normal (cam_perspective () * cam_trans (), px, py); + if (top_view ()) { - // by definition the ray goes through the camera position - QVector3D hp = hit_point_with_scene (ray.second); + // Plain zoom - if (event->modifiers () & Qt::ControlModifier) { - - // "Ctrl" is closeup - - double f = event->angleDelta ().y () * (1.0 / (90 * 8)); - m_displacement += -((f / m_scale_factor) * std::min (cam_dist (), double ((cam_position () - hp).length ()))) * ray.second; - - } else { - - // No shift is zoom + QVector3D hp (px, py, 0.0); double f = exp (event->angleDelta ().y () * (1.0 / (90 * 8))); - QVector3D initial_displacement = m_displacement; - QVector3D displacement = m_displacement; - m_scale_factor *= f; - displacement += hp * (1.0 - f) / m_scale_factor; + m_displacement += hp * (1.0 - f) / m_scale_factor; - // normalize the scene translation so the scene does not "flee" + } else { - QMatrix4x4 ct = cam_trans (); - initial_displacement = ct.map (initial_displacement); - displacement = ct.map (displacement); + // compute vector of line of sight + std::pair ray = camera_normal (cam_perspective () * cam_trans (), px, py); - lay::normalize_scene_trans (cam_perspective (), displacement, m_scale_factor, initial_displacement.z ()); + // by definition the ray goes through the camera position + QVector3D hp = hit_point_with_scene (ray.second); - m_displacement = ct.inverted ().map (displacement); + if (event->modifiers () & Qt::ControlModifier) { + + // "Ctrl" is closeup + + double f = event->angleDelta ().y () * (1.0 / (90 * 8)); + m_displacement += -((f / m_scale_factor) * std::min (cam_dist (), double ((cam_position () - hp).length ()))) * ray.second; + + } else { + + // No shift is zoom + + double f = exp (event->angleDelta ().y () * (1.0 / (90 * 8))); + + QVector3D initial_displacement = m_displacement; + QVector3D displacement = m_displacement; + + m_scale_factor *= f; + displacement += hp * (1.0 - f) / m_scale_factor; + + // normalize the scene translation so the scene does not "flee" + + QMatrix4x4 ct = cam_trans (); + initial_displacement = ct.map (initial_displacement); + displacement = ct.map (displacement); + + lay::normalize_scene_trans (cam_perspective (), displacement, m_scale_factor, initial_displacement.z ()); + + m_displacement = ct.inverted ().map (displacement); + + } } @@ -137,10 +288,8 @@ void D25ViewWidget::keyPressEvent (QKeyEvent *event) { if (event->key () == Qt::Key_Shift) { - m_top_view = true; - m_dragging = false; - m_rotating = false; - refresh (); + mp_mode.reset (0); + set_top_view (true); } } @@ -148,10 +297,8 @@ void D25ViewWidget::keyReleaseEvent (QKeyEvent *event) { if (event->key () == Qt::Key_Shift) { - m_top_view = false; - m_dragging = false; - m_rotating = false; - refresh (); + mp_mode.reset (0); + set_top_view (false); } } @@ -178,181 +325,87 @@ D25ViewWidget::hit_point_with_scene (const QVector3D &line_dir) void D25ViewWidget::mousePressEvent (QMouseEvent *event) { - m_dragging = m_rotating = false; + mp_mode.reset (0); + if (event->button () == Qt::MidButton) { - m_dragging = true; + mp_mode.reset (new D25PanInteractionMode (this, event->pos ())); } else if (event->button () == Qt::LeftButton) { - m_rotating = true; - } - - m_start_pos = event->pos (); - m_start_cam_position = cam_position (); - m_start_cam_azimuth = cam_azimuth (); - m_start_cam_elevation = cam_elevation (); - m_start_displacement = m_displacement; - - m_focus_dist = 2.0; - m_hit_point = QVector3D (); - - if (m_dragging) { - - // by definition the ray goes through the camera position - QVector3D hp = hit_point_with_scene (cam_direction ()); - - m_focus_dist = (cam_position () - hp).length (); - m_hit_point = cam_position () + cam_direction () * m_focus_dist; - - } else if (m_rotating) { - - double px = (event->pos ().x () - width () / 2) * 2.0 / width (); - double py = -(event->pos ().y () - height () / 2) * 2.0 / height (); - - // compute vector of line of sight - std::pair ray = camera_normal (cam_perspective () * cam_trans (), px, py); - - // by definition the ray goes through the camera position - QVector3D hp = hit_point_with_scene (ray.second); - - m_focus_dist = std::max (m_focus_dist, double ((cam_position () - hp).length ())); - m_hit_point = cam_position () + ray.second * m_focus_dist; - + if (! top_view ()) { + mp_mode.reset (new D25Rotate2DInteractionMode (this, event->pos ())); + } else { + mp_mode.reset (new D25RotateAzimuthInteractionMode (this, event->pos ())); + } } } void D25ViewWidget::mouseReleaseEvent (QMouseEvent * /*event*/) { - m_dragging = false; - m_rotating = false; + mp_mode.reset (0); } void D25ViewWidget::mouseMoveEvent (QMouseEvent *event) { - if (! m_dragging && ! m_rotating) { - return; + if (mp_mode.get ()) { + mp_mode->mouse_move (event); } +} - if (m_dragging) { +inline double square (double x) { return x * x; } - QPoint d = event->pos () - m_start_pos; - double f = tan ((cam_fov () / 2) / 180.0 * M_PI) * m_focus_dist * 2.0 / double (height ()); - double dx = d.x () * f; - double dy = -d.y () * f; +void +D25ViewWidget::fit () +{ + // we pick a scale factor to adjust the z dimension roughly to 1/4 of the screen height at a focus distance of 2 + double norm_height = 0.5; + double in_focus = 2.0; + m_scale_factor = (tan (cam_fov () * M_PI / 180.0 / 2.0) * 2.0) * in_focus * norm_height / std::max (0.001, (m_zmax - m_zmin)); - QVector3D xv (cos (m_start_cam_azimuth * M_PI / 180.0), 0.0, sin (m_start_cam_azimuth * M_PI / 180.0)); - double re = sin (m_start_cam_elevation * M_PI / 180.0); - QVector3D yv (-re * xv.z (), cos (m_start_cam_elevation * M_PI / 180.0), re * xv.x ()); - QVector3D drag = xv * dx + yv * dy; + QVector3D dim = QVector3D (m_bbox.width (), m_zmax - m_zmin, m_bbox.height ()) * m_scale_factor; + QVector3D bll = QVector3D (m_bbox.left (), m_zmin, -(m_bbox.bottom () + m_bbox.height ())); - m_displacement = m_start_displacement + drag / m_scale_factor; + // now we pick a displacement which moves the center to y = 0 and x, y in a way that the dimension covers the field of + // view and is centered. + // (we use the elliptic approximation which works exactly for azimuth angles which are a multiple of 90 degree) - } else { + double dh = sqrt (square (cos (cam_azimuth () * M_PI / 180.0) * dim.x ()) + square (sin (cam_azimuth () * M_PI / 180.0) * dim.z ())); + double dv = sqrt (square (cos (cam_azimuth () * M_PI / 180.0) * dim.z ()) + square (sin (cam_azimuth () * M_PI / 180.0) * dim.x ())); - if (! m_top_view) { + double d = std::max (dh, fabs (sin (cam_elevation ()) * dv)); - // fixed focus point for rotation - double focus_dist = 2.0; + QVector3D new_center (0.0, 0.0, -dv / 2.0 + std::max (0.0, -d / (2.0 * tan (cam_fov () * M_PI / 180.0 / 2.0)) + cam_dist ())); + QVector3D new_center_in_scene = cam_trans ().inverted ().map (new_center); - QPoint d = event->pos () - m_start_pos; - double f = tan ((cam_fov () / 2) / 180.0 * M_PI) * focus_dist * 2.0 / double (height ()); - double dx = d.x () * f; - double dy = -d.y () * f; + new_center_in_scene.setY (0.0); - double da = dx / (cam_dist () - focus_dist) * 180.0 / M_PI; - m_cam_azimuth = m_start_cam_azimuth + da; - - double de = dy / (cam_dist () - focus_dist) * 180.0 / M_PI; - m_cam_elevation = m_start_cam_elevation + de; - - } else { - - // simple change of azimuth only - with center in the middle - - QPoint m = event->pos () - m_start_pos; - QVector3D p (m_start_pos.x () - width () / 2, -m_start_pos.y () + height () / 2, 0); - QVector3D d (m.x (), -m.y (), 0); - - double cp = QVector3D::crossProduct (p, p + d).z () / p.length () / (p + d).length (); - cp = std::max (-1.0, std::min (1.0, cp)); - double da = asin (cp) * 180.0 / M_PI; - - m_cam_azimuth += da; - m_start_pos = event->pos (); - - } - - } + m_displacement = (new_center_in_scene - dim * 0.5) / m_scale_factor - bll; refresh (); } -double -D25ViewWidget::cam_fov () const -{ - return m_fov; // @@@ -} - -double -D25ViewWidget::cam_dist () const -{ - return 4.0; // @@@ -} - -QVector3D -D25ViewWidget::cam_direction () const -{ - QVector3D cd = cam_trans ().map (QVector3D (0, 0, 1)); - cd.setZ (-cd.z ()); - return cd; -} - -QVector3D -D25ViewWidget::cam_position () const -{ - return cam_direction () * -cam_dist (); -} - -double -D25ViewWidget::cam_azimuth () const -{ - return m_cam_azimuth; -} - -double -D25ViewWidget::cam_elevation () const -{ - return m_top_view ? -90.0 : m_cam_elevation; -} - -QMatrix4x4 -D25ViewWidget::cam_perspective () const -{ - QMatrix4x4 t; - t.perspective (cam_fov (), float (width ()) / float (height ()), 0.1f, 10000.0f); - t.translate (QVector3D (0.0, 0.0, -cam_dist ())); - return t; -} - -QMatrix4x4 -D25ViewWidget::cam_trans () const -{ - QMatrix4x4 t; - t.rotate (-cam_elevation (), 1.0, 0.0, 0.0); - t.rotate (cam_azimuth (), 0.0, 1.0, 0.0); - return t; -} - void D25ViewWidget::refresh () { QVector3D cp = cam_position (); -printf("@@@ e=%g a=%g x,y,z=%g,%g,%g d=%g,%g,%g s=%g f=%g\n", cam_elevation (), cam_azimuth (), cp.x(), cp.y(), cp.z(), m_displacement.x(), m_displacement.y(), m_displacement.z(), m_scale_factor, m_focus_dist); fflush(stdout); +printf("@@@ e=%g a=%g x,y,z=%g,%g,%g d=%g,%g,%g s=%g\n", cam_elevation (), cam_azimuth (), cp.x(), cp.y(), cp.z(), m_displacement.x(), m_displacement.y(), m_displacement.z(), m_scale_factor); fflush(stdout); update (); } +void +D25ViewWidget::camera_changed () +{ + refresh (); +} + +double +D25ViewWidget::aspect_ratio () const +{ + return double (width ()) / double (height ()); +} + void D25ViewWidget::attach_view (LayoutView *view) { @@ -439,61 +492,26 @@ D25ViewWidget::render_polygon (D25ViewWidget::chunks_type &chunks, const db::Pol // triangle bottom - chunks.add (pts[0].x () * dbu); - chunks.add (zstart); - chunks.add (pts[0].y () * dbu); - - chunks.add (pts[2].x () * dbu); - chunks.add (zstart); - chunks.add (pts[2].y () * dbu); - - chunks.add (pts[1].x () * dbu); - chunks.add (zstart); - chunks.add (pts[1].y () * dbu); + chunks.add (pts[0].x () * dbu, zstart, pts[0].y () * dbu); + chunks.add (pts[2].x () * dbu, zstart, pts[2].y () * dbu); + chunks.add (pts[1].x () * dbu, zstart, pts[1].y () * dbu); // triangle top - - chunks.add (pts[0].x () * dbu); - chunks.add (zstop); - chunks.add (pts[0].y () * dbu); - - chunks.add (pts[1].x () * dbu); - chunks.add (zstop); - chunks.add (pts[1].y () * dbu); - - chunks.add (pts[2].x () * dbu); - chunks.add (zstop); - chunks.add (pts[2].y () * dbu); + chunks.add (pts[0].x () * dbu, zstop, pts[0].y () * dbu); + chunks.add (pts[1].x () * dbu, zstop, pts[1].y () * dbu); + chunks.add (pts[2].x () * dbu, zstop, pts[2].y () * dbu); if (poly.hull ().size () == 4) { // triangle bottom - - chunks.add (pts[0].x () * dbu); - chunks.add (zstart); - chunks.add (pts[0].y () * dbu); - - chunks.add (pts[3].x () * dbu); - chunks.add (zstart); - chunks.add (pts[3].y () * dbu); - - chunks.add (pts[2].x () * dbu); - chunks.add (zstart); - chunks.add (pts[2].y () * dbu); + chunks.add (pts[0].x () * dbu, zstart, pts[0].y () * dbu); + chunks.add (pts[3].x () * dbu, zstart, pts[3].y () * dbu); + chunks.add (pts[2].x () * dbu, zstart, pts[2].y () * dbu); // triangle top - - chunks.add (pts[0].x () * dbu); - chunks.add (zstop); - chunks.add (pts[0].y () * dbu); - - chunks.add (pts[2].x () * dbu); - chunks.add (zstop); - chunks.add (pts[2].y () * dbu); - - chunks.add (pts[3].x () * dbu); - chunks.add (zstop); - chunks.add (pts[3].y () * dbu); + chunks.add (pts[0].x () * dbu, zstop, pts[0].y () * dbu); + chunks.add (pts[2].x () * dbu, zstop, pts[2].y () * dbu); + chunks.add (pts[3].x () * dbu, zstop, pts[3].y () * dbu); } @@ -503,29 +521,12 @@ D25ViewWidget::render_polygon (D25ViewWidget::chunks_type &chunks, const db::Pol void D25ViewWidget::render_wall (D25ViewWidget::chunks_type &chunks, const db::Edge &edge, double dbu, double zstart, double zstop) { - chunks.add (edge.p1 ().x () * dbu); - chunks.add (zstart); - chunks.add (edge.p1 ().y () * dbu); - - chunks.add (edge.p2 ().x () * dbu); - chunks.add (zstop); - chunks.add (edge.p2 ().y () * dbu); - - chunks.add (edge.p1 ().x () * dbu); - chunks.add (zstop); - chunks.add (edge.p1 ().y () * dbu); - - chunks.add (edge.p1 ().x () * dbu); - chunks.add (zstart); - chunks.add (edge.p1 ().y () * dbu); - - chunks.add (edge.p2 ().x () * dbu); - chunks.add (zstart); - chunks.add (edge.p2 ().y () * dbu); - - chunks.add (edge.p2 ().x () * dbu); - chunks.add (zstop); - chunks.add (edge.p2 ().y () * dbu); + chunks.add (edge.p1 ().x () * dbu, zstart, edge.p1 ().y () * dbu); + chunks.add (edge.p2 ().x () * dbu, zstop, edge.p2 ().y () * dbu); + chunks.add (edge.p1 ().x () * dbu, zstop, edge.p1 ().y () * dbu); + chunks.add (edge.p1 ().x () * dbu, zstart, edge.p1 ().y () * dbu); + chunks.add (edge.p2 ().x () * dbu, zstart, edge.p2 ().y () * dbu); + chunks.add (edge.p2 ().x () * dbu, zstop, edge.p2 ().y () * dbu); } void @@ -642,8 +643,7 @@ D25ViewWidget::initializeGL () "uniform vec4 ambient;\n" "uniform vec3 illum;\n" "out lowp vec4 vertexColor;\n" - "uniform mat4 geo_matrix;\n" - "uniform mat4 cam_matrix;\n" + "uniform mat4 matrix;\n" "layout (triangles) in;\n" "layout (triangle_strip, max_vertices = 3) out;\n" "\n" @@ -655,11 +655,11 @@ D25ViewWidget::initializeGL () " float dp = dot(normalize(n), illum);\n" " vertexColor = color * (dp * 0.5 + 0.5) - (min(0.0, dp) * 0.5 * ambient);\n" " vertexColor.a = 1.0;\n" - " gl_Position = cam_matrix * geo_matrix * p0;\n" + " gl_Position = matrix * p0;\n" " EmitVertex();\n" - " gl_Position = cam_matrix * geo_matrix * p1;\n" + " gl_Position = matrix * p1;\n" " EmitVertex();\n" - " gl_Position = cam_matrix * geo_matrix * p2;\n" + " gl_Position = matrix * p2;\n" " EmitVertex();\n" " EndPrimitive();\n" "}\n"; @@ -743,9 +743,6 @@ D25ViewWidget::initializeGL () void D25ViewWidget::paintGL () { - GLfloat vertices[6000]; - size_t nmax = sizeof (vertices) / sizeof (GLfloat); - const qreal retinaScale = devicePixelRatio (); glViewport (0, 0, width () * retinaScale, height () * retinaScale); @@ -767,8 +764,9 @@ D25ViewWidget::paintGL () m_shapes_program->bind (); - m_shapes_program->setUniformValue ("geo_matrix", cam_trans () * scene_trans); - m_shapes_program->setUniformValue ("cam_matrix", cam_perspective ()); + // draw the actual layout + + m_shapes_program->setUniformValue ("matrix", cam_perspective () * cam_trans () * scene_trans); // NOTE: z axis of illum points towards the scene because we include the z inversion in the scene transformation matrix m_shapes_program->setUniformValue ("illum", QVector3D (-3.0, -4.0, 2.0).normalized ()); @@ -779,20 +777,19 @@ D25ViewWidget::paintGL () glEnableVertexAttribArray (positions); for (std::list::const_iterator l = m_layers.begin (); l != m_layers.end (); ++l) { - m_shapes_program->setUniformValue ("color", l->color [0], l->color [1], l->color [2], l->color [3]); - - for (chunks_type::iterator c = l->vertex_chunk->begin (); c != l->vertex_chunk->end (); ++c) { - glVertexAttribPointer (positions, 3, GL_FLOAT, GL_FALSE, 0, c->front ()); - glDrawArrays (GL_TRIANGLES, 0, c->size () / 3); - } - + l->vertex_chunk->draw_to (this, positions, GL_TRIANGLES); } glDisableVertexAttribArray (positions); m_shapes_program->release (); + // decoration + + // a vertex buffer for the decoration + lay::mem_chunks vertexes; + m_gridplane_program->bind (); glEnable (GL_DEPTH_TEST); @@ -802,26 +799,14 @@ D25ViewWidget::paintGL () m_gridplane_program->setUniformValue ("matrix", cam_perspective () * cam_trans ()); - size_t index = 0; - double compass_rad = 0.3; double compass_bars = 0.4; - vertices[index++] = -compass_bars; - vertices[index++] = 0.0; - vertices[index++] = 0.0; + vertexes.add (-compass_bars, 0.0, 0.0); + vertexes.add (compass_bars, 0.0, 0.0); - vertices[index++] = compass_bars; - vertices[index++] = 0.0; - vertices[index++] = 0.0; - - vertices[index++] = 0.0; - vertices[index++] = 0.0; - vertices[index++] = -compass_bars; - - vertices[index++] = 0.0; - vertices[index++] = 0.0; - vertices[index++] = compass_bars; + vertexes.add (0.0, 0.0, -compass_bars); + vertexes.add (0.0, 0.0, compass_bars); int ncircle = 64; double x = compass_rad, z = 0.0; @@ -833,13 +818,8 @@ D25ViewWidget::paintGL () double xx = compass_rad * cos (a); double zz = compass_rad * sin (a); - vertices[index++] = x; - vertices[index++] = 0.0; - vertices[index++] = z; - - vertices[index++] = xx; - vertices[index++] = 0.0; - vertices[index++] = zz; + vertexes.add (x, 0.0, z); + vertexes.add (xx, 0.0, zz); x = xx; z = zz; @@ -848,29 +828,18 @@ D25ViewWidget::paintGL () m_gridplane_program->setUniformValue ("color", 1.0, 1.0, 1.0, 0.25f); - glVertexAttribPointer (positions, 3, GL_FLOAT, GL_FALSE, 0, vertices); - glLineWidth (2.0); - glDrawArrays (GL_LINES, 0, index / 3); - - index = 0; + vertexes.draw_to (this, positions, GL_LINES); // arrow - vertices[index++] = -0.25 * compass_rad; - vertices[index++] = 0.0; - vertices[index++] = 0.6 * compass_rad; - vertices[index++] = 0.0; - vertices[index++] = 0.0; - vertices[index++] = -0.8 * compass_rad; + vertexes.clear (); - vertices[index++] = 0.25 * compass_rad; - vertices[index++] = 0.0; - vertices[index++] = 0.6 * compass_rad; + vertexes.add (-0.25 * compass_rad, 0.0, 0.6 * compass_rad); + vertexes.add (0.0, 0.0, -0.8 * compass_rad); + vertexes.add (0.25 * compass_rad, 0.0, 0.6 * compass_rad); - glVertexAttribPointer (positions, 3, GL_FLOAT, GL_FALSE, 0, vertices); - - glDrawArrays (GL_TRIANGLES, 0, index / 3); + vertexes.draw_to (this, positions, GL_TRIANGLES); // draw base plane @@ -880,71 +849,61 @@ D25ViewWidget::paintGL () double gminor = gg.second, gmajor = gg.first; double margin = std::max (m_bbox.width (), m_bbox.height ()) * 0.02; - double l = m_bbox.left () - margin; - double r = m_bbox.right () + margin; - double b = m_bbox.bottom () - margin; - double t = m_bbox.top () + margin; - // @@@ + double l = m_bbox.left (); + double r = m_bbox.right (); + double b = m_bbox.bottom (); + double t = m_bbox.top (); // major and minor grid lines + vertexes.clear (); + const double epsilon = 1e-6; for (int major = 0; major < 2; ++major) { m_gridplane_program->setUniformValue ("color", 1.0, 1.0, 1.0, major ? 0.25f : 0.15f); - size_t index = 0; double x, y; double step = (major ? gmajor : gminor); x = ceil (l / step) * step; - for ( ; index < nmax && x < r - step * epsilon; x += step) { + for ( ; x < r - step * epsilon; x += step) { if ((fabs (floor (x / gmajor + 0.5) * gmajor - x) < epsilon) == (major != 0)) { - vertices [index++] = x; - vertices [index++] = 0.0; - vertices [index++] = b; - vertices [index++] = x; - vertices [index++] = 0.0; - vertices [index++] = t; + vertexes.add (x, 0.0, b - margin); + vertexes.add (x, 0.0, t + margin); } } y = ceil (b / step) * step; - for ( ; index < nmax && y < t - step * epsilon; y += step) { + for ( ; y < t - step * epsilon; y += step) { if ((fabs (floor (y / gmajor + 0.5) * gmajor - y) < epsilon) == (major != 0)) { - vertices [index++] = l; - vertices [index++] = 0.0; - vertices [index++] = y; - vertices [index++] = r; - vertices [index++] = 0.0; - vertices [index++] = y; + vertexes.add (l - margin, 0.0, y); + vertexes.add (r + margin, 0.0, y); } } - glVertexAttribPointer (positions, 3, GL_FLOAT, GL_FALSE, 0, vertices); - glLineWidth (2.0); - glDrawArrays (GL_LINES, 0, index / 3); + vertexes.draw_to (this, positions, GL_LINES); } - l = m_bbox.left (); - r = m_bbox.right (); - b = m_bbox.bottom (); - t = m_bbox.top (); + // the plane itself - GLfloat plane_vertices[] = { - float (l), 0.0f, float (b), float (l), 0.0f, float (t), float (r), 0.0f, float (t), - float (l), 0.0f, float (b), float (r), 0.0f, float (t), float (r), 0.0f, float (b) - }; + vertexes.clear (); + + vertexes.add (l, 0.0, b); + vertexes.add (l, 0.0, t); + vertexes.add (r, 0.0, t); + + vertexes.add (l, 0.0, b); + vertexes.add (r, 0.0, b); + vertexes.add (r, 0.0, t); m_gridplane_program->setUniformValue ("color", 1.0, 1.0, 1.0, 0.1f); - glVertexAttribPointer (positions, 3, GL_FLOAT, GL_FALSE, 0, plane_vertices); - - glDrawArrays (GL_TRIANGLES, 0, 6); + vertexes.draw_to (this, positions, GL_TRIANGLES); glDisableVertexAttribArray (positions); diff --git a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.h b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.h index e89404360..36f8d59b8 100644 --- a/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.h +++ b/src/plugins/tools/view_25d/lay_plugin/layD25ViewWidget.h @@ -23,6 +23,11 @@ #ifndef HDR_layD25ViewWidget #define HDR_layD25ViewWidget +#include "dbPolygon.h" + +#include "layD25MemChunks.h" +#include "layD25Camera.h" + #include #include #include @@ -32,9 +37,7 @@ #include #include -#include "dbPolygon.h" - -#include "layD25MemChunks.h" +#include namespace db { @@ -46,10 +49,25 @@ namespace lay { class LayoutView; +class D25ViewWidget; + +class D25InteractionMode +{ +public: + D25InteractionMode (D25ViewWidget *widget); + virtual ~D25InteractionMode (); + + D25ViewWidget *view () { return mp_view; } + virtual void mouse_move (QMouseEvent *event) = 0; + +private: + D25ViewWidget *mp_view; +}; class D25ViewWidget : public QOpenGLWidget, - private QOpenGLFunctions + private QOpenGLFunctions, + public D25Camera { Q_OBJECT @@ -66,22 +84,40 @@ public: void attach_view (lay::LayoutView *view); + QVector3D hit_point_with_scene(const QVector3D &line_dir); + void refresh (); + void reset (); + + QVector3D displacement () const { return m_displacement; } + + void set_displacement (const QVector3D &d) + { + m_displacement = d; + refresh (); + } + + double scale_factor () const { return m_scale_factor; } + + void set_scale_factor (double f) + { + m_scale_factor = f; + refresh (); + } + +protected: + virtual void camera_changed (); + virtual double aspect_ratio () const; + +public slots: + void fit (); + private: typedef lay::mem_chunks chunks_type; + std::auto_ptr mp_mode; QOpenGLShaderProgram *m_shapes_program, *m_gridplane_program; - bool m_dragging, m_rotating; double m_scale_factor; - double m_cam_azimuth, m_cam_elevation; - bool m_top_view; QVector3D m_displacement; - double m_focus_dist; - double m_fov; - QVector3D m_hit_point; - QPoint m_start_pos; - QVector3D m_start_cam_position; - double m_start_cam_azimuth, m_start_cam_elevation; - QVector3D m_start_displacement; lay::LayoutView *mp_view; db::DBox m_bbox; double m_zmin, m_zmax; @@ -99,21 +135,10 @@ private: void paintGL (); void resizeGL (int w, int h); - void refresh (); - void reset (); void prepare_view (); void render_layout (D25ViewWidget::chunks_type &chunks, const db::Layout &layout, const db::Cell &cell, unsigned int layer, double zstart, double zstop); void render_polygon (D25ViewWidget::chunks_type &chunks, const db::Polygon &poly, double dbu, double zstart, double zstop); void render_wall (D25ViewWidget::chunks_type &chunks, const db::Edge &poly, double dbu, double zstart, double zstop); - QVector3D hit_point_with_scene(const QVector3D &line_dir); - double cam_elevation () const; - double cam_azimuth () const; - QVector3D cam_position () const; - QVector3D cam_direction () const; - QMatrix4x4 cam_perspective () const; - QMatrix4x4 cam_trans () const; - double cam_dist () const; - double cam_fov () const; }; } diff --git a/src/plugins/tools/view_25d/lay_plugin/lay_plugin.pro b/src/plugins/tools/view_25d/lay_plugin/lay_plugin.pro index 6afb49ac2..2ef786e9b 100644 --- a/src/plugins/tools/view_25d/lay_plugin/lay_plugin.pro +++ b/src/plugins/tools/view_25d/lay_plugin/lay_plugin.pro @@ -12,14 +12,16 @@ HEADERS = \ layD25View.h \ layD25ViewWidget.h \ layD25MemChunks.h \ - layD25ViewUtils.h + layD25ViewUtils.h \ + layD25Camera.h SOURCES = \ layD25View.cc \ layD25ViewWidget.cc \ layD25Plugin.cc \ layD25MemChunks.cc \ - layD25ViewUtils.cc + layD25ViewUtils.cc \ + layD25Camera.cc FORMS = \ D25View.ui \ diff --git a/src/plugins/tools/view_25d/unit_tests/layD25CameraTests.cc b/src/plugins/tools/view_25d/unit_tests/layD25CameraTests.cc new file mode 100644 index 000000000..2934a9a9c --- /dev/null +++ b/src/plugins/tools/view_25d/unit_tests/layD25CameraTests.cc @@ -0,0 +1,102 @@ + +/* + + KLayout Layout Viewer + Copyright (C) 2006-2020 Matthias Koefferlein + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +*/ + + +#include "layD25Camera.h" +#include "tlUnitTest.h" + +static std::string v2s (const QVector4D &v) +{ + return tl::to_string (v.x ()) + "," + tl::to_string (v.y ()) + "," + tl::to_string (v.z ()); +} + +static std::string v2s (const QVector3D &v) +{ + return tl::to_string (v.x ()) + "," + tl::to_string (v.y ()) + "," + tl::to_string (v.z ()); +} + +static std::string v2s_2d (const QVector3D &v) +{ + return tl::to_string (v.x ()) + "," + tl::to_string (v.y ()); +} + +TEST(1_Transformations) +{ + lay::D25Camera cam; + + cam.set_cam_azimuth (45.0); + EXPECT_EQ (cam.cam_azimuth (), 45.0); + cam.set_cam_elevation (22.0); + EXPECT_EQ (cam.cam_elevation (), 22.0); + + cam.camera_reset (); + EXPECT_EQ (cam.cam_azimuth (), 0.0); + EXPECT_EQ (cam.cam_elevation (), 0.0); + + EXPECT_EQ (v2s (cam.cam_trans ().map (QVector4D (1, 0, 0, 1))), "1,0,0"); + EXPECT_EQ (v2s (cam.cam_trans ().map (QVector4D (0, 1, 0, 1))), "0,1,0"); + EXPECT_EQ (v2s (cam.cam_trans ().map (QVector4D (0, 0, 1, 1))), "0,0,1"); + EXPECT_EQ (v2s (cam.cam_direction ()), "0,0,-1"); + EXPECT_EQ (v2s (cam.cam_position ()), "0,0,4"); + + // looking up from the bottom, x axis stays the same (azimuth = 0) + cam.set_cam_elevation (90.0); + + EXPECT_EQ (v2s (cam.cam_trans ().map (QVector4D (1, 0, 0, 1))), "1,0,0"); + EXPECT_EQ (v2s (cam.cam_trans ().map (QVector4D (0, 1, 0, 1))), "0,0,-1"); + EXPECT_EQ (v2s (cam.cam_trans ().map (QVector4D (0, 0, 1, 1))), "0,1,0"); + + EXPECT_EQ (v2s (cam.cam_direction ()), "0,1,0"); + EXPECT_EQ (v2s (cam.cam_position ()), "0,-4,0"); + + // looking down from the top, x axis stays the same (azimuth = 0) + cam.set_cam_elevation (-90.0); + + EXPECT_EQ (v2s (cam.cam_trans ().map (QVector4D (1, 0, 0, 1))), "1,0,0"); + EXPECT_EQ (v2s (cam.cam_trans ().map (QVector4D (0, 1, 0, 1))), "0,0,1"); + EXPECT_EQ (v2s (cam.cam_trans ().map (QVector4D (0, 0, 1, 1))), "0,-1,0"); + + EXPECT_EQ (v2s (cam.cam_direction ()), "0,-1,0"); + EXPECT_EQ (v2s (cam.cam_position ()), "0,4,0"); + + // looking from the left, y axis stays the same (elevation = 0) + cam.set_cam_elevation (0.0); + cam.set_cam_azimuth (90.0); + + EXPECT_EQ (v2s (cam.cam_trans ().map (QVector4D (1, 0, 0, 1))), "0,0,-1"); + EXPECT_EQ (v2s (cam.cam_trans ().map (QVector4D (0, 1, 0, 1))), "0,1,0"); + EXPECT_EQ (v2s (cam.cam_trans ().map (QVector4D (0, 0, 1, 1))), "1,0,0"); + + EXPECT_EQ (v2s (cam.cam_direction ()), "1,0,0"); + EXPECT_EQ (v2s (cam.cam_position ()), "-4,0,0"); + + // looking from the right, y axis stays the same (elevation = 0) + cam.set_cam_elevation (0.0); + cam.set_cam_azimuth (-90.0); + + EXPECT_EQ (v2s (cam.cam_trans ().map (QVector4D (1, 0, 0, 1))), "0,0,1"); + EXPECT_EQ (v2s (cam.cam_trans ().map (QVector4D (0, 1, 0, 1))), "0,1,0"); + EXPECT_EQ (v2s (cam.cam_trans ().map (QVector4D (0, 0, 1, 1))), "-1,0,0"); + + EXPECT_EQ (v2s (cam.cam_direction ()), "-1,0,0"); + EXPECT_EQ (v2s (cam.cam_position ()), "4,0,0"); +} diff --git a/src/plugins/tools/view_25d/unit_tests/unit_tests.pro b/src/plugins/tools/view_25d/unit_tests/unit_tests.pro index 729ddc42e..c3e4e8e21 100644 --- a/src/plugins/tools/view_25d/unit_tests/unit_tests.pro +++ b/src/plugins/tools/view_25d/unit_tests/unit_tests.pro @@ -7,7 +7,8 @@ include($$PWD/../../../../lib_ut.pri) SOURCES = \ layD25MemChunksTests.cc \ - layD25ViewUtilsTests.cc + layD25ViewUtilsTests.cc \ + layD25CameraTests.cc INCLUDEPATH += $$LAY_INC $$TL_INC $$DB_INC $$GSI_INC $$PWD/../lay_plugin $$PWD/../../../common DEPENDPATH += $$LAY_INC $$TL_INC $$DB_INC $$GSI_INC $$PWD/../lay_plugin $$PWD/../../../common