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 @@
-
- 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