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="file_12.png">images/file_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 prefix="/syntax">
<file alias="ruby.xml">syntax/ruby.xml</file>

View File

@ -6,8 +6,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>576</width>
<height>649</height>
<width>854</width>
<height>665</height>
</rect>
</property>
<property name="windowTitle">
@ -29,17 +29,249 @@
<property name="bottomMargin">
<number>9</number>
</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>
<widget class="lay::D25ViewWidget" name="d25_view"/>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
<widget class="QFrame" name="frame_2">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Close</set>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</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>
</item>
</layout>
@ -51,10 +283,9 @@
<header>layD25ViewWidget.h</header>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>buttonBox</tabstop>
</tabstops>
<resources/>
<resources>
<include location="../../../../lay/lay/layResources.qrc"/>
</resources>
<connections>
<connection>
<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"

View File

@ -24,6 +24,7 @@
#define HDR_layD25MemChunks
#include <QDialog>
#include <QOpenGLFunctions>
#include "tlObject.h"
#include <string.h> // for memcpy
@ -31,6 +32,19 @@
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
*
@ -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<Obj> () (), GL_FALSE, 0, c->front ());
ctx->glDrawArrays (mode, 0, c->size () / 3);
}
}
private:
chunk *mp_chunks;
chunk *mp_last_chunk;

View File

@ -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 ()
{

View File

@ -54,6 +54,9 @@ public:
protected:
void accept ();
private slots:
void fit_button_clicked ();
private:
Ui::D25View *mp_ui;
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)
{
// 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 ();
}

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)
: 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<QVector3D, QVector3D> 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<QVector3D, QVector3D> 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<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;
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<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]);
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<float, 1024 * 18> 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);

View File

@ -23,6 +23,11 @@
#ifndef HDR_layD25ViewWidget
#define HDR_layD25ViewWidget
#include "dbPolygon.h"
#include "layD25MemChunks.h"
#include "layD25Camera.h"
#include <QOpenGLWidget>
#include <QOpenGLVertexArrayObject>
#include <QOpenGLBuffer>
@ -32,9 +37,7 @@
#include <QPoint>
#include <QVector3D>
#include "dbPolygon.h"
#include "layD25MemChunks.h"
#include <memory>
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<GLfloat, 1024 * 9> chunks_type;
std::auto_ptr<D25InteractionMode> 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;
};
}

View File

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

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 = \
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