Fri, 04 Jul 2014 22:19:01 +0300
- saving work done on edit mode revamp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/editmodes/abstracteditmode.cc Fri Jul 04 22:19:01 2014 +0300 @@ -0,0 +1,123 @@ +#include <stdexcept> +#include "abstracteditmode.h" +#include "selectmode.h" +#include "drawmode.h" +#include "circlemode.h" +#include "magicwandmode.h" +#include "../mainWindow.h" + +AbstractEditMode::AbstractEditMode (GLRenderer* renderer) : + _renderer (renderer) {} + +AbstractEditMode* AbstractEditMode::createByType (GLRenderer* renderer, EditModeType type) +{ + switch (type) + { + case EditModeType::Select: return new SelectMode (renderer); + case EditModeType::Draw: return new DrawMode (renderer); + case EditModeType::Circle: return new CircleMode (renderer); + case EditModeType::MagicWand: return new MagicWandMode (renderer); + } + + throw std::logic_error ("bad type given to AbstractEditMode::createByType"); +} + +GLRenderer* AbstractEditMode::renderer() const +{ + return _renderer; +} + +AbstractDrawMode::AbstractDrawMode (GLRenderer* renderer) : + AbstractEditMode (renderer), + _polybrush (QBrush (QColor (64, 192, 0, 128))) +{ + // Disable the context menu - we need the right mouse button + // for removing vertices. + renderer->setContextMenuPolicy (Qt::NoContextMenu); + + // Use the crosshair cursor when drawing. + renderer->setCursor (Qt::CrossCursor); + + // Clear the selection when beginning to draw. + getCurrentDocument()->clearSelection(); + + g_win->updateSelection(); + m_drawedVerts.clear(); +} + +AbstractSelectMode::AbstractSelectMode (GLRenderer* renderer) : + AbstractEditMode (renderer) +{ + renderer->unsetCursor(); + renderer->setContextMenuPolicy (Qt::DefaultContextMenu); +} + +// ============================================================================= +// +void AbstractDrawMode::addDrawnVertex (Vertex const& pos) +{ + if (preAddVertex (pos)) + return; + + m_drawedVerts << pos; +} + +virtual void AbstractDrawMode::mouseReleased (MouseEventData const& data) +{ + if (data.releasedButtons & Qt::MidButton) + { + // Find the closest vertex to our cursor + double minimumDistance = 1024.0; + const Vertex* closest = null; + Vertex cursorPosition = renderer()->coordconv2_3 (data.ev->pos(), false); + QPoint cursorPosition2D (data.ev->pos()); + const Axis relZ = renderer()->getRelativeZ(); + QList<Vertex> vertices; + + for (auto it = renderer()->document()->vertices().begin(); it != renderer()->document()->vertices().end(); ++it) + vertices << it.key(); + + // Sort the vertices in order of distance to camera + std::sort (vertices.begin(), vertices.end(), [&](const Vertex& a, const Vertex& b) -> bool + { + if (renderer()->getFixedCamera (renderer()->camera()).negatedDepth) + return a[relZ] > b[relZ]; + + return a[relZ] < b[relZ]; + }); + + for (const Vertex& vrt : vertices) + { + // If the vertex in 2d space is very close to the cursor then we use + // it regardless of depth. + QPoint vect2d = renderer()->coordconv3_2 (vrt) - cursorPosition2D; + const double distance2DSquared = std::pow (vect2d.x(), 2) + std::pow (vect2d.y(), 2); + if (distance2DSquared < 16.0 * 16.0) + { + closest = &vrt; + break; + } + + // Check if too far away from the cursor. + if (distance2DSquared > 64.0 * 64.0) + continue; + + // Not very close to the cursor. Compare using true distance, + // including depth. + const double distanceSquared = (vrt - cursorPosition).lengthSquared(); + + if (distanceSquared < minimumDistance) + { + minimumDistance = distanceSquared; + closest = &vrt; + } + } + + if (closest != null) + addDrawnVertex (*closest); + + return true; + } + + return false; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/editmodes/abstracteditmode.h Fri Jul 04 22:19:01 2014 +0300 @@ -0,0 +1,79 @@ +#pragma once +#include "../main.h" +#include "../glRenderer.h" + +class QPainter; +class GLRenderer; + +enum class EditModeType +{ + Select, + Draw, + Circle, + MagicWand, +}; + +class AbstractEditMode +{ + GLRenderer* _renderer; + QBrush _polybrush; + +public: + struct MouseEventData + { + QMouseEvent* ev; + Qt::KeyboardModifiers keymods; + bool mouseMoved; + Qt::MouseButtons releasedButtons; + }; + + AbstractEditMode (GLRenderer* renderer); + + virtual bool allowFreeCamera() const = 0; + virtual void render (QPainter& painter) const {}; + GLRenderer* renderer() const; + virtual EditModeType type() const = 0; + virtual void mousePressed (MouseEventData const& data) {} + virtual void mouseReleased (MouseEventData const& data) {} + + static AbstractEditMode* createByType (GLRenderer* renderer, EditModeType type); +}; + +// +// Base class for draw-like edit modes +// +class AbstractDrawMode : public AbstractEditMode +{ + QList<Vertex> m_drawedVerts; + Vertex m_rectverts[4]; + +public: + AbstractDrawMode (GLRenderer* renderer); + + virtual bool allowFreeCamera() const override + { + return false; + } + + virtual void mouseReleased (MouseEventData const& data) override; + void addDrawnVertex (const Vertex& pos); + + virtual bool preAddVertex (Vertex const&) + { + return false; + } +}; + +// +// Base class for select-like edit modes +// +class AbstractSelectMode : public AbstractEditMode +{ +public: + AbstractSelectMode (GLRenderer* renderer); + + virtual bool allowFreeCamera() const override + { + return true; + } +};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/editmodes/circlemode.cc Fri Jul 04 22:19:01 2014 +0300 @@ -0,0 +1,133 @@ +#include <QPainter> +#include "circlemode.h" +#include "../miscallenous.h" +#include "../ldObject.h" + +CircleMode::CircleMode (GLRenderer* renderer) : + Super (renderer) {} + +double CircleMode::getCircleDrawDist (int pos) const +{ + assert (m_drawedVerts.size() >= pos + 1); + Vertex v1 = (m_drawedVerts.size() >= pos + 2) ? m_drawedVerts[pos + 1] : + renderer()->coordconv2_3 (renderer()->mousePosition(), false); + Axis localx, localy; + renderer()->getRelativeAxes (localx, localy); + double dx = m_drawedVerts[0][localx] - v1[localx]; + double dy = m_drawedVerts[0][localy] - v1[localy]; + return Grid::snap (sqrt ((dx * dx) + (dy * dy)), Grid::Coordinate); +} + +Matrix CircleMode::getCircleDrawMatrix (double scale) +{ + // Matrix templates. 2 is substituted with the scale value, 1 is inverted to -1 if needed. + static const Matrix templates[3] = + { + { 2, 0, 0, 0, 1, 0, 0, 0, 2 }, + { 2, 0, 0, 0, 0, 2, 0, 1, 0 }, + { 0, 1, 0, 2, 0, 0, 0, 0, 2 }, + }; + + Matrix transform = templates[renderer()->camera() % 3]; + + for (int i = 0; i < 9; ++i) + { + if (transform[i] == 2) + transform[i] = scale; + elif (transform[i] == 1 && renderer()->camera() >= 3) + transform[i] = -1; + } + + return transform; +} + +void CircleMode::render (QPainter& painter) const +{ + // If we have not specified the center point of the circle yet, preview it on the screen. + if (m_drawedVerts.isEmpty()) + { + renderer()->drawBlip (painter, renderer()->coordconv3_2 (renderer()->position3D())); + } + else + { + QVector<Vertex> verts, verts2; + const double dist0 = getCircleDrawDist (0), + dist1 = (m_drawedVerts.size() >= 2) ? getCircleDrawDist (1) : -1; + const int segs = g_lores; + const double angleUnit = (2 * pi) / segs; + Axis relX, relY; + QVector<QPoint> ringpoints, circlepoints, circle2points; + + renderer()->getRelativeAxes (relX, relY); + + // Calculate the preview positions of vertices + for (int i = 0; i < segs; ++i) + { + Vertex v = g_origin; + v.setCoordinate (relX, m_drawedVerts[0][relX] + (cos (i * angleUnit) * dist0)); + v.setCoordinate (relY, m_drawedVerts[0][relY] + (sin (i * angleUnit) * dist0)); + verts << v; + + if (dist1 != -1) + { + v.setCoordinate (relX, m_drawedVerts[0][relX] + (cos (i * angleUnit) * dist1)); + v.setCoordinate (relY, m_drawedVerts[0][relY] + (sin (i * angleUnit) * dist1)); + verts2 << v; + } + } + + int i = 0; + for (const Vertex& v : verts + verts2) + { + // Calculate the 2D point of the vertex + QPoint point = renderer()->coordconv3_2 (v); + + // Draw a green blip at where it is + renderer()->drawBlip (painter, point); + + // Add it to the list of points for the green ring fill. + ringpoints << point; + + // Also add the circle points to separate lists + if (i < verts.size()) + circlepoints << point; + else + circle2points << point; + + ++i; + } + + // Insert the first point as the seventeenth one so that + // the ring polygon is closed properly. + if (ringpoints.size() >= 16) + ringpoints.insert (16, ringpoints[0]); + + // Same for the outer ring. Note that the indices are offset by 1 + // because of the insertion done above bumps the values. + if (ringpoints.size() >= 33) + ringpoints.insert (33, ringpoints[17]); + + // Draw the ring + painter.setBrush ((m_drawedVerts.size() >= 2) ? _polybrush : Qt::NoBrush); + painter.setPen (Qt::NoPen); + painter.drawPolygon (QPolygon (ringpoints)); + + // Draw the circles + painter.setBrush (Qt::NoBrush); + painter.setPen (renderer()->getLinePen()); + painter.drawPolygon (QPolygon (circlepoints)); + painter.drawPolygon (QPolygon (circle2points)); + + // Draw the current radius in the middle of the circle. + QPoint origin = renderer()->coordconv3_2 (m_drawedVerts[0]); + QString label = QString::number (dist0); + painter.setPen (textpen); + painter.drawText (origin.x() - (metrics.width (label) / 2), origin.y(), label); + + if (m_drawedVerts.size() >= 2) + { + painter.drawText (origin.x() - (metrics.width (label) / 2), + origin.y() + metrics.height(), QString::number (dist1)); + } + } +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/editmodes/circlemode.h Fri Jul 04 22:19:01 2014 +0300 @@ -0,0 +1,15 @@ +#include "abstracteditmode.h" + +class CircleMode : public AbstractDrawMode +{ + DEFINE_CLASS (CircleMode, AbstractDrawMode) + +public: + CircleMode (GLRenderer* renderer); + + virtual void render (QPainter& painter) const override; + virtual EditModeType type() const override; + + double getCircleDrawDist (int pos) const; + Matrix getCircleDrawMatrix (double scale); +};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/editmodes/drawmode.cc Fri Jul 04 22:19:01 2014 +0300 @@ -0,0 +1,151 @@ +#include <QPainter> +#include "drawmode.h" + +CFGENTRY (Bool, drawLineLengths, true) +CFGENTRY (Bool, drawAngles, false) + +DrawMode::DrawMode (GLRenderer* renderer) : + Super (renderer), + _rectdraw (false) {} + +EditModeType DrawMode::type() const +{ + return EditModeType::Draw; +} + +void DrawMode::render (QPainter& painter) const +{ + QPoint poly[4]; + Vertex poly3d[4]; + int numverts = 4; + QFontMetrics metrics (QFont()); + + // Calculate polygon data + if (not _rectdraw) + { + numverts = m_drawedVerts.size() + 1; + int i = 0; + + for (Vertex& vert : m_drawedVerts) + poly3d[i++] = vert; + + // Draw the cursor vertex as the last one in the list. + if (numverts <= 4) + poly3d[i] = renderer()->position3D(); + else + numverts = 4; + } + else + { + // Get vertex information from m_rectverts + if (m_drawedVerts.size() > 0) + for (int i = 0; i < numverts; ++i) + poly3d[i] = m_rectverts[i]; + else + poly3d[0] = renderer()->position3D(); + } + + // Convert to 2D + for (int i = 0; i < numverts; ++i) + poly[i] = renderer()->coordconv3_2 (poly3d[i]); + + if (numverts > 0) + { + // Draw the polygon-to-be + painter.setBrush (_polybrush); + painter.drawPolygon (poly, numverts); + + // Draw vertex blips + for (int i = 0; i < numverts; ++i) + { + QPoint& blip = poly[i]; + painter.setPen (renderer()->linePen()); + renderer()->drawBlip (painter, blip); + + // Draw their coordinates + painter.setPen (renderer()->textPen()); + painter.drawText (blip.x(), blip.y() - 8, poly3d[i].toString (true)); + } + + // Draw line lenghts and angle info if appropriate + if (numverts >= 2) + { + int numlines = (m_drawedVerts.size() == 1) ? 1 : m_drawedVerts.size() + 1; + painter.setPen (renderer()->textPen()); + + for (int i = 0; i < numlines; ++i) + { + const int j = (i + 1 < numverts) ? i + 1 : 0; + const int h = (i - 1 >= 0) ? i - 1 : numverts - 1; + + if (cfg::drawLineLengths) + { + const QString label = QString::number ((poly3d[j] - poly3d[i]).length()); + QPoint origin = QLineF (poly[i], poly[j]).pointAt (0.5).toPoint(); + painter.drawText (origin, label); + } + + if (cfg::drawAngles) + { + QLineF l0 (poly[h], poly[i]), + l1 (poly[i], poly[j]); + + double angle = 180 - l0.angleTo (l1); + + if (angle < 0) + angle = 180 - l1.angleTo (l0); + + QString label = QString::number (angle) + QString::fromUtf8 (QByteArray ("\302\260")); + QPoint pos = poly[i]; + pos.setY (pos.y() + metrics.height()); + + painter.drawText (pos, label); + } + } + } + } +} + +bool DrawMode::preAddVertex (Vertex const& pos) +{ + // If we picked an already-existing vertex, stop drawing + for (Vertex& vert : m_drawedVerts) + { + if (vert == pos) + { + endDraw (true); + return true; + } + } + + return false; +} + +void DrawMode::mouseReleased (MouseEventData const& data) +{ + if (_rectdraw) + { + if (m_drawedVerts.size() == 2) + { + endDraw (true); + return; + } + } + else + { + // If we have 4 verts, stop drawing. + if (m_drawedVerts.size() >= 4) + { + endDraw (true); + return; + } + + if (m_drawedVerts.isEmpty() && ev->modifiers() & Qt::ShiftModifier) + { + _rectdraw = true; + updateRectVerts(); + } + } + + addDrawnVertex (renderer()->position3D()); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/editmodes/drawmode.h Fri Jul 04 22:19:01 2014 +0300 @@ -0,0 +1,15 @@ +#include "abstracteditmode.h" + +class DrawMode : public AbstractDrawMode +{ + DEFINE_CLASS (DrawMode, AbstractDrawMode) + bool _rectdraw; + +public: + DrawMode (GLRenderer* renderer); + + virtual void render (QPainter& painter) const override; + virtual EditModeType type() const override; + virtual bool preAddVertex (Vertex const& pos) override; + virtual void mouseReleased (MouseEventData const& data) override; +};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/editmodes/magicwandmode.cc Fri Jul 04 22:19:01 2014 +0300 @@ -0,0 +1,210 @@ +/* + * LDForge: LDraw parts authoring CAD + * Copyright (C) 2013, 2014 Santeri 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 <QMouseEvent> +#include "magicwandmode.h" +#include "../ldDocument.h" +#include "../mainWindow.h" + +MagicWandMode::MagicWandMode (GLRenderer* renderer) : + Super (renderer) +{ + // Get vertex<->object data + for (LDObjectPtr obj : getCurrentDocument()->objects()) + { + // Note: this deliberately only takes vertex-objects into account. + // The magic wand does not process subparts. + for (int i = 0; i < obj->numVertices(); ++i) + _vertices[obj->vertex (i)] << obj; + } +} + +EditModeType MagicWandMode::type() const +{ + return EditModeType::MagicWand; +} + +bool MagicWandMode::allowFreeCamera() const +{ + return true; +} + +void MagicWandMode::fillBoundaries (LDObjectPtr obj, QVector<BoundaryType>& boundaries, QVector<LDObjectPtr>& candidates) +{ + // All boundaries obviously share vertices with the object, therefore they're all in the list + // of candidates. + for (auto it = candidates.begin(); it != candidates.end(); ++it) + { + if ((*it)->type() != OBJ_Line || (*it)->vertex (0) == (*it)->vertex (1)) + continue; + + int matches = 0; + + for (int i = 0; i < obj->numVertices(); ++i) + { + if (not eq (obj->vertex (i), (*it)->vertex (0), (*it)->vertex (1))) + continue; + + if (++matches == 2) + { + // Boundary found. Add to boundaries list and get it off the candidates list. + boundaries.append (std::make_tuple ((*it)->vertex (0), (*it)->vertex (1))); + break; + } + } + } +} + +void MagicWandMode::doMagic (LDObjectPtr obj, MagicWandMode::MagicType type) +{ + if (obj == null) + { + if (type == Set) + { + getCurrentDocument()->clearSelection(); + g_win->buildObjList(); + } + + return; + } + + int matchesneeded = 0; + QVector<BoundaryType> boundaries; + LDObjectType objtype = obj->type(); + + if (type != InternalRecursion) + { + _selection.clear(); + _selection.append (obj); + } + + switch (obj->type()) + { + case OBJ_Line: + case OBJ_CondLine: + matchesneeded = 1; + break; + + case OBJ_Triangle: + case OBJ_Quad: + matchesneeded = 2; + break; + + default: + return; + } + + QVector<LDObjectPtr> candidates; + + // Get the list of objects that touch this object, i.e. share a vertex + // with this. + for (int i = 0; i < obj->numVertices(); ++i) + candidates += _vertices[obj->vertex (i)]; + + removeDuplicates (candidates); + + // If we're dealing with surfaces, get a list of boundaries. + if (matchesneeded > 1) + fillBoundaries (obj, boundaries, candidates); + + for (LDObjectPtr candidate : candidates) + { + try + { + // If we're doing this on lines, we need exact type match. Surface types (quads and + // triangles) can be mixed. Also don't consider self a candidate, and don't consider + // objects we have already processed. + if ((candidate == obj) || + (candidate->color() != obj->color()) || + (_selection.contains (candidate)) || + (matchesneeded == 1 && (candidate->type() != objtype)) || + ((candidate->numVertices() > 2) ^ (matchesneeded == 2))) + { + throw 0; + } + + // Now ensure the two objects share enough vertices. + QVector<Vertex> matches; + + for (int i = 0; i < obj->numVertices(); ++i) + { + for (int j = 0; j < candidate->numVertices(); ++j) + { + if (obj->vertex(i) == candidate->vertex(j)) + { + matches << obj->vertex(i); + break; + } + } + } + + if (matches.size() < matchesneeded) + throw 0; // Not enough matches. + + // Check if a boundary gets in between the objects. + for (auto boundary : boundaries) + { + if (eq (matches[0], std::get<0> (boundary), std::get<1> (boundary)) && + eq (matches[1], std::get<0> (boundary), std::get<1> (boundary))) + { + throw 0; + } + } + + _selection.append (candidate); + doMagic (candidate, InternalRecursion); + } + catch (int&) + { + continue; + } + } + + switch (type) + { + case Set: + getCurrentDocument()->clearSelection(); + case Additive: + for (LDObjectPtr obj : _selection) + obj->select(); + break; + + case Subtractive: + for (LDObjectPtr obj : _selection) + obj->deselect(); + break; + + case InternalRecursion: + break; + } + + if (type != InternalRecursion) + g_win->buildObjList(); +} + +void MagicWandMode::mouseReleased (MouseEventData const& data) +{ + MagicType wandtype = MagicWandMode::Set; + + if (data.keymods & Qt::ShiftModifier) + wandtype = MagicWandMode::Additive; + elif (data.keymods & Qt::ControlModifier) + wandtype = MagicWandMode::Subtractive; + + doMagic (renderer()->pickOneObject (data.ev->x(), data.ev->y()), wandtype); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/editmodes/magicwandmode.h Fri Jul 04 22:19:01 2014 +0300 @@ -0,0 +1,49 @@ +/* + * LDForge: LDraw parts authoring CAD + * Copyright (C) 2013, 2014 Santeri 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 "abstracteditmode.h" +#include "../basics.h" +#include <QMap> +#include <QVector> + +class MagicWandMode : public AbstractSelectMode +{ + QMap<Vertex, QVector<LDObjectPtr>> _vertices; + QVector<LDObjectPtr> _selection; + + DEFINE_CLASS (MagicWandMode, AbstractSelectMode) + +public: + using BoundaryType = std::tuple<Vertex, Vertex>; + enum MagicType + { + Set, + Additive, + Subtractive, + InternalRecursion + }; + + MagicWandMode (GLRenderer* renderer); + void doMagic (LDObjectPtr obj, MagicType type); + virtual EditModeType type() const override; + virtual void mouseReleased (MouseEventData const& data) override; + +private: + void fillBoundaries (LDObjectPtr obj, QVector<BoundaryType>& boundaries, QVector<LDObjectPtr>& candidates); +};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/editmodes/selectmode.cc Fri Jul 04 22:19:01 2014 +0300 @@ -0,0 +1,35 @@ +#include <QMouseEvent> +#include "../glRenderer.h" +#include "selectmode.h" + +SelectMode::SelectMode (GLRenderer* renderer) : + Super (renderer) {} + +EditModeType SelectMode::type() const +{ + return EditModeType::Select; +} + +void SelectMode::mouseReleased (MouseEventData const& data) +{ + if (not data.mouseMoved) + _rangepick = false; + + if (not _rangepick) + _addpick = (data.keymods & Qt::ControlModifier); + + if (not data.mouseMoved || _rangepick) + renderer()->pick (data.ev->x(), data.ev->y()); +} + +void SelectMode::mousePressed (MouseEventData const& data) +{ + if (data.ev->modifiers() & Qt::ControlModifier) + { + _rangepick = true; + _rangeStart.setX (data.ev->x()); + _rangeStart.setY (data.ev->y()); + _addpick = (data.keymods & Qt::AltModifier); + data.ev->accept(); + } +} \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/editmodes/selectmode.h Fri Jul 04 22:19:01 2014 +0300 @@ -0,0 +1,16 @@ +#include "abstracteditmode.h" + +class SelectMode : public AbstractSelectMode +{ + QPoint _rangeStart; + bool _rangepick; + bool _addpick; + + DEFINE_CLASS (SelectMode, AbstractSelectMode) + +public: + SelectMode (GLRenderer* renderer); + + virtual void mouseReleased (MouseEventData const& data) override; + virtual EditModeType type() const override; +}; \ No newline at end of file
--- a/src/glRenderer.cc Fri Jul 04 00:09:37 2014 +0300 +++ b/src/glRenderer.cc Fri Jul 04 22:19:01 2014 +0300 @@ -55,15 +55,6 @@ {{ 0, -1, 0 }, Z, Y, false, true, true }, // right }; -// Matrix templates for circle drawing. 2 is substituted with -// the scale value, 1 is inverted to -1 if needed. -static const Matrix g_circleDrawMatrixTemplates[3] = -{ - { 2, 0, 0, 0, 1, 0, 0, 0, 2 }, - { 2, 0, 0, 0, 0, 2, 0, 1, 0 }, - { 0, 1, 0, 2, 0, 0, 0, 0, 2 }, -}; - CFGENTRY (String, backgroundColor, "#FFFFFF") CFGENTRY (String, mainColor, "#A0A0A0") CFGENTRY (Float, mainColorAlpha, 1.0) @@ -75,8 +66,6 @@ CFGENTRY (Bool, drawWireframe, false) CFGENTRY (Bool, useLogoStuds, false) CFGENTRY (Bool, antiAliasedLines, true) -CFGENTRY (Bool, drawLineLengths, true) -CFGENTRY (Bool, drawAngles, false) CFGENTRY (Bool, randomColors, false) CFGENTRY (Bool, highlightObjectBelowCursor, true) CFGENTRY (Bool, drawSurfaces, true) @@ -116,11 +105,10 @@ // GLRenderer::GLRenderer (QWidget* parent) : QGLWidget (parent) { - m_isPicking = m_rangepick = false; + m_isPicking = false; m_camera = (ECamera) cfg::camera; m_drawToolTip = false; - m_editMode = ESelectMode; - m_rectdraw = false; + m_editmode = AbstractEditMode::createByType (EditModeType::Select); m_panning = false; m_compiler = new GLCompiler (this); setDrawOnly (false); @@ -133,7 +121,6 @@ m_thickBorderPen = QPen (QColor (0, 0, 0, 208), 2, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin); m_thinBorderPen = m_thickBorderPen; m_thinBorderPen.setWidth (1); - m_wand = null; setAcceptDrops (true); connect (m_toolTipTimer, SIGNAL (timeout()), this, SLOT (slot_toolTipTimer())); @@ -523,7 +510,7 @@ assert (camera() != EFreeCamera); Vertex pos3d; - const LDFixedCameraInfo* cam = &g_FixedCameras[camera()]; + const LDFixedCamera* cam = &g_FixedCameras[camera()]; const Axis axisX = cam->axisX; const Axis axisY = cam->axisY; const int negXFac = cam->negX ? -1 : 1, @@ -560,7 +547,7 @@ QPoint GLRenderer::coordconv3_2 (const Vertex& pos3d) const { GLfloat m[16]; - const LDFixedCameraInfo* cam = &g_FixedCameras[camera()]; + const LDFixedCamera* cam = &g_FixedCameras[camera()]; const Axis axisX = cam->axisX; const Axis axisY = cam->axisY; const int negXFac = cam->negX ? -1 : 1, @@ -583,6 +570,19 @@ return QPoint (rx, -ry); } +QPen GLRenderer::textPen() const +{ + return QPen (m_darkbg ? Qt::white : Qt::black); +} + +QPen GLRenderer::linePen() const +{ + QPen linepen (m_thinBorderPen); + linepen.setWidth (2); + linepen.setColor (luma (m_bgcolor) < 40 ? Qt::white : Qt::black); + return linepen; +} + // ============================================================================= // void GLRenderer::paintEvent (QPaintEvent*) @@ -593,8 +593,6 @@ initGLData(); drawGLScene(); - const QPen textpen (m_darkbg ? Qt::white : Qt::black); - const QBrush polybrush (QColor (64, 192, 0, 128)); QPainter paint (this); QFontMetrics metrics = QFontMetrics (QFont()); paint.setRenderHint (QPainter::HighQualityAntialiasing); @@ -609,13 +607,13 @@ QString text = format ("Rotation: (%1, %2, %3)\nPanning: (%4, %5), Zoom: %6", rot(X), rot(Y), rot(Z), pan(X), pan(Y), zoom()); QRect textSize = metrics.boundingRect (0, 0, m_width, m_height, Qt::AlignCenter, text); - paint.setPen (textpen); + paint.setPen (textPen()); paint.drawText ((width() - textSize.width()) / 2, height() - textSize.height(), textSize.width(), textSize.height(), Qt::AlignCenter, text); } #endif - if (camera() != EFreeCamera && !isPicking()) + if (camera() != EFreeCamera && not isPicking()) { // Paint the overlay image if we have one const LDGLOverlay& overlay = currentDocumentData().overlays[camera()]; @@ -634,210 +632,26 @@ QString text = format (tr ("X: %1, Y: %2, Z: %3"), m_position3D[X], m_position3D[Y], m_position3D[Z]); QFontMetrics metrics = QFontMetrics (font()); QRect textSize = metrics.boundingRect (0, 0, m_width, m_height, Qt::AlignCenter, text); - paint.setPen (textpen); + paint.setPen (textPen()); paint.drawText (m_width - textSize.width(), m_height - 16, textSize.width(), textSize.height(), Qt::AlignCenter, text); - - QPen linepen = m_thinBorderPen; - linepen.setWidth (2); - linepen.setColor (luma (m_bgcolor) < 40 ? Qt::white : Qt::black); - - // Mode-specific rendering - if (editMode() == EDrawMode) - { - QPoint poly[4]; - Vertex poly3d[4]; - int numverts = 4; - - // Calculate polygon data - if (not m_rectdraw) - { - numverts = m_drawedVerts.size() + 1; - int i = 0; - - for (Vertex& vert : m_drawedVerts) - poly3d[i++] = vert; - - // Draw the cursor vertex as the last one in the list. - if (numverts <= 4) - poly3d[i] = m_position3D; - else - numverts = 4; - } - else - { - // Get vertex information from m_rectverts - if (m_drawedVerts.size() > 0) - for (int i = 0; i < numverts; ++i) - poly3d[i] = m_rectverts[i]; - else - poly3d[0] = m_position3D; - } - - // Convert to 2D - for (int i = 0; i < numverts; ++i) - poly[i] = coordconv3_2 (poly3d[i]); - - if (numverts > 0) - { - // Draw the polygon-to-be - paint.setBrush (polybrush); - paint.drawPolygon (poly, numverts); - - // Draw vertex blips - for (int i = 0; i < numverts; ++i) - { - QPoint& blip = poly[i]; - paint.setPen (linepen); - drawBlip (paint, blip); - - // Draw their coordinates - paint.setPen (textpen); - paint.drawText (blip.x(), blip.y() - 8, poly3d[i].toString (true)); - } - - // Draw line lenghts and angle info if appropriate - if (numverts >= 2) - { - int numlines = (m_drawedVerts.size() == 1) ? 1 : m_drawedVerts.size() + 1; - paint.setPen (textpen); - - for (int i = 0; i < numlines; ++i) - { - const int j = (i + 1 < numverts) ? i + 1 : 0; - const int h = (i - 1 >= 0) ? i - 1 : numverts - 1; - - if (cfg::drawLineLengths) - { - const QString label = QString::number ((poly3d[j] - poly3d[i]).length()); - QPoint origin = QLineF (poly[i], poly[j]).pointAt (0.5).toPoint(); - paint.drawText (origin, label); - } - - if (cfg::drawAngles) - { - QLineF l0 (poly[h], poly[i]), - l1 (poly[i], poly[j]); - - double angle = 180 - l0.angleTo (l1); - - if (angle < 0) - angle = 180 - l1.angleTo (l0); - - QString label = QString::number (angle) + QString::fromUtf8 (QByteArray ("\302\260")); - QPoint pos = poly[i]; - pos.setY (pos.y() + metrics.height()); - - paint.drawText (pos, label); - } - } - } - } - } - elif (editMode() == ECircleMode) - { - // If we have not specified the center point of the circle yet, preview it on the screen. - if (m_drawedVerts.isEmpty()) - drawBlip (paint, coordconv3_2 (m_position3D)); - else - { - QVector<Vertex> verts, verts2; - const double dist0 = getCircleDrawDist (0), - dist1 = (m_drawedVerts.size() >= 2) ? getCircleDrawDist (1) : -1; - const int segs = g_lores; - const double angleUnit = (2 * pi) / segs; - Axis relX, relY; - QVector<QPoint> ringpoints, circlepoints, circle2points; - - getRelativeAxes (relX, relY); - - // Calculate the preview positions of vertices - for (int i = 0; i < segs; ++i) - { - Vertex v = g_origin; - v.setCoordinate (relX, m_drawedVerts[0][relX] + (cos (i * angleUnit) * dist0)); - v.setCoordinate (relY, m_drawedVerts[0][relY] + (sin (i * angleUnit) * dist0)); - verts << v; - - if (dist1 != -1) - { - v.setCoordinate (relX, m_drawedVerts[0][relX] + (cos (i * angleUnit) * dist1)); - v.setCoordinate (relY, m_drawedVerts[0][relY] + (sin (i * angleUnit) * dist1)); - verts2 << v; - } - } - - int i = 0; - for (const Vertex& v : verts + verts2) - { - // Calculate the 2D point of the vertex - QPoint point = coordconv3_2 (v); - - // Draw a green blip at where it is - drawBlip (paint, point); - - // Add it to the list of points for the green ring fill. - ringpoints << point; - - // Also add the circle points to separate lists - if (i < verts.size()) - circlepoints << point; - else - circle2points << point; - - ++i; - } - - // Insert the first point as the seventeenth one so that - // the ring polygon is closed properly. - if (ringpoints.size() >= 16) - ringpoints.insert (16, ringpoints[0]); - - // Same for the outer ring. Note that the indices are offset by 1 - // because of the insertion done above bumps the values. - if (ringpoints.size() >= 33) - ringpoints.insert (33, ringpoints[17]); - - // Draw the ring - paint.setBrush ((m_drawedVerts.size() >= 2) ? polybrush : Qt::NoBrush); - paint.setPen (Qt::NoPen); - paint.drawPolygon (QPolygon (ringpoints)); - - // Draw the circles - paint.setBrush (Qt::NoBrush); - paint.setPen (linepen); - paint.drawPolygon (QPolygon (circlepoints)); - paint.drawPolygon (QPolygon (circle2points)); - - { // Draw the current radius in the middle of the circle. - QPoint origin = coordconv3_2 (m_drawedVerts[0]); - QString label = QString::number (dist0); - paint.setPen (textpen); - paint.drawText (origin.x() - (metrics.width (label) / 2), origin.y(), label); - - if (m_drawedVerts.size() >= 2) - { - label = QString::number (dist1); - paint.drawText (origin.x() - (metrics.width (label) / 2), origin.y() + metrics.height(), label); - } - } - } - } } - // Camera icons if (not isPicking()) { + // Draw edit mode HUD + m_editmode->render (paint); + // Draw a background for the selected camera paint.setPen (m_thinBorderPen); paint.setBrush (QBrush (QColor (0, 128, 160, 128))); paint.drawRect (m_cameraIcons[camera()].selRect); - // Draw the actual icons + // Draw the camera icons for (CameraIcon& info : m_cameraIcons) { // Don't draw the free camera icon when in draw mode - if (&info == &m_cameraIcons[EFreeCamera] && not eq (editMode(), ESelectMode, EMagicWandMode)) + if (&info == &m_cameraIcons[EFreeCamera] && not m_editmode->allowFreeCamera()) continue; paint.drawPixmap (info.destRect, *info.img, info.srcRect); @@ -851,7 +665,7 @@ QString label; label = format (formatstr, tr (g_CameraNames[camera()])); - paint.setPen (textpen); + paint.setPen (textPen()); paint.drawText (QPoint (margin, height() - (margin + metrics.descent())), label); } @@ -873,7 +687,7 @@ { int y = 0; const int margin = 2; - QColor penColor = textpen.color(); + QColor penColor = textPen().color(); for (const MessageManager::Line& line : messageLog()->getLines()) { @@ -927,39 +741,21 @@ // ============================================================================= // -void GLRenderer::addDrawnVertex (Vertex pos) -{ - // If we picked an already-existing vertex, stop drawing - if (editMode() == EDrawMode) - { - for (Vertex& vert : m_drawedVerts) - { - if (vert == pos) - { - endDraw (true); - return; - } - } - } - - m_drawedVerts << pos; -} - -// ============================================================================= -// void GLRenderer::mouseReleaseEvent (QMouseEvent* ev) { const bool wasLeft = (m_lastButtons & Qt::LeftButton) && not (ev->buttons() & Qt::LeftButton), wasRight = (m_lastButtons & Qt::RightButton) && not (ev->buttons() & Qt::RightButton), wasMid = (m_lastButtons & Qt::MidButton) && not (ev->buttons() & Qt::MidButton); + Qt::MouseButtons releasedbuttons = m_lastButtons & ~ev->buttons(); + if (m_panning) m_panning = false; if (wasLeft) { // Check if we selected a camera icon - if (not m_rangepick) + if (m_totalmove < 10) { for (CameraIcon & info : m_cameraIcons) { @@ -971,35 +767,21 @@ } } + if (not isDrawOnly()) + { + AbstractEditMode::MouseEventData data; + data.ev = ev; + data.mouseMoved = m_totalmove >= 10; + data.keymods = m_keymods; + data.releasedButtons = releasedbuttons; + m_editmode->mouseReleased (data); + } + switch (editMode()) { case EDrawMode: { - if (m_rectdraw) - { - if (m_drawedVerts.size() == 2) - { - endDraw (true); - return; - } - } - else - { - // If we have 4 verts, stop drawing. - if (m_drawedVerts.size() >= 4) - { - endDraw (true); - return; - } - - if (m_drawedVerts.isEmpty() && ev->modifiers() & Qt::ShiftModifier) - { - m_rectdraw = true; - updateRectVerts(); - } - } - - addDrawnVertex (m_position3D); + break; } @@ -1014,35 +796,6 @@ addDrawnVertex (m_position3D); break; } - - case ESelectMode: - { - if (not isDrawOnly()) - { - if (m_totalmove < 10) - m_rangepick = false; - - if (not m_rangepick) - m_addpick = (m_keymods & Qt::ControlModifier); - - if (m_totalmove < 10 || m_rangepick) - pick (ev->x(), ev->y()); - } - break; - } - - case EMagicWandMode: - { - MagicWand::MagicType wandtype = MagicWand::Set; - - if (m_keymods & Qt::ShiftModifier) - wandtype = MagicWand::Additive; - elif (m_keymods & Qt::ControlModifier) - wandtype = MagicWand::Subtractive; - - m_wand->doMagic (pickOneObject (ev->x(), ev->y()), wandtype); - break; - } } m_rangepick = false; @@ -1050,55 +803,7 @@ if (wasMid && editMode() != ESelectMode && m_drawedVerts.size() < 4 && m_totalmove < 10) { - // Find the closest vertex to our cursor - double minimumDistance = 1024.0; - const Vertex* closest = null; - Vertex cursorPosition = coordconv2_3 (m_mousePosition, false); - QPoint cursorPosition2D (m_mousePosition); - const Axis relZ = getRelativeZ(); - QList<Vertex> vertices; - - for (auto it = document()->vertices().begin(); it != document()->vertices().end(); ++it) - vertices << it.key(); - - // Sort the vertices in order of distance to camera - std::sort (vertices.begin(), vertices.end(), [&](const Vertex& a, const Vertex& b) -> bool - { - if (g_FixedCameras[camera()].negatedDepth) - return a[relZ] > b[relZ]; - - return a[relZ] < b[relZ]; - }); - - for (const Vertex& vrt : vertices) - { - // If the vertex in 2d space is very close to the cursor then we use - // it regardless of depth. - QPoint vect2d = coordconv3_2 (vrt) - cursorPosition2D; - const double distance2DSquared = std::pow (vect2d.x(), 2) + std::pow (vect2d.y(), 2); - if (distance2DSquared < 16.0 * 16.0) - { - closest = &vrt; - break; - } - - // Check if too far away from the cursor. - if (distance2DSquared > 64.0 * 64.0) - continue; - - // Not very close to the cursor. Compare using true distance, - // including depth. - const double distanceSquared = (vrt - cursorPosition).lengthSquared(); - - if (distanceSquared < minimumDistance) - { - minimumDistance = distanceSquared; - closest = &vrt; - } - } - - if (closest != null) - addDrawnVertex (*closest); + } if (wasRight && not m_drawedVerts.isEmpty()) @@ -1120,16 +825,6 @@ void GLRenderer::mousePressEvent (QMouseEvent* ev) { m_totalmove = 0; - - if (ev->modifiers() & Qt::ControlModifier) - { - m_rangepick = true; - m_rangeStart.setX (ev->x()); - m_rangeStart.setY (ev->y()); - m_addpick = (m_keymods & Qt::AltModifier); - ev->accept(); - } - m_lastButtons = ev->buttons(); } @@ -1229,6 +924,10 @@ // void GLRenderer::setCamera (const ECamera cam) { + // The edit mode may forbid the free camera. + if (cam == EFreeCamera && not m_editmode->allowFreeCamera()) + return; + m_camera = cam; cfg::camera = (int) cam; g_win->updateEditModeActions(); @@ -1368,55 +1067,17 @@ // ============================================================================= // -void GLRenderer::setEditMode (EditMode const& a) +void GLRenderer::setEditMode (EditModeType a) { - if (m_editMode == a) + if (m_editmode != null && m_editmode->type() == a) return; - m_editMode = a; - - if (a == EMagicWandMode) - m_wand = new MagicWand; - else - { - delete m_wand; - m_wand = null; - } - - switch (a) - { - case ESelectMode: - case EMagicWandMode: - { - unsetCursor(); - setContextMenuPolicy (Qt::DefaultContextMenu); - } break; + delete m_editmode; + m_editmode = AbstractEditMode::createByType (a); - case EDrawMode: - case ECircleMode: - { - // Cannot draw into the free camera - use top instead. - if (camera() == EFreeCamera) - setCamera (ETopCamera); - - // Disable the context menu - we need the right mouse button - // for removing vertices. - setContextMenuPolicy (Qt::NoContextMenu); - - // Use the crosshair cursor when drawing. - setCursor (Qt::CrossCursor); - - // Clear the selection when beginning to draw. - LDObjectList priorsel = selection(); - getCurrentDocument()->clearSelection(); - - for (LDObjectPtr obj : priorsel) - compileObject (obj); - - g_win->updateSelection(); - m_drawedVerts.clear(); - } break; - } + // If we cannot use the free camera, use the top one instead. + if (camera() == EFreeCamera && not m_editmode->allowFreeCamera()) + setCamera (ETopCamera); g_win->updateEditModeActions(); update(); @@ -1424,6 +1085,13 @@ // ============================================================================= // +EditModeType GLRenderer::currentEditModeType() const +{ + return m_editmode->type(); +} + +// ============================================================================= +// void GLRenderer::setDocument (LDDocumentPtr const& a) { m_document = a; @@ -1467,23 +1135,6 @@ // ============================================================================= // -Matrix GLRenderer::getCircleDrawMatrix (double scale) -{ - Matrix transform = g_circleDrawMatrixTemplates[camera() % 3]; - - for (int i = 0; i < 9; ++i) - { - if (transform[i] == 2) - transform[i] = scale; - elif (transform[i] == 1 && camera() >= 3) - transform[i] = -1; - } - - return transform; -} - -// ============================================================================= -// void GLRenderer::endDraw (bool accept) { (void) accept; @@ -1676,22 +1327,9 @@ // ============================================================================= // -double GLRenderer::getCircleDrawDist (int pos) const -{ - assert (m_drawedVerts.size() >= pos + 1); - Vertex v1 = (m_drawedVerts.size() >= pos + 2) ? m_drawedVerts[pos + 1] : coordconv2_3 (m_mousePosition, false); - Axis relX, relY; - getRelativeAxes (relX, relY); - double dx = m_drawedVerts[0][relX] - v1[relX]; - double dy = m_drawedVerts[0][relY] - v1[relY]; - return Grid::snap (sqrt ((dx * dx) + (dy * dy)), Grid::Coordinate); -} - -// ============================================================================= -// void GLRenderer::getRelativeAxes (Axis& relX, Axis& relY) const { - const LDFixedCameraInfo* cam = &g_FixedCameras[camera()]; + const LDFixedCamera* cam = &g_FixedCameras[camera()]; relX = cam->axisX; relY = cam->axisY; } @@ -1700,7 +1338,7 @@ // Axis GLRenderer::getRelativeZ() const { - const LDFixedCameraInfo* cam = &g_FixedCameras[camera()]; + const LDFixedCamera* cam = &g_FixedCameras[camera()]; return (Axis) (3 - cam->axisX - cam->axisY); } @@ -1788,7 +1426,7 @@ if (camid == (ECamera) -1) camid = camera(); - const LDFixedCameraInfo* cam = &g_FixedCameras[camid]; + const LDFixedCamera* cam = &g_FixedCameras[camid]; return (y) ? cam->axisY : cam->axisX; } @@ -2255,3 +1893,13 @@ ev->acceptProposedAction(); } } + +Vertex const& GLRenderer::position3D() const +{ + return m_position3D; +} + +LDFixedCamera const& GLRenderer::getFixedCamera (ECamera cam) +{ + return g_FixedCameras[cam]; +}
--- a/src/glRenderer.h Fri Jul 04 00:09:37 2014 +0300 +++ b/src/glRenderer.h Fri Jul 04 22:19:01 2014 +0300 @@ -23,6 +23,7 @@ #include "ldObject.h" #include "ldDocument.h" #include "glShared.h" +#include "editmodes/abstracteditmode.h" class GLCompiler; class MessageManager; @@ -32,15 +33,7 @@ class QSpinBox; class QLineEdit; class QTimer; -class MagicWand; - -enum EditMode -{ - ESelectMode, - EDrawMode, - ECircleMode, - EMagicWandMode, -}; +class MagicWandMode; // // Meta for overlays @@ -58,7 +51,7 @@ bool invalid; }; -struct LDFixedCameraInfo +struct LDFixedCamera { const char glrotate[3]; const Axis axisX, @@ -150,7 +143,6 @@ PROPERTY (public, MessageManager*, messageLog, setMessageLog, STOCK_WRITE) PROPERTY (private, bool, isPicking, setPicking, CUSTOM_WRITE) PROPERTY (public, LDDocumentPtr, document, setDocument, CUSTOM_WRITE) - PROPERTY (public, EditMode, editMode, setEditMode, CUSTOM_WRITE) PROPERTY (private, GLCompiler*, compiler, setCompiler, STOCK_WRITE) PROPERTY (public, LDObjectWeakPtr, objectAtCursor, setObjectAtCursor, STOCK_WRITE) PROPERTY (private, bool, isCameraMoving, setCameraMoving, STOCK_WRITE) @@ -162,26 +154,37 @@ inline ECamera camera() const; void clearOverlay(); void compileObject (LDObjectPtr obj); + Vertex coordconv2_3 (const QPoint& pos2d, bool snap) const; + QPoint coordconv3_2 (const Vertex& pos3d) const; + EditModeType currentEditModeType() const; + void drawBlip (QPainter& paint, QPoint pos) const; void drawGLScene(); void endDraw (bool accept); void forgetObject (LDObjectPtr obj); Axis getCameraAxis (bool y, ECamera camid = (ECamera) -1); const char* getCameraName() const; double getDepthValue() const; + LDFixedCamera const& getFixedCamera (ECamera cam) const; + void getRelativeAxes (Axis& relX, Axis& relY) const; + Axis getRelativeZ() const; LDGLOverlay& getOverlay (int newcam); uchar* getScreencap (int& w, int& h); void hardRefresh(); void highlightCursorObject(); void initGLData(); void initOverlaysFromObjects(); + QPen linePen() const; void needZoomToFit(); + Vertex const& position3D() const; void refresh(); void resetAngles(); void resetAllAngles(); void setBackground(); void setCamera (const ECamera cam); void setDepthValue (double depth); + void setEditMode (EditModeType type); bool setupOverlay (ECamera cam, QString file, int x, int y, int w, int h); + QPen textPen() const; void updateOverlayObjects(); void zoomNotch (bool inward); @@ -212,14 +215,11 @@ double m_virtWidth, m_virtHeight; bool m_darkbg, - m_rangepick, - m_addpick, m_drawToolTip, m_screencap, m_panning; QPoint m_mousePosition, - m_globalpos, - m_rangeStart; + m_globalpos; QPen m_thickBorderPen, m_thinBorderPen; ECamera m_camera, @@ -228,25 +228,14 @@ int m_width, m_height, m_totalmove; - QList<Vertex> m_drawedVerts; - bool m_rectdraw; - Vertex m_rectverts[4]; QColor m_bgcolor; - MagicWand* m_wand; + AbstractEditMode* m_editmode; - void addDrawnVertex (Vertex m_hoverpos); void calcCameraIcons(); void clampAngle (double& angle) const; inline LDGLData& currentDocumentData() const; - Vertex coordconv2_3 (const QPoint& pos2d, bool snap) const; - QPoint coordconv3_2 (const Vertex& pos3d) const; - void drawBlip (QPainter& paint, QPoint pos) const; void drawVBOs (EVBOSurface surface, EVBOComplement colors, GLenum type); LDOverlayPtr findOverlayObject (ECamera cam); - double getCircleDrawDist (int pos) const; - Matrix getCircleDrawMatrix (double scale); - void getRelativeAxes (Axis& relX, Axis& relY) const; - Axis getRelativeZ() const; inline double& pan (Axis ax); inline const double& pan (Axis ax) const; void pick (int mouseX, int mouseY);
--- a/src/ldDocument.cc Fri Jul 04 00:09:37 2014 +0300 +++ b/src/ldDocument.cc Fri Jul 04 22:19:01 2014 +0300 @@ -1485,6 +1485,9 @@ removeFromSelection (obj); assert (m_sel.isEmpty()); + + if (self() == getCurrentDocument()) + g_win->buildObjList(); } // =============================================================================
--- a/src/macros.h Fri Jul 04 00:09:37 2014 +0300 +++ b/src/macros.h Fri Jul 04 22:19:01 2014 +0300 @@ -63,6 +63,11 @@ #define readAccess(A) inline decltype(_##A) A() const { return _##A; } #define writeAccess(A,B) inline void B (decltype(_##A) const& a) const { _##A = a; } +#define DEFINE_CLASS(SELF, SUPER) \ +public: \ + using Self = SELF; \ + using Super = SUPER; + // ============================================================================= // #define elif(A) else if (A)
--- a/src/magicWand.cc Fri Jul 04 00:09:37 2014 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,187 +0,0 @@ -/* - * LDForge: LDraw parts authoring CAD - * Copyright (C) 2013, 2014 Santeri 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 "main.h" -#include "magicWand.h" -#include "ldDocument.h" -#include "mainWindow.h" - -MagicWand::MagicWand() -{ - // Get vertex<->object data - for (LDObjectPtr obj : getCurrentDocument()->objects()) - { - // Note: this deliberately only takes vertex-objects into account. - // The magic wand does not process subparts. - for (int i = 0; i < obj->numVertices(); ++i) - _vertices[obj->vertex (i)] << obj; - } -} - -void MagicWand::fillBoundaries (LDObjectPtr obj, QVector<BoundaryType>& boundaries, QVector<LDObjectPtr>& candidates) -{ - // All boundaries obviously share vertices with the object, therefore they're all in the list - // of candidates. - for (auto it = candidates.begin(); it != candidates.end(); ++it) - { - if ((*it)->type() != OBJ_Line || (*it)->vertex (0) == (*it)->vertex (1)) - continue; - - int matches = 0; - - for (int i = 0; i < obj->numVertices(); ++i) - { - if (not eq (obj->vertex (i), (*it)->vertex (0), (*it)->vertex (1))) - continue; - - if (++matches == 2) - { - // Boundary found. Add to boundaries list and get it off the candidates list. - boundaries.append (std::make_tuple ((*it)->vertex (0), (*it)->vertex (1))); - break; - } - } - } -} - -void MagicWand::doMagic (LDObjectPtr obj, MagicWand::MagicType type) -{ - if (obj == null) - { - if (type == Set) - { - getCurrentDocument()->clearSelection(); - g_win->buildObjList(); - } - - return; - } - - int matchesneeded = 0; - QVector<BoundaryType> boundaries; - LDObjectType objtype = obj->type(); - - if (type != InternalRecursion) - { - _selection.clear(); - _selection.append (obj); - } - - switch (obj->type()) - { - case OBJ_Line: - case OBJ_CondLine: - matchesneeded = 1; - break; - - case OBJ_Triangle: - case OBJ_Quad: - matchesneeded = 2; - break; - - default: - return; - } - - QVector<LDObjectPtr> candidates; - - // Get the list of objects that touch this object, i.e. share a vertex - // with this. - for (int i = 0; i < obj->numVertices(); ++i) - candidates += _vertices[obj->vertex (i)]; - - removeDuplicates (candidates); - - // If we're dealing with surfaces, get a list of boundaries. - if (matchesneeded > 1) - fillBoundaries (obj, boundaries, candidates); - - for (LDObjectPtr candidate : candidates) - { - try - { - // If we're doing this on lines, we need exact type match. Surface types (quads and - // triangles) can be mixed. Also don't consider self a candidate, and don't consider - // objects we have already processed. - if ((candidate == obj) || - (candidate->color() != obj->color()) || - (_selection.contains (candidate)) || - (matchesneeded == 1 && (candidate->type() != objtype)) || - ((candidate->numVertices() > 2) ^ (matchesneeded == 2))) - { - throw 0; - } - - // Now ensure the two objects share enough vertices. - QVector<Vertex> matches; - - for (int i = 0; i < obj->numVertices(); ++i) - { - for (int j = 0; j < candidate->numVertices(); ++j) - { - if (obj->vertex(i) == candidate->vertex(j)) - { - matches << obj->vertex(i); - break; - } - } - } - - if (matches.size() < matchesneeded) - throw 0; // Not enough matches. - - // Check if a boundary gets in between the objects. - for (auto boundary : boundaries) - { - if (eq (matches[0], std::get<0> (boundary), std::get<1> (boundary)) && - eq (matches[1], std::get<0> (boundary), std::get<1> (boundary))) - { - throw 0; - } - } - - _selection.append (candidate); - doMagic (candidate, InternalRecursion); - } - catch (int&) - { - continue; - } - } - - switch (type) - { - case Set: - getCurrentDocument()->clearSelection(); - case Additive: - for (LDObjectPtr obj : _selection) - obj->select(); - break; - - case Subtractive: - for (LDObjectPtr obj : _selection) - obj->deselect(); - break; - - case InternalRecursion: - break; - } - - if (type != InternalRecursion) - g_win->buildObjList(); -}
--- a/src/magicWand.h Fri Jul 04 00:09:37 2014 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,44 +0,0 @@ -/* - * LDForge: LDraw parts authoring CAD - * Copyright (C) 2013, 2014 Santeri 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 "basics.h" -#include <QMap> -#include <QVector> - -class MagicWand -{ - QMap<Vertex, QVector<LDObjectPtr>> _vertices; - QVector<LDObjectPtr> _selection; - -public: - using BoundaryType = std::tuple<Vertex, Vertex>; - enum MagicType - { - Set, - Additive, - Subtractive, - InternalRecursion - }; - - MagicWand(); - void doMagic (LDObjectPtr obj, MagicType type); - -private: - void fillBoundaries (LDObjectPtr obj, QVector<BoundaryType>& boundaries, QVector<LDObjectPtr>& candidates); -};
--- a/src/mainWindow.cc Fri Jul 04 00:09:37 2014 +0300 +++ b/src/mainWindow.cc Fri Jul 04 22:19:01 2014 +0300 @@ -50,6 +50,7 @@ #include "configuration.h" #include "ui_ldforge.h" #include "primitives.h" +#include "editmodes/abstracteditmode.h" static bool g_isSelectionLocked = false; static QMap<QAction*, QKeySequence> g_defaultShortcuts; @@ -690,11 +691,11 @@ // void MainWindow::updateEditModeActions() { - const EditMode mode = R()->editMode(); - ui->actionModeSelect->setChecked (mode == ESelectMode); - ui->actionModeDraw->setChecked (mode == EDrawMode); - ui->actionModeCircle->setChecked (mode == ECircleMode); - ui->actionModeMagicWand->setChecked (mode == EMagicWandMode); + const EditModeType mode = R()->currentEditModeType(); + ui->actionModeSelect->setChecked (mode == EditModeType::Select); + ui->actionModeDraw->setChecked (mode == EditModeType::Draw); + ui->actionModeCircle->setChecked (mode == EditModeType::Circle); + ui->actionModeMagicWand->setChecked (mode == EditModeType::MagicWand); } // =============================================================================