WIP: bugfixes, refactoring.

This commit is contained in:
Matthias Koefferlein 2020-04-16 23:14:22 +02:00
parent 2ec712b104
commit f9aa89a0b3
14 changed files with 1050 additions and 378 deletions

View File

@ -127,6 +127,12 @@
<file alias="folder_12.png">images/folder_12.png</file> <file alias="folder_12.png">images/folder_12.png</file>
<file alias="file_12.png">images/file_12.png</file> <file alias="file_12.png">images/file_12.png</file>
<file alias="empty_12.png">images/empty_12.png</file> <file alias="empty_12.png">images/empty_12.png</file>
<file alias="fit_front.png">images/fit_front.png</file>
<file alias="fit_back.png">images/fit_back.png</file>
<file alias="fit_left.png">images/fit_left.png</file>
<file alias="fit_right.png">images/fit_right.png</file>
<file alias="fit_top.png">images/fit_top.png</file>
<file alias="fit_bottom.png">images/fit_bottom.png</file>
</qresource> </qresource>
<qresource prefix="/syntax"> <qresource prefix="/syntax">
<file alias="ruby.xml">syntax/ruby.xml</file> <file alias="ruby.xml">syntax/ruby.xml</file>

View File

@ -6,8 +6,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>576</width> <width>854</width>
<height>649</height> <height>665</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
@ -29,17 +29,249 @@
<property name="bottomMargin"> <property name="bottomMargin">
<number>9</number> <number>9</number>
</property> </property>
<item>
<widget class="QFrame" name="frame">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="spacing">
<number>6</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QSlider" name="zoom_slider">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="zoom_factor">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>60</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>%</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>5</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QToolButton" name="fit_left">
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../../../../lay/lay/layResources.qrc">
<normaloff>:/fit_left.png</normaloff>:/fit_left.png</iconset>
</property>
<property name="iconSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="fit_front">
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../../../../lay/lay/layResources.qrc">
<normaloff>:/fit_front.png</normaloff>:/fit_front.png</iconset>
</property>
<property name="iconSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="fit_right">
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../../../../lay/lay/layResources.qrc">
<normaloff>:/fit_right.png</normaloff>:/fit_right.png</iconset>
</property>
<property name="iconSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="fit_back">
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../../../../lay/lay/layResources.qrc">
<normaloff>:/fit_back.png</normaloff>:/fit_back.png</iconset>
</property>
<property name="iconSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="fit_top">
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../../../../lay/lay/layResources.qrc">
<normaloff>:/fit_top.png</normaloff>:/fit_top.png</iconset>
</property>
<property name="iconSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="fit_bottom">
<property name="text">
<string>...</string>
</property>
<property name="icon">
<iconset resource="../../../../lay/lay/layResources.qrc">
<normaloff>:/fit_bottom.png</normaloff>:/fit_bottom.png</iconset>
</property>
<property name="iconSize">
<size>
<width>24</width>
<height>24</height>
</size>
</property>
<property name="autoRaise">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item> <item>
<widget class="lay::D25ViewWidget" name="d25_view"/> <widget class="lay::D25ViewWidget" name="d25_view"/>
</item> </item>
<item> <item>
<widget class="QDialogButtonBox" name="buttonBox"> <widget class="QFrame" name="frame_2">
<property name="orientation"> <property name="sizePolicy">
<enum>Qt::Horizontal</enum> <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property> </property>
<property name="standardButtons"> <property name="frameShape">
<set>QDialogButtonBox::Close</set> <enum>QFrame::NoFrame</enum>
</property> </property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Press and hold SHIFT for top view</string>
</property>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Close</set>
</property>
</widget>
</item>
</layout>
</widget> </widget>
</item> </item>
</layout> </layout>
@ -51,10 +283,9 @@
<header>layD25ViewWidget.h</header> <header>layD25ViewWidget.h</header>
</customwidget> </customwidget>
</customwidgets> </customwidgets>
<tabstops> <resources>
<tabstop>buttonBox</tabstop> <include location="../../../../lay/lay/layResources.qrc"/>
</tabstops> </resources>
<resources/>
<connections> <connections>
<connection> <connection>
<sender>buttonBox</sender> <sender>buttonBox</sender>

View File

@ -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 <QWidget>
#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;
}
}

View File

@ -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 <QMatrix4x4>
#include <QVector3D>
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

View File

@ -21,3 +21,4 @@
*/ */
#include "layD25MemChunks.h" #include "layD25MemChunks.h"

View File

@ -24,6 +24,7 @@
#define HDR_layD25MemChunks #define HDR_layD25MemChunks
#include <QDialog> #include <QDialog>
#include <QOpenGLFunctions>
#include "tlObject.h" #include "tlObject.h"
#include <string.h> // for memcpy #include <string.h> // for memcpy
@ -31,6 +32,19 @@
namespace lay namespace lay
{ {
template <class Obj>
struct gl_type2enum
{
GLenum operator() () const;
};
template <>
struct gl_type2enum<float>
{
GLenum operator() () const { return GL_FLOAT; }
};
/** /**
* @brief Provides a semi-contiguous array of objects * @brief Provides a semi-contiguous array of objects
* *
@ -201,6 +215,25 @@ public:
mp_last_chunk->m_objects [mp_last_chunk->m_len++] = element; 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 * @brief begin iterator
*/ */
@ -211,6 +244,17 @@ public:
*/ */
iterator end () const { return iterator (); } 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<Obj> () (), GL_FALSE, 0, c->front ());
ctx->glDrawArrays (mode, 0, c->size () / 3);
}
}
private: private:
chunk *mp_chunks; chunk *mp_chunks;
chunk *mp_last_chunk; chunk *mp_last_chunk;

View File

@ -31,6 +31,8 @@
namespace lay namespace lay
{ {
const double initial_elevation = 3.0;
D25View::D25View (QWidget *parent) D25View::D25View (QWidget *parent)
: QDialog (parent) : QDialog (parent)
{ {
@ -41,6 +43,13 @@ D25View::D25View (QWidget *parent)
mp_ui->d25_view->setFocusPolicy (Qt::StrongFocus); mp_ui->d25_view->setFocusPolicy (Qt::StrongFocus);
mp_ui->d25_view->setFocus (); 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 () D25View::~D25View ()
@ -55,6 +64,10 @@ D25View::exec_dialog (lay::LayoutView *view)
mp_view.reset (view); mp_view.reset (view);
mp_ui->d25_view->attach_view (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 (); int ret = QDialog::exec ();
mp_ui->d25_view->attach_view (0); mp_ui->d25_view->attach_view (0);
@ -62,6 +75,36 @@ D25View::exec_dialog (lay::LayoutView *view)
return ret; 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 void
D25View::accept () D25View::accept ()
{ {

View File

@ -54,6 +54,9 @@ public:
protected: protected:
void accept (); void accept ();
private slots:
void fit_button_clicked ();
private: private:
Ui::D25View *mp_ui; Ui::D25View *mp_ui;
tl::weak_ptr<lay::LayoutView> mp_view; tl::weak_ptr<lay::LayoutView> mp_view;

View File

@ -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) 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 (); return fabs (QVector3D::dotProduct (a, b)) < 0.5 * a.length () * b.length ();
} }

View File

@ -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<QVector3D, QVector3D> 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) D25ViewWidget::D25ViewWidget (QWidget *parent)
: QOpenGLWidget (parent), : QOpenGLWidget (parent),
m_shapes_program (0) m_shapes_program (0)
@ -74,13 +214,9 @@ void
D25ViewWidget::reset () D25ViewWidget::reset ()
{ {
m_scale_factor = 1.0; m_scale_factor = 1.0;
m_focus_dist = 0.0; mp_mode.reset (0);
m_fov = 90.0;
m_cam_azimuth = m_cam_elevation = 0.0;
m_top_view = false;
m_dragging = m_rotating = false;
refresh (); camera_reset ();
} }
void void
@ -93,40 +229,55 @@ D25ViewWidget::wheelEvent (QWheelEvent *event)
double px = (event->pos ().x () - width () / 2) * 2.0 / width (); double px = (event->pos ().x () - width () / 2) * 2.0 / width ();
double py = -(event->pos ().y () - height () / 2) * 2.0 / height (); double py = -(event->pos ().y () - height () / 2) * 2.0 / height ();
// compute vector of line of sight if (top_view ()) {
std::pair<QVector3D, QVector3D> ray = camera_normal (cam_perspective () * cam_trans (), px, py);
// by definition the ray goes through the camera position // Plain zoom
QVector3D hp = hit_point_with_scene (ray.second);
if (event->modifiers () & Qt::ControlModifier) { QVector3D hp (px, py, 0.0);
// "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))); double f = exp (event->angleDelta ().y () * (1.0 / (90 * 8)));
QVector3D initial_displacement = m_displacement;
QVector3D displacement = m_displacement;
m_scale_factor *= f; 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 (); // compute vector of line of sight
initial_displacement = ct.map (initial_displacement); std::pair<QVector3D, QVector3D> ray = camera_normal (cam_perspective () * cam_trans (), px, py);
displacement = ct.map (displacement);
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) D25ViewWidget::keyPressEvent (QKeyEvent *event)
{ {
if (event->key () == Qt::Key_Shift) { if (event->key () == Qt::Key_Shift) {
m_top_view = true; mp_mode.reset (0);
m_dragging = false; set_top_view (true);
m_rotating = false;
refresh ();
} }
} }
@ -148,10 +297,8 @@ void
D25ViewWidget::keyReleaseEvent (QKeyEvent *event) D25ViewWidget::keyReleaseEvent (QKeyEvent *event)
{ {
if (event->key () == Qt::Key_Shift) { if (event->key () == Qt::Key_Shift) {
m_top_view = false; mp_mode.reset (0);
m_dragging = false; set_top_view (false);
m_rotating = false;
refresh ();
} }
} }
@ -178,181 +325,87 @@ D25ViewWidget::hit_point_with_scene (const QVector3D &line_dir)
void void
D25ViewWidget::mousePressEvent (QMouseEvent *event) D25ViewWidget::mousePressEvent (QMouseEvent *event)
{ {
m_dragging = m_rotating = false; mp_mode.reset (0);
if (event->button () == Qt::MidButton) { if (event->button () == Qt::MidButton) {
m_dragging = true; mp_mode.reset (new D25PanInteractionMode (this, event->pos ()));
} else if (event->button () == Qt::LeftButton) { } else if (event->button () == Qt::LeftButton) {
m_rotating = true; if (! top_view ()) {
} mp_mode.reset (new D25Rotate2DInteractionMode (this, event->pos ()));
} else {
m_start_pos = event->pos (); mp_mode.reset (new D25RotateAzimuthInteractionMode (this, 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<QVector3D, QVector3D> 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;
} }
} }
void void
D25ViewWidget::mouseReleaseEvent (QMouseEvent * /*event*/) D25ViewWidget::mouseReleaseEvent (QMouseEvent * /*event*/)
{ {
m_dragging = false; mp_mode.reset (0);
m_rotating = false;
} }
void void
D25ViewWidget::mouseMoveEvent (QMouseEvent *event) D25ViewWidget::mouseMoveEvent (QMouseEvent *event)
{ {
if (! m_dragging && ! m_rotating) { if (mp_mode.get ()) {
return; mp_mode->mouse_move (event);
} }
}
if (m_dragging) { inline double square (double x) { return x * x; }
QPoint d = event->pos () - m_start_pos; void
double f = tan ((cam_fov () / 2) / 180.0 * M_PI) * m_focus_dist * 2.0 / double (height ()); D25ViewWidget::fit ()
double dx = d.x () * f; {
double dy = -d.y () * f; // 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)); QVector3D dim = QVector3D (m_bbox.width (), m_zmax - m_zmin, m_bbox.height ()) * m_scale_factor;
double re = sin (m_start_cam_elevation * M_PI / 180.0); QVector3D bll = QVector3D (m_bbox.left (), m_zmin, -(m_bbox.bottom () + m_bbox.height ()));
QVector3D yv (-re * xv.z (), cos (m_start_cam_elevation * M_PI / 180.0), re * xv.x ());
QVector3D drag = xv * dx + yv * dy;
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 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 ()));
double focus_dist = 2.0; QVector3D new_center_in_scene = cam_trans ().inverted ().map (new_center);
QPoint d = event->pos () - m_start_pos; new_center_in_scene.setY (0.0);
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;
double da = dx / (cam_dist () - focus_dist) * 180.0 / M_PI; m_displacement = (new_center_in_scene - dim * 0.5) / m_scale_factor - bll;
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 ();
}
}
refresh (); 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 void
D25ViewWidget::refresh () D25ViewWidget::refresh ()
{ {
QVector3D cp = cam_position (); 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 (); update ();
} }
void
D25ViewWidget::camera_changed ()
{
refresh ();
}
double
D25ViewWidget::aspect_ratio () const
{
return double (width ()) / double (height ());
}
void void
D25ViewWidget::attach_view (LayoutView *view) D25ViewWidget::attach_view (LayoutView *view)
{ {
@ -439,61 +492,26 @@ D25ViewWidget::render_polygon (D25ViewWidget::chunks_type &chunks, const db::Pol
// triangle bottom // triangle bottom
chunks.add (pts[0].x () * dbu); chunks.add (pts[0].x () * dbu, zstart, pts[0].y () * dbu);
chunks.add (zstart); chunks.add (pts[2].x () * dbu, zstart, pts[2].y () * dbu);
chunks.add (pts[0].y () * dbu); chunks.add (pts[1].x () * dbu, zstart, pts[1].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);
// triangle top // triangle top
chunks.add (pts[0].x () * dbu, zstop, pts[0].y () * dbu);
chunks.add (pts[0].x () * dbu); chunks.add (pts[1].x () * dbu, zstop, pts[1].y () * dbu);
chunks.add (zstop); chunks.add (pts[2].x () * dbu, zstop, pts[2].y () * dbu);
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);
if (poly.hull ().size () == 4) { if (poly.hull ().size () == 4) {
// triangle bottom // triangle bottom
chunks.add (pts[0].x () * dbu, zstart, pts[0].y () * dbu);
chunks.add (pts[0].x () * dbu); chunks.add (pts[3].x () * dbu, zstart, pts[3].y () * dbu);
chunks.add (zstart); chunks.add (pts[2].x () * dbu, zstart, pts[2].y () * dbu);
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);
// triangle top // triangle top
chunks.add (pts[0].x () * dbu, zstop, pts[0].y () * dbu);
chunks.add (pts[0].x () * dbu); chunks.add (pts[2].x () * dbu, zstop, pts[2].y () * dbu);
chunks.add (zstop); chunks.add (pts[3].x () * dbu, zstop, pts[3].y () * dbu);
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);
} }
@ -503,29 +521,12 @@ D25ViewWidget::render_polygon (D25ViewWidget::chunks_type &chunks, const db::Pol
void void
D25ViewWidget::render_wall (D25ViewWidget::chunks_type &chunks, const db::Edge &edge, double dbu, double zstart, double zstop) 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 (edge.p1 ().x () * dbu, zstart, edge.p1 ().y () * dbu);
chunks.add (zstart); chunks.add (edge.p2 ().x () * dbu, zstop, edge.p2 ().y () * dbu);
chunks.add (edge.p1 ().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); chunks.add (edge.p2 ().x () * dbu, zstart, edge.p2 ().y () * dbu);
chunks.add (zstop); chunks.add (edge.p2 ().x () * dbu, zstop, edge.p2 ().y () * dbu);
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);
} }
void void
@ -642,8 +643,7 @@ D25ViewWidget::initializeGL ()
"uniform vec4 ambient;\n" "uniform vec4 ambient;\n"
"uniform vec3 illum;\n" "uniform vec3 illum;\n"
"out lowp vec4 vertexColor;\n" "out lowp vec4 vertexColor;\n"
"uniform mat4 geo_matrix;\n" "uniform mat4 matrix;\n"
"uniform mat4 cam_matrix;\n"
"layout (triangles) in;\n" "layout (triangles) in;\n"
"layout (triangle_strip, max_vertices = 3) out;\n" "layout (triangle_strip, max_vertices = 3) out;\n"
"\n" "\n"
@ -655,11 +655,11 @@ D25ViewWidget::initializeGL ()
" float dp = dot(normalize(n), illum);\n" " float dp = dot(normalize(n), illum);\n"
" vertexColor = color * (dp * 0.5 + 0.5) - (min(0.0, dp) * 0.5 * ambient);\n" " vertexColor = color * (dp * 0.5 + 0.5) - (min(0.0, dp) * 0.5 * ambient);\n"
" vertexColor.a = 1.0;\n" " vertexColor.a = 1.0;\n"
" gl_Position = cam_matrix * geo_matrix * p0;\n" " gl_Position = matrix * p0;\n"
" EmitVertex();\n" " EmitVertex();\n"
" gl_Position = cam_matrix * geo_matrix * p1;\n" " gl_Position = matrix * p1;\n"
" EmitVertex();\n" " EmitVertex();\n"
" gl_Position = cam_matrix * geo_matrix * p2;\n" " gl_Position = matrix * p2;\n"
" EmitVertex();\n" " EmitVertex();\n"
" EndPrimitive();\n" " EndPrimitive();\n"
"}\n"; "}\n";
@ -743,9 +743,6 @@ D25ViewWidget::initializeGL ()
void void
D25ViewWidget::paintGL () D25ViewWidget::paintGL ()
{ {
GLfloat vertices[6000];
size_t nmax = sizeof (vertices) / sizeof (GLfloat);
const qreal retinaScale = devicePixelRatio (); const qreal retinaScale = devicePixelRatio ();
glViewport (0, 0, width () * retinaScale, height () * retinaScale); glViewport (0, 0, width () * retinaScale, height () * retinaScale);
@ -767,8 +764,9 @@ D25ViewWidget::paintGL ()
m_shapes_program->bind (); m_shapes_program->bind ();
m_shapes_program->setUniformValue ("geo_matrix", cam_trans () * scene_trans); // draw the actual layout
m_shapes_program->setUniformValue ("cam_matrix", cam_perspective ());
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 // 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 ()); m_shapes_program->setUniformValue ("illum", QVector3D (-3.0, -4.0, 2.0).normalized ());
@ -779,20 +777,19 @@ D25ViewWidget::paintGL ()
glEnableVertexAttribArray (positions); glEnableVertexAttribArray (positions);
for (std::list<LayerInfo>::const_iterator l = m_layers.begin (); l != m_layers.end (); ++l) { for (std::list<LayerInfo>::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]); m_shapes_program->setUniformValue ("color", l->color [0], l->color [1], l->color [2], l->color [3]);
l->vertex_chunk->draw_to (this, positions, GL_TRIANGLES);
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);
}
} }
glDisableVertexAttribArray (positions); glDisableVertexAttribArray (positions);
m_shapes_program->release (); m_shapes_program->release ();
// decoration
// a vertex buffer for the decoration
lay::mem_chunks<float, 1024 * 18> vertexes;
m_gridplane_program->bind (); m_gridplane_program->bind ();
glEnable (GL_DEPTH_TEST); glEnable (GL_DEPTH_TEST);
@ -802,26 +799,14 @@ D25ViewWidget::paintGL ()
m_gridplane_program->setUniformValue ("matrix", cam_perspective () * cam_trans ()); m_gridplane_program->setUniformValue ("matrix", cam_perspective () * cam_trans ());
size_t index = 0;
double compass_rad = 0.3; double compass_rad = 0.3;
double compass_bars = 0.4; double compass_bars = 0.4;
vertices[index++] = -compass_bars; vertexes.add (-compass_bars, 0.0, 0.0);
vertices[index++] = 0.0; vertexes.add (compass_bars, 0.0, 0.0);
vertices[index++] = 0.0;
vertices[index++] = compass_bars; vertexes.add (0.0, 0.0, -compass_bars);
vertices[index++] = 0.0; vertexes.add (0.0, 0.0, compass_bars);
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;
int ncircle = 64; int ncircle = 64;
double x = compass_rad, z = 0.0; double x = compass_rad, z = 0.0;
@ -833,13 +818,8 @@ D25ViewWidget::paintGL ()
double xx = compass_rad * cos (a); double xx = compass_rad * cos (a);
double zz = compass_rad * sin (a); double zz = compass_rad * sin (a);
vertices[index++] = x; vertexes.add (x, 0.0, z);
vertices[index++] = 0.0; vertexes.add (xx, 0.0, zz);
vertices[index++] = z;
vertices[index++] = xx;
vertices[index++] = 0.0;
vertices[index++] = zz;
x = xx; x = xx;
z = zz; z = zz;
@ -848,29 +828,18 @@ D25ViewWidget::paintGL ()
m_gridplane_program->setUniformValue ("color", 1.0, 1.0, 1.0, 0.25f); 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); glLineWidth (2.0);
glDrawArrays (GL_LINES, 0, index / 3); vertexes.draw_to (this, positions, GL_LINES);
index = 0;
// arrow // arrow
vertices[index++] = -0.25 * compass_rad;
vertices[index++] = 0.0;
vertices[index++] = 0.6 * compass_rad;
vertices[index++] = 0.0; vertexes.clear ();
vertices[index++] = 0.0;
vertices[index++] = -0.8 * compass_rad;
vertices[index++] = 0.25 * compass_rad; vertexes.add (-0.25 * compass_rad, 0.0, 0.6 * compass_rad);
vertices[index++] = 0.0; vertexes.add (0.0, 0.0, -0.8 * compass_rad);
vertices[index++] = 0.6 * compass_rad; vertexes.add (0.25 * compass_rad, 0.0, 0.6 * compass_rad);
glVertexAttribPointer (positions, 3, GL_FLOAT, GL_FALSE, 0, vertices); vertexes.draw_to (this, positions, GL_TRIANGLES);
glDrawArrays (GL_TRIANGLES, 0, index / 3);
// draw base plane // draw base plane
@ -880,71 +849,61 @@ D25ViewWidget::paintGL ()
double gminor = gg.second, gmajor = gg.first; double gminor = gg.second, gmajor = gg.first;
double margin = std::max (m_bbox.width (), m_bbox.height ()) * 0.02; 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 // major and minor grid lines
vertexes.clear ();
const double epsilon = 1e-6; const double epsilon = 1e-6;
for (int major = 0; major < 2; ++major) { for (int major = 0; major < 2; ++major) {
m_gridplane_program->setUniformValue ("color", 1.0, 1.0, 1.0, major ? 0.25f : 0.15f); m_gridplane_program->setUniformValue ("color", 1.0, 1.0, 1.0, major ? 0.25f : 0.15f);
size_t index = 0;
double x, y; double x, y;
double step = (major ? gmajor : gminor); double step = (major ? gmajor : gminor);
x = ceil (l / step) * step; 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)) { if ((fabs (floor (x / gmajor + 0.5) * gmajor - x) < epsilon) == (major != 0)) {
vertices [index++] = x; vertexes.add (x, 0.0, b - margin);
vertices [index++] = 0.0; vertexes.add (x, 0.0, t + margin);
vertices [index++] = b;
vertices [index++] = x;
vertices [index++] = 0.0;
vertices [index++] = t;
} }
} }
y = ceil (b / step) * step; 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)) { if ((fabs (floor (y / gmajor + 0.5) * gmajor - y) < epsilon) == (major != 0)) {
vertices [index++] = l; vertexes.add (l - margin, 0.0, y);
vertices [index++] = 0.0; vertexes.add (r + margin, 0.0, y);
vertices [index++] = y;
vertices [index++] = r;
vertices [index++] = 0.0;
vertices [index++] = y;
} }
} }
glVertexAttribPointer (positions, 3, GL_FLOAT, GL_FALSE, 0, vertices);
glLineWidth (2.0); glLineWidth (2.0);
glDrawArrays (GL_LINES, 0, index / 3); vertexes.draw_to (this, positions, GL_LINES);
} }
l = m_bbox.left (); // the plane itself
r = m_bbox.right ();
b = m_bbox.bottom ();
t = m_bbox.top ();
GLfloat plane_vertices[] = { vertexes.clear ();
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.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); m_gridplane_program->setUniformValue ("color", 1.0, 1.0, 1.0, 0.1f);
glVertexAttribPointer (positions, 3, GL_FLOAT, GL_FALSE, 0, plane_vertices); vertexes.draw_to (this, positions, GL_TRIANGLES);
glDrawArrays (GL_TRIANGLES, 0, 6);
glDisableVertexAttribArray (positions); glDisableVertexAttribArray (positions);

View File

@ -23,6 +23,11 @@
#ifndef HDR_layD25ViewWidget #ifndef HDR_layD25ViewWidget
#define HDR_layD25ViewWidget #define HDR_layD25ViewWidget
#include "dbPolygon.h"
#include "layD25MemChunks.h"
#include "layD25Camera.h"
#include <QOpenGLWidget> #include <QOpenGLWidget>
#include <QOpenGLVertexArrayObject> #include <QOpenGLVertexArrayObject>
#include <QOpenGLBuffer> #include <QOpenGLBuffer>
@ -32,9 +37,7 @@
#include <QPoint> #include <QPoint>
#include <QVector3D> #include <QVector3D>
#include "dbPolygon.h" #include <memory>
#include "layD25MemChunks.h"
namespace db namespace db
{ {
@ -46,10 +49,25 @@ namespace lay
{ {
class LayoutView; 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 class D25ViewWidget
: public QOpenGLWidget, : public QOpenGLWidget,
private QOpenGLFunctions private QOpenGLFunctions,
public D25Camera
{ {
Q_OBJECT Q_OBJECT
@ -66,22 +84,40 @@ public:
void attach_view (lay::LayoutView *view); 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: private:
typedef lay::mem_chunks<GLfloat, 1024 * 9> chunks_type; typedef lay::mem_chunks<GLfloat, 1024 * 9> chunks_type;
std::auto_ptr<D25InteractionMode> mp_mode;
QOpenGLShaderProgram *m_shapes_program, *m_gridplane_program; QOpenGLShaderProgram *m_shapes_program, *m_gridplane_program;
bool m_dragging, m_rotating;
double m_scale_factor; double m_scale_factor;
double m_cam_azimuth, m_cam_elevation;
bool m_top_view;
QVector3D m_displacement; 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; lay::LayoutView *mp_view;
db::DBox m_bbox; db::DBox m_bbox;
double m_zmin, m_zmax; double m_zmin, m_zmax;
@ -99,21 +135,10 @@ private:
void paintGL (); void paintGL ();
void resizeGL (int w, int h); void resizeGL (int w, int h);
void refresh ();
void reset ();
void prepare_view (); 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_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_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); 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;
}; };
} }

View File

@ -12,14 +12,16 @@ HEADERS = \
layD25View.h \ layD25View.h \
layD25ViewWidget.h \ layD25ViewWidget.h \
layD25MemChunks.h \ layD25MemChunks.h \
layD25ViewUtils.h layD25ViewUtils.h \
layD25Camera.h
SOURCES = \ SOURCES = \
layD25View.cc \ layD25View.cc \
layD25ViewWidget.cc \ layD25ViewWidget.cc \
layD25Plugin.cc \ layD25Plugin.cc \
layD25MemChunks.cc \ layD25MemChunks.cc \
layD25ViewUtils.cc layD25ViewUtils.cc \
layD25Camera.cc
FORMS = \ FORMS = \
D25View.ui \ D25View.ui \

View File

@ -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");
}

View File

@ -7,7 +7,8 @@ include($$PWD/../../../../lib_ut.pri)
SOURCES = \ SOURCES = \
layD25MemChunksTests.cc \ layD25MemChunksTests.cc \
layD25ViewUtilsTests.cc layD25ViewUtilsTests.cc \
layD25CameraTests.cc
INCLUDEPATH += $$LAY_INC $$TL_INC $$DB_INC $$GSI_INC $$PWD/../lay_plugin $$PWD/../../../common 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 DEPENDPATH += $$LAY_INC $$TL_INC $$DB_INC $$GSI_INC $$PWD/../lay_plugin $$PWD/../../../common