Tue, 14 Feb 2017 07:57:27 +0200
Exported GLRenderer cameras into a new class, GLCamera.
--- a/CMakeLists.txt Mon Feb 13 20:59:16 2017 +0200 +++ b/CMakeLists.txt Tue Feb 14 07:57:27 2017 +0200 @@ -35,8 +35,9 @@ src/documentloader.cpp src/documentmanager.cpp src/editHistory.cpp + src/glcamera.cpp + src/glCompiler.cpp src/glRenderer.cpp - src/glCompiler.cpp src/grid.cpp src/guiutilities.cpp src/hierarchyelement.cpp @@ -94,6 +95,7 @@ src/doublemap.h src/editHistory.h src/format.h + src/glcamera.h src/glCompiler.h src/glRenderer.h src/grid.h
--- a/src/canvas.cpp Mon Feb 13 20:59:16 2017 +0200 +++ b/src/canvas.cpp Tue Feb 14 07:57:27 2017 +0200 @@ -19,6 +19,7 @@ #include <QMouseEvent> #include "canvas.h" #include "documentmanager.h" +#include "glcamera.h" #include "grid.h" #include "ldDocument.h" #include "mainwindow.h" @@ -148,7 +149,7 @@ void Canvas::mouseMoveEvent(QMouseEvent* event) { // Calculate 3d position of the cursor - m_position3D = convert2dTo3d(mousePosition(), true); + m_position3D = currentCamera().convert2dTo3d(mousePosition(), grid()); if (not m_currentEditMode->mouseMoved(event)) GLRenderer::mouseMoveEvent(event); @@ -190,7 +191,7 @@ void Canvas::drawBlipCoordinates(QPainter& painter, const Vertex& pos3d) const { - drawBlipCoordinates (painter, pos3d, convert3dTo2d (pos3d)); + drawBlipCoordinates(painter, pos3d, currentCamera().convert3dTo2d(pos3d)); } void Canvas::drawBlipCoordinates(QPainter& painter, const Vertex& pos3d, QPointF pos) const @@ -210,24 +211,22 @@ int Canvas::depthNegateFactor() const { - return cameraInfo(camera()).negatedDepth ? -1 : 1; + return currentCamera().isAxisNegated(Z) ? -1 : 1; } // ============================================================================= // void Canvas::getRelativeAxes(Axis& relativeX, Axis& relativeY) const { - const CameraInfo& camera = cameraInfo(this->camera()); - relativeX = camera.localX; - relativeY = camera.localY; + relativeX = currentCamera().axisX(); + relativeY = currentCamera().axisY(); } // ============================================================================= // Axis Canvas::getRelativeZ() const { - const CameraInfo& camera = cameraInfo(this->camera()); - return static_cast<Axis>(3 - camera.localX - camera.localY); + return currentCamera().axisZ(); } // ============================================================================= @@ -248,69 +247,6 @@ return 0.0; } -/* - * This converts a 2D point on the screen to a 3D point in the model. If 'snap' is true, the 3D point will snap to the current grid. - */ -Vertex Canvas::convert2dTo3d(const QPoint& position2d, bool snap) const -{ - if (camera() == Camera::Free) - { - return {0, 0, 0}; - } - else - { - Vertex position3d; - const CameraInfo& camera = cameraInfo(this->camera()); - Axis axisX = camera.localX; - Axis axisY = camera.localY; - int signX = camera.negatedX ? -1 : 1; - int signY = camera.negatedY ? -1 : 1; - - // Calculate cx and cy - these are the LDraw unit coords the cursor is at. - double cx = (-virtualWidth() + ((2 * position2d.x() * virtualWidth()) / width()) - panning(X)); - double cy = (virtualHeight() - ((2 * position2d.y() * virtualHeight()) / height()) - panning(Y)); - - if (snap) - { - cx = grid()->snap(cx, Grid::Coordinate); - cy = grid()->snap(cy, Grid::Coordinate); - } - - cx *= signX; - cy *= signY; - roundToDecimals(cx, 4); - roundToDecimals(cy, 4); - - // Create the vertex from the coordinates - position3d.setCoordinate(axisX, cx); - position3d.setCoordinate(axisY, cy); - position3d.setCoordinate(static_cast<Axis>(3 - axisX - axisY), getDepthValue()); - return position3d; - } -} - -/* - * Inverse operation for the above - convert a 3D position to a 2D screen position. - */ -QPoint Canvas::convert3dTo2d(const Vertex& position3d) const -{ - if (camera() == Camera::Free) - { - return {0, 0}; - } - else - { - const CameraInfo& camera = cameraInfo(this->camera()); - Axis axisX = camera.localX; - Axis axisY = camera.localY; - int signX = camera.negatedX ? -1 : 1; - int signY = camera.negatedY ? -1 : 1; - int rx = (((position3d[axisX] * signX) + virtualWidth() + panning(X)) * width()) / (2 * virtualWidth()); - int ry = (((position3d[axisY] * signY) - virtualHeight() + panning(Y)) * height()) / (2 * virtualHeight()); - return {rx, -ry}; - } -} - void Canvas::contextMenuEvent(QContextMenuEvent* event) { m_window->spawnContextMenu(event->globalPos());
--- a/src/canvas.h Mon Feb 13 20:59:16 2017 +0200 +++ b/src/canvas.h Tue Feb 14 07:57:27 2017 +0200 @@ -26,8 +26,6 @@ Canvas(LDDocument* document, QWidget* parent = nullptr); ~Canvas(); - Vertex convert2dTo3d(const QPoint& pos2d, bool snap) const; - QPoint convert3dTo2d(const Vertex& pos3d) const; EditModeType currentEditModeType() const; int depthNegateFactor() const; LDDocument* document() const;
--- a/src/editmodes/abstractEditMode.cpp Mon Feb 13 20:59:16 2017 +0200 +++ b/src/editmodes/abstractEditMode.cpp Tue Feb 14 07:57:27 2017 +0200 @@ -116,7 +116,7 @@ // Find the closest vertex to our cursor double minimumDistance = 1024.0; const Vertex* closest = nullptr; - Vertex cursorPosition = renderer()->convert2dTo3d(data.ev->pos(), false); + Vertex cursorPosition = renderer()->currentCamera().convert2dTo3d(data.ev->pos()); QPoint cursorPosition2D = data.ev->pos(); const Axis depthAxis = renderer()->getRelativeZ(); QList<Vertex> vertices = currentDocument()->inlineVertices().toList(); @@ -133,8 +133,8 @@ for (const Vertex& vertex : vertices) { // If the vertex in 2d space is very close to the cursor then we use it regardless of depth. - QPoint vect2d = renderer()->convert3dTo2d(vertex) - cursorPosition2D; - double distance2DSquared = std::pow (vect2d.x(), 2) + std::pow (vect2d.y(), 2); + QPoint vect2d = renderer()->currentCamera().convert3dTo2d(vertex) - cursorPosition2D; + double distance2DSquared = std::pow(vect2d.x(), 2) + std::pow(vect2d.y(), 2); if (distance2DSquared < 16.0 * 16.0) { @@ -232,7 +232,7 @@ // Convert to 2D for (int i = 0; i < countof(polygon3d); ++i) - polygon2d[i] = renderer()->convert3dTo2d(polygon3d[i]); + polygon2d[i] = renderer()->currentCamera().convert3dTo2d(polygon3d[i]); // Draw the polygon-to-be painter.setBrush(m_polybrush);
--- a/src/editmodes/circleMode.cpp Mon Feb 13 20:59:16 2017 +0200 +++ b/src/editmodes/circleMode.cpp Tue Feb 14 07:57:27 2017 +0200 @@ -48,7 +48,7 @@ if (countof(m_drawedVerts) >= position + 2) v1 = m_drawedVerts[position + 1]; else - v1 = renderer()->convert2dTo3d (renderer()->mousePosition(), false); + v1 = renderer()->currentCamera().convert2dTo3d(renderer()->mousePosition(), grid()); Axis localx, localy; renderer()->getRelativeAxes(localx, localy); @@ -193,7 +193,7 @@ if (not m_drawedVerts.isEmpty()) { int divisions = m_window->ringToolHiRes() ? HighResolution : LowResolution; - QPointF originSpot = renderer()->convert3dTo2d(m_drawedVerts.first()); + QPointF originSpot = renderer()->currentCamera().convert3dTo2d(m_drawedVerts.first()); // Line from the origin of the circle to current mouse position QLineF hand1 = {originSpot, renderer()->mousePositionF()}; // Line from the origin spot to @@ -218,7 +218,7 @@ // If we have not specified the center point of the circle yet, preview it on the screen. if (m_drawedVerts.isEmpty()) { - QPoint position2d = renderer()->convert3dTo2d(renderer()->position3D()); + QPoint position2d = renderer()->currentCamera().convert3dTo2d(renderer()->position3D()); renderer()->drawPoint(painter, position2d); renderer()->drawBlipCoordinates(painter, renderer()->position3D(), position2d); return; @@ -240,18 +240,18 @@ { const double sinangle (sin (angleoffset + i * angleUnit)); const double cosangle (cos (angleoffset + i * angleUnit)); - Vertex v (Origin); - v.setCoordinate (relX, m_drawedVerts[0][relX] + (cosangle * innerdistance)); - v.setCoordinate (relY, m_drawedVerts[0][relY] + (sinangle * innerdistance)); - innerverts << v; - innerverts2d << renderer()->convert3dTo2d (v); + Vertex vertex; + vertex.setCoordinate (relX, m_drawedVerts[0][relX] + (cosangle * innerdistance)); + vertex.setCoordinate (relY, m_drawedVerts[0][relY] + (sinangle * innerdistance)); + innerverts << vertex; + innerverts2d << renderer()->currentCamera().convert3dTo2d(vertex); if (outerdistance != -1) { - v.setCoordinate (relX, m_drawedVerts[0][relX] + (cosangle * outerdistance)); - v.setCoordinate (relY, m_drawedVerts[0][relY] + (sinangle * outerdistance)); - outerverts << v; - outerverts2d << renderer()->convert3dTo2d (v); + vertex.setCoordinate (relX, m_drawedVerts[0][relX] + (cosangle * outerdistance)); + vertex.setCoordinate (relY, m_drawedVerts[0][relY] + (sinangle * outerdistance)); + outerverts << vertex; + outerverts2d << renderer()->currentCamera().convert3dTo2d(vertex); } } @@ -297,7 +297,7 @@ painter.drawLines(lines); // Draw the current radius in the middle of the circle. - QPoint origin = renderer()->convert3dTo2d (m_drawedVerts[0]); + QPoint origin = renderer()->currentCamera().convert3dTo2d (m_drawedVerts[0]); QString label = QString::number (innerdistance); painter.setPen(renderer()->textPen()); painter.drawText(origin.x() - (metrics.width(label) / 2), origin.y(), label);
--- a/src/editmodes/curvemode.cpp Mon Feb 13 20:59:16 2017 +0200 +++ b/src/editmodes/curvemode.cpp Tue Feb 14 07:57:27 2017 +0200 @@ -45,7 +45,7 @@ curve[3] = curve[2]; for (int i = 0; i < countof(curve); ++i) - curve2d[i] = renderer()->convert3dTo2d (curve[i]); + curve2d[i] = renderer()->currentCamera().convert3dTo2d(curve[i]); painter.setPen (QColor (0, 112, 112)); if (countof(m_drawedVerts) >= 2) @@ -71,7 +71,7 @@ else { // Even if we have nothing, still draw the vertex at the cursor - QPoint vertex2d = renderer()->convert3dTo2d (getCursorVertex()); + QPoint vertex2d = renderer()->currentCamera().convert3dTo2d(getCursorVertex()); renderer()->drawPoint (painter, vertex2d); renderer()->drawBlipCoordinates (painter, getCursorVertex(), vertex2d); }
--- a/src/editmodes/linePathMode.cpp Mon Feb 13 20:59:16 2017 +0200 +++ b/src/editmodes/linePathMode.cpp Tue Feb 14 07:57:27 2017 +0200 @@ -31,7 +31,7 @@ points3d << renderer()->position3D(); for (Vertex const& vrt : points3d) - points << renderer()->convert3dTo2d (vrt); + points << renderer()->currentCamera().convert3dTo2d(vrt); painter.setPen (renderer()->textPen());
--- a/src/editmodes/rectangleMode.cpp Mon Feb 13 20:59:16 2017 +0200 +++ b/src/editmodes/rectangleMode.cpp Tue Feb 14 07:57:27 2017 +0200 @@ -74,9 +74,9 @@ Vertex v0 = m_drawedVerts[0], v1 = (countof(m_drawedVerts) >= 2) ? m_drawedVerts[1] : renderer()->position3D(); - const Axis localx = renderer()->getCameraAxis (false), - localy = renderer()->getCameraAxis (true), - localz = (Axis) (3 - localx - localy); + Axis localx, localy, localz; + renderer()->getRelativeAxes(localx, localy); + localz = renderer()->getRelativeZ(); for (int i = 0; i < 4; ++i) m_rectangleVerts[i].setCoordinate (localz, renderer()->getDepthValue());
--- a/src/glRenderer.cpp Mon Feb 13 20:59:16 2017 +0200 +++ b/src/glRenderer.cpp Tue Feb 14 07:57:27 2017 +0200 @@ -37,13 +37,13 @@ const CameraInfo g_cameraInfo[EnumLimits<Camera>::Count] = { - {{ 1, 0, 0 }, X, Z, false, false, false }, // top - {{ 0, 0, 0 }, X, Y, false, true, false }, // front - {{ 0, 1, 0 }, Z, Y, true, true, false }, // left - {{ -1, 0, 0 }, X, Z, false, true, true }, // bottom - {{ 0, 0, 0 }, X, Y, true, true, true }, // back - {{ 0, -1, 0 }, Z, Y, false, true, true }, // right - {{ 1, 0, 0 }, X, Z, false, false, false }, // free (defensive dummy data) + {{ 1, 0, 0 }, X, Z, false, false, false }, // top + {{ 0, 0, 0 }, X, Y, false, true, false }, // front + {{ 0, 1, 0 }, Z, Y, true, true, false }, // left + {{ -1, 0, 0 }, X, Z, false, true, true }, // bottom + {{ 0, 0, 0 }, X, Y, true, true, true }, // back + {{ 0, -1, 0 }, Z, Y, false, true, true }, // right + {{ 1, 0, 0 }, X, Z, false, false, false }, // free (defensive dummy data) }; const QPen GLRenderer::thinBorderPen {QColor {0, 0, 0, 208}, 1, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin}; @@ -53,7 +53,16 @@ GLRenderer::GLRenderer(const Model* model, QWidget* parent) : QGLWidget {parent}, HierarchyElement {parent}, - m_model {model} + m_model {model}, + m_cameras { + {1, 0, 0, X, Z, false, false, false}, // top + {0, 0, 0, X, Y, false, true, false}, // front + {0, 1, 0, Z, Y, true, true, false}, // left + {-1, 0, 0, X, Z, false, true, true}, // bottom + {0, 0, 0, X, Y, true, true, true}, // back + {0, -1, 0, Z, Y, false, true, true}, // right + {GLCamera::FreeCamera}, // free + } { m_camera = (Camera) m_config->camera(); m_compiler = new GLCompiler (this); @@ -99,26 +108,45 @@ void GLRenderer::calcCameraIcons() { int i = 0; + const int columns = 3; + const int firstAtLastRow = countof(m_cameras) - (countof(m_cameras) % columns); - for (CameraIcon& info : m_cameraIcons) + for (CameraIcon& cameraIcon : m_cameraIcons) { - // MATH - int x1 = (width() - (info.camera != Camera::Free ? 48 : 16)) + ((i % 3) * 16) - 1; - int y1 = ((i / 3) * 16) + 1; + int row = i / columns; + int column; - info.sourceRect = QRect (0, 0, 16, 16); - info.targetRect = QRect (x1, y1, 16, 16); - info.hitRect = QRect ( - info.targetRect.x(), - info.targetRect.y(), - info.targetRect.width() + 1, - info.targetRect.height() + 1 - ); + if (i < firstAtLastRow) + column = i % columns; + else + column = i + columns - (countof(m_cameras) % columns); + + int x1 = width() - 48 + (column * 16) - 1; + int y1 = (row * 16) + 1; + + cameraIcon.sourceRect = {0, 0, 16, 16}; + cameraIcon.targetRect = {x1, y1, 16, 16}; + cameraIcon.hitRect = { + cameraIcon.targetRect.x(), + cameraIcon.targetRect.y(), + cameraIcon.targetRect.width() + 1, + cameraIcon.targetRect.height() + 1 + }; ++i; } } +GLCamera& GLRenderer::currentCamera() +{ + return m_cameras[static_cast<int>(camera())]; +} + +const GLCamera& GLRenderer::currentCamera() const +{ + return m_cameras[static_cast<int>(camera())]; +} + // ============================================================================= // void GLRenderer::initGLData() @@ -187,7 +215,7 @@ glGetFloatv(GL_MODELVIEW_MATRIX, m_rotationMatrix.data()); glPopMatrix(); } - panning(X) = panning(Y) = 0.0f; + currentCamera().setPanning(0, 0); needZoomToFit(); } @@ -334,6 +362,10 @@ glLoadIdentity(); gluPerspective (45.0f, (double) width / (double) height, 1.0f, 10000.0f); glMatrixMode (GL_MODELVIEW); + + // Unfortunately Qt does not provide a resized() signal so we have to manually feed the information. + for (GLCamera& camera : m_cameras) + camera.rendererResized(width, height); } // ============================================================================= @@ -346,9 +378,6 @@ zoomAllToFit(); } - m_virtualWidth = zoom(); - m_virtualHeight = (height() * m_virtualWidth) / width(); - if (m_config->drawWireframe() and not m_isDrawingSelectionScene) glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); @@ -366,15 +395,12 @@ glPushMatrix(); glLoadIdentity(); - glOrtho (-m_virtualWidth, m_virtualWidth, -m_virtualHeight, m_virtualHeight, -100.0f, 100.0f); + const QSizeF& virtualSize = currentCamera().virtualSize(); + glOrtho(-virtualSize.width(), virtualSize.width(), -virtualSize.height(), virtualSize.height(), -100.0f, 100.0f); glTranslatef(panning (X), panning (Y), 0.0f); if (camera() != Camera::Front and camera() != Camera::Back) - { - glRotatef(90.0f, g_cameraInfo[static_cast<int>(camera())].glrotate[0], - g_cameraInfo[static_cast<int>(camera())].glrotate[1], - g_cameraInfo[static_cast<int>(camera())].glrotate[2]); - } + glRotatef(90.0f, currentCamera().glRotate(X), currentCamera().glRotate(Y), currentCamera().glRotate(Z)); // Back camera needs to be handled differently if (camera() == Camera::Back) @@ -604,8 +630,7 @@ if (mid or (left and shift)) { - panning(X) += 0.03f * xMove * (zoom() / 7.5f); - panning(Y) -= 0.03f * yMove * (zoom() / 7.5f); + currentCamera().pan(xMove, yMove); m_panning = true; m_isCameraMoving = true; } @@ -657,8 +682,7 @@ void GLRenderer::wheelEvent(QWheelEvent* ev) { makeCurrent(); - zoomNotch(ev->delta() > 0); - zoom() = qBound(0.01, zoom(), 10000.0); + currentCamera().zoomNotch(ev->delta() > 0); m_isCameraMoving = true; update(); ev->accept(); @@ -827,17 +851,6 @@ // ============================================================================= // -Axis GLRenderer::getCameraAxis (bool y, Camera camid) -{ - if (camid == (Camera) -1) - camid = camera(); - - const CameraInfo& cameraData = cameraInfo(camid); - return (y) ? cameraData.localY : cameraData.localX; -} - -// ============================================================================= -// QString GLRenderer::cameraName (Camera camera) const { switch (camera) @@ -862,16 +875,9 @@ // ============================================================================= // -void GLRenderer::zoomNotch (bool inward) -{ - zoom() *= inward ? 0.833f : 1.2f; -} - -// ============================================================================= -// void GLRenderer::zoomToFit() { - zoom() = 30.0f; + currentCamera().setZoom(30.0f); bool lastfilled = false; bool firstrun = true; enum { black = 0xFF000000 }; @@ -887,11 +893,11 @@ if (zoom() > 10000.0 or zoom() < 0.0) { // Nothing to draw if we get here. - zoom() = 30.0; + currentCamera().setZoom(30.0); break; } - zoomNotch (inward); + currentCamera().zoomNotch(inward); QVector<unsigned char> capture (4 * width() * height()); drawGLScene(); glReadPixels (0, 0, width(), height(), GL_RGBA, GL_UNSIGNED_BYTE, capture.data()); @@ -935,7 +941,7 @@ // last run had ideal zoom - zoom a bit back and we should reach it. if (filled and not lastfilled) { - zoomNotch (false); + currentCamera().zoomNotch(false); break; } @@ -1036,19 +1042,14 @@ return m_camera; } -double& GLRenderer::panning (Axis ax) -{ - return (ax == X) ? m_panX[static_cast<int>(camera())] : m_panY[static_cast<int>(camera())]; -} - double GLRenderer::panning (Axis ax) const { - return (ax == X) ? m_panX[static_cast<int>(camera())] : m_panY[static_cast<int>(camera())]; + return (ax == X) ? currentCamera().panningX() : currentCamera().panningY(); } -double& GLRenderer::zoom() +double GLRenderer::zoom() { - return m_zoom[static_cast<int>(camera())]; + return currentCamera().zoom(); } const QGenericMatrix<4, 4, GLfloat>& GLRenderer::rotationMatrix() const @@ -1066,16 +1067,6 @@ return m_lastButtons; } -double GLRenderer::virtualHeight() const -{ - return m_virtualHeight; -} - -double GLRenderer::virtualWidth() const -{ - return m_virtualWidth; -} - const Model* GLRenderer::model() const { return m_model;
--- a/src/glRenderer.h Mon Feb 13 20:59:16 2017 +0200 +++ b/src/glRenderer.h Tue Feb 14 07:57:27 2017 +0200 @@ -21,6 +21,7 @@ #include "main.h" #include "model.h" #include "glShared.h" +#include "glcamera.h" class GLCompiler; class MessageManager; @@ -56,8 +57,6 @@ _End }; -MAKE_ITERABLE_ENUM(Camera) - struct CameraIcon { QPixmap image; @@ -67,6 +66,8 @@ Camera camera; }; +MAKE_ITERABLE_ENUM(Camera) + // The main renderer object, draws the brick on the screen, manages the camera and selection picking. class GLRenderer : public QGLWidget, protected QOpenGLFunctions, public HierarchyElement { @@ -82,10 +83,11 @@ QString cameraName(Camera camera) const; QByteArray capturePixels(); GLCompiler* compiler() const; + GLCamera& currentCamera(); + const GLCamera& currentCamera() const; QString currentCameraName() const; void drawGLScene(); void forgetObject(LDObject* obj); - Axis getCameraAxis(bool y, Camera camid = (Camera) -1); void highlightCursorObject(); void initGLData(); bool isDrawOnly() const; @@ -107,9 +109,6 @@ void setDrawOnly(bool value); void setPicking(bool a); QPen textPen() const; - double virtualHeight() const; - double virtualWidth() const; - void zoomNotch(bool inward); static const QPen thinBorderPen; @@ -134,8 +133,7 @@ Qt::MouseButtons lastButtons() const; double panning (Axis ax) const; const QGenericMatrix<4, 4, GLfloat>& rotationMatrix() const; - double& panning (Axis ax); - double& zoom(); + double zoom(); template<typename... Args> QString format (QString fmtstr, Args... args) @@ -151,12 +149,8 @@ QTimer* m_toolTipTimer; Qt::MouseButtons m_lastButtons; Qt::KeyboardModifiers m_currentKeyboardModifiers; - double m_virtualWidth; - double m_virtualHeight; QGenericMatrix<4, 4, GLfloat> m_rotationMatrix; - double m_panX[7] = {0}; - double m_panY[7] = {0}; - double m_zoom[7] = {30}; + GLCamera m_cameras[7]; bool m_useDarkBackground = false; bool m_drawToolTip = false; bool m_takingScreenCapture = false;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glcamera.cpp Tue Feb 14 07:57:27 2017 +0200 @@ -0,0 +1,234 @@ +/* + * LDForge: LDraw parts authoring CAD + * Copyright (C) 2013 - 2017 Teemu Piippo + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#include "glcamera.h" +#include "grid.h" +#include "miscallenous.h" + +/* + * Constructs a fixed camera from parameters. + */ +GLCamera::GLCamera(int glRotateX, int glRotateY, int glRotateZ, Axis localX, Axis localY, bool negatedX, bool negatedY, bool negatedDepth) : + m_glrotate {glRotateX, glRotateY, glRotateZ}, + m_localX {localX}, + m_localY {localY}, + m_negatedX {negatedX}, + m_negatedY {negatedY}, + m_negatedDepth {negatedDepth} {} + +/* + * Constructs a free camera. + */ +GLCamera::GLCamera(FreeToken) : + m_isFree {true} {} + +/* + * Returns OpenGL rotation information for this camera. + */ +int GLCamera::glRotate(Axis axis) const +{ + return m_glrotate[axis]; +} + +/* + * Returns whether or not the given axis is negated on this camera. + */ +bool GLCamera::isAxisNegated(Axis axis) const +{ + switch (axis) + { + case X: + return m_negatedX; + + case Y: + return m_negatedY; + + case Z: + return m_negatedDepth; + + default: + return false; + } +} + +/* + * Returns the 3D axis that is on the X axis in this camera. + */ +Axis GLCamera::axisX() const +{ + return m_localX; +} + +/* + * Returns the 3D axis that is on the Y axis in this camera. + */ +Axis GLCamera::axisY() const +{ + return m_localY; +} + +/* + * Returns the 3D axis that is on the Z axis in this camera (inwards). + */ +Axis GLCamera::axisZ() const +{ + return static_cast<Axis>(3 - m_localX - m_localY); +} + +/* + * This converts a 2D point on the screen to a 3D point in the model. If 'snap' is true, the 3D point will snap to the current grid. + */ +Vertex GLCamera::convert2dTo3d(const QPoint& position2d, Grid* grid) const +{ + if (m_isFree) + { + return {0, 0, 0}; + } + else + { + Vertex position3d; + int signX = m_negatedX ? -1 : 1; + int signY = m_negatedY ? -1 : 1; + + // Calculate cx and cy - these are the LDraw unit coords the cursor is at. + double cx = -m_virtualSize.width() + (2 * position2d.x() * m_virtualSize.width() / m_size.width()) - m_panningX; + double cy = m_virtualSize.height() - (2 * position2d.y() * m_virtualSize.height() / m_size.height()) - m_panningY; + + // If a grid was passed, snap coordinates to it. + if (grid) + { + cx = grid->snap(cx, Grid::Coordinate); + cy = grid->snap(cy, Grid::Coordinate); + } + + roundToDecimals(cx, 4); + roundToDecimals(cy, 4); + + // Create the vertex from the coordinates + position3d.setCoordinate(axisX(), cx * signX); + position3d.setCoordinate(axisY(), cy * signY); + position3d.setCoordinate(axisZ(), m_depth); + return position3d; + } +} + +/* + * Inverse operation for the above - convert a 3D position to a 2D screen position. + */ +QPoint GLCamera::convert3dTo2d(const Vertex& position3d) const +{ + if (m_isFree) + { + return {0, 0}; + } + else + { + int signX = m_negatedX ? -1 : 1; + int signY = m_negatedY ? -1 : 1; + int rx = (position3d[axisX()] * signX + m_virtualSize.width() + m_panningX) * m_size.width() / 2 / m_virtualSize.width(); + int ry = (position3d[axisY()] * signY - m_virtualSize.height() + m_panningY) * m_size.height() / 2 / m_virtualSize.height(); + return {rx, -ry}; + } +} + +/* + * Resizes the camera when the renderer is resized. + */ +void GLCamera::rendererResized(int width, int height) +{ + m_size = {width, height}; + m_virtualSize = {m_zoom, height * m_zoom / width}; +} + +/* + * Returns the "virtual size" of the camera. Used to zoom in while keeping proportions. + */ +const QSizeF& GLCamera::virtualSize() const +{ + return m_virtualSize; +} + +/* + * Returns the "z depth" of the camera. Since the camera provides 2D editing, this value fills in the value for the + * third dimension for 3D vertices. + */ +double GLCamera::depth() const +{ + return m_depth; +} + +/* + * Returns the X-panning of this camera. + */ +double GLCamera::panningX() const +{ + return m_panningX; +} + +/* + * Returns the Y-panning of this camera. + */ +double GLCamera::panningY() const +{ + return m_panningY; +} + +/* + * Returns the zoom level of this camera. + */ +double GLCamera::zoom() const +{ + return m_zoom; +} + +/* + * Explicitly sets the panning of this camera. + */ +void GLCamera::setPanning(double x, double y) +{ + m_panningX = x; + m_panningY = y; +} + +/* + * Makes the camera pan by the provided mouse move input. + */ +void GLCamera::pan(int xMove, int yMove) +{ + m_panningX += 0.03f * xMove * zoom() / 7.5f; + m_panningY -= 0.03f * yMove * zoom() / 7.5f; +} + +/* + * Zooms the camera in one notch (e.g. by mousewheel). + */ +void GLCamera::zoomNotch (bool inward) +{ + m_zoom *= inward ? 0.833f : 1.2f; + m_zoom = qBound(0.01, zoom(), 10000.0); + rendererResized(m_size.width(), m_size.height()); +} + +/* + * Explicitly sets the zoom of this camera. + */ +void GLCamera::setZoom(double zoom) +{ + m_zoom = zoom; + rendererResized(m_size.width(), m_size.height()); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/glcamera.h Tue Feb 14 07:57:27 2017 +0200 @@ -0,0 +1,69 @@ +/* + * LDForge: LDraw parts authoring CAD + * Copyright (C) 2013 - 2017 Teemu Piippo + * + * 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 3 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, see <http://www.gnu.org/licenses/>. + */ + +#pragma once +#include "main.h" + +/* + * Models a 2D x/y co-ordinate system that maps to a fixed camera position. + * Owns camera orientation information and provides 2Dāā3D translation. + */ +class GLCamera : public QObject +{ + Q_OBJECT + +public: + // This is used to construct the free camera + enum FreeToken { FreeCamera }; + + GLCamera(int glRotateX, int glRotateY, int glRotateZ, Axis localX, Axis localY, bool negatedX, bool negatedY, bool negatedDepth); + GLCamera(FreeToken); + + Axis axisX() const; + Axis axisY() const; + Axis axisZ() const; + double depth() const; + int glRotate(Axis axis) const; + bool isAxisNegated(Axis axis) const; + Q_SLOT void rendererResized(int width, int height); + const QSizeF& virtualSize() const; + Vertex convert2dTo3d(const QPoint& pos2d, Grid* grid = nullptr) const; + QPoint convert3dTo2d(const Vertex& pos3d) const; + double panningX() const; + double panningY() const; + double zoom() const; + void setPanning(double x, double y); + void pan(int xMove, int yMove); + void zoomNotch(bool inward); + void setZoom(double zoom); + +private: + double m_panningX = 0; + double m_panningY = 0; + double m_depth = 0; + double m_zoom = 30; + QSize m_size; + QSizeF m_virtualSize; + int m_glrotate[3] = {0, 0, 0}; // GL model transformation to use + Axis m_localX = X; // Which axis to use for Y + Axis m_localY = Y; // Which axis to use for Y + bool m_isFree = false; // Is this the free camera? + bool m_negatedX = false; // Is +x to the left? + bool m_negatedY = false; // Is +y downwards? + bool m_negatedDepth = false; // is greater depth value closer to camera? +};