Fri, 04 Jul 2014 23:44:44 +0300
- merged with main
--- a/CMakeLists.txt Fri Jul 04 23:43:57 2014 +0300 +++ b/CMakeLists.txt Fri Jul 04 23:44:44 2014 +0300 @@ -39,7 +39,6 @@ src/ldConfig.cc src/ldDocument.cc src/ldObject.cc - src/magicWand.cc src/main.cc src/mainWindow.cc src/messageLog.cc @@ -48,6 +47,11 @@ src/primitives.cc src/radioGroup.cc src/version.cc + src/editmodes/abstracteditmode.cc + src/editmodes/circlemode.cc + src/editmodes/drawmode.cc + src/editmodes/magicwandmode.cc + src/editmodes/selectmode.cc ) set (LDFORGE_HEADERS @@ -76,7 +80,11 @@ src/mainWindow.h src/editHistory.h src/format.h - src/magicWand.h + src/editmodes/abstracteditmode.h + src/editmodes/circlemode.h + src/editmodes/drawmode.h + src/editmodes/magicwandmode.h + src/editmodes/selectmode.h ) set (LDFORGE_FORMS
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/editmodes/abstracteditmode.cc Fri Jul 04 23:44:44 2014 +0300 @@ -0,0 +1,155 @@ +#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(); + _drawedVerts.clear(); +} + +AbstractSelectMode::AbstractSelectMode (GLRenderer* renderer) : + AbstractEditMode (renderer) +{ + renderer->unsetCursor(); + renderer->setContextMenuPolicy (Qt::DefaultContextMenu); +} + +// ============================================================================= +// +void AbstractDrawMode::addDrawnVertex (Vertex const& pos) +{ + if (preAddVertex (pos)) + return; + + _drawedVerts << pos; +} + +bool AbstractDrawMode::mouseReleased (MouseEventData const& data) +{ + if (Super::mouseReleased (data)) + return true; + + if ((data.releasedButtons & Qt::MidButton) && (_drawedVerts.size() < 4) && (not data.mouseMoved)) + { + // 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(); + QVector<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; + } + + if ((data.releasedButtons & Qt::RightButton) && (not _drawedVerts.isEmpty())) + { + // Remove the last vertex + _drawedVerts.removeLast(); + + return true; + } + + return false; +} + +void AbstractDrawMode::finishDraw (LDObjectList& objs) +{ + if (objs.size() > 0) + { + for (LDObjectPtr obj : objs) + { + renderer()->document()->addObject (obj); + renderer()->compileObject (obj); + } + + g_win->refresh(); + g_win->endAction(); + } + + _drawedVerts.clear(); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/editmodes/abstracteditmode.h Fri Jul 04 23:44:44 2014 +0300 @@ -0,0 +1,86 @@ +#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 bool mousePressed (QMouseEvent*) { return false; } + virtual bool mouseReleased (MouseEventData const&) { return false; } + virtual bool mouseDoubleClicked (QMouseEvent*) { return false; } + virtual bool mouseMoved (QMouseEvent*) { return false; } + void finishDraw (LDObjectList& objs); + + static AbstractEditMode* createByType (GLRenderer* renderer, EditModeType type); +}; + +// +// Base class for draw-like edit modes +// +class AbstractDrawMode : public AbstractEditMode +{ + QList<Vertex> _drawedVerts; + Vertex m_rectverts[4]; + + DEFINE_CLASS (AbstractDrawMode, AbstractEditMode) + +public: + AbstractDrawMode (GLRenderer* renderer); + + virtual bool allowFreeCamera() const override + { + return false; + } + + bool mouseReleased (const AbstractEditMode::MouseEventData& 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 +{ + DEFINE_CLASS (AbstractSelectMode, 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 23:44:44 2014 +0300 @@ -0,0 +1,256 @@ +#include <QPainter> +#include "circlemode.h" +#include "../miscallenous.h" +#include "../ldObject.h" +#include "../ldDocument.h" +#include "../misc/ringFinder.h" +#include "../primitives.h" + +CircleMode::CircleMode (GLRenderer* renderer) : + Super (renderer) {} + +double CircleMode::getCircleDrawDist (int pos) const +{ + assert (_drawedVerts.size() >= pos + 1); + Vertex v1 = (_drawedVerts.size() >= pos + 2) ? _drawedVerts[pos + 1] : + renderer()->coordconv2_3 (renderer()->mousePosition(), false); + Axis localx, localy; + renderer()->getRelativeAxes (localx, localy); + double dx = _drawedVerts[0][localx] - v1[localx]; + double dy = _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::buildCircle() +{ + LDObjectList objs; + const int segs = g_lores, divs = g_lores; // TODO: make customizable + double dist0 = getCircleDrawDist (0), + dist1 = getCircleDrawDist (1); + LDDocumentPtr refFile; + Matrix transform; + bool circleOrDisc = false; + + if (dist1 < dist0) + std::swap<double> (dist0, dist1); + + if (dist0 == dist1) + { + // If the radii are the same, there's no ring space to fill. Use a circle. + refFile = getDocument ("4-4edge.dat"); + transform = getCircleDrawMatrix (dist0); + circleOrDisc = true; + } + elif (dist0 == 0 || dist1 == 0) + { + // If either radii is 0, use a disc. + refFile = getDocument ("4-4disc.dat"); + transform = getCircleDrawMatrix ((dist0 != 0) ? dist0 : dist1); + circleOrDisc = true; + } + elif (g_RingFinder.findRings (dist0, dist1)) + { + // The ring finder found a solution, use that. Add the component rings to the file. + for (const RingFinder::Component& cmp : g_RingFinder.bestSolution()->getComponents()) + { + // Get a ref file for this primitive. If we cannot find it in the + // LDraw library, generate it. + if ((refFile = ::getDocument (radialFileName (::Ring, g_lores, g_lores, cmp.num))) == null) + { + refFile = generatePrimitive (::Ring, g_lores, g_lores, cmp.num); + refFile->setImplicit (false); + } + + LDSubfilePtr ref = spawn<LDSubfile>(); + ref->setFileInfo (refFile); + ref->setTransform (getCircleDrawMatrix (cmp.scale)); + ref->setPosition (_drawedVerts[0]); + ref->setColor (maincolor()); + objs << ref; + } + } + else + { + // Ring finder failed, last resort: draw the ring with quads + QList<QLineF> c0, c1; + Axis localx, localy, localz; + renderer()->getRelativeAxes (localx, localy); + localz = (Axis) (3 - localx - localy); + double x0 = _drawedVerts[0][localx], + y0 = _drawedVerts[0][localy]; + + Vertex templ; + templ.setCoordinate (localx, x0); + templ.setCoordinate (localy, y0); + templ.setCoordinate (localz, renderer()->getDepthValue()); + + // Calculate circle coords + makeCircle (segs, divs, dist0, c0); + makeCircle (segs, divs, dist1, c1); + + for (int i = 0; i < segs; ++i) + { + Vertex v0, v1, v2, v3; + v0 = v1 = v2 = v3 = templ; + v0.setCoordinate (localx, v0[localx] + c0[i].x1()); + v0.setCoordinate (localy, v0[localy] + c0[i].y1()); + v1.setCoordinate (localx, v1[localx] + c0[i].x2()); + v1.setCoordinate (localy, v1[localy] + c0[i].y2()); + v2.setCoordinate (localx, v2[localx] + c1[i].x2()); + v2.setCoordinate (localy, v2[localy] + c1[i].y2()); + v3.setCoordinate (localx, v3[localx] + c1[i].x1()); + v3.setCoordinate (localy, v3[localy] + c1[i].y1()); + + LDQuadPtr quad (spawn<LDQuad> (v0, v1, v2, v3)); + quad->setColor (maincolor()); + + // Ensure the quads always are BFC-front towards the camera + if (renderer()->camera() % 3 <= 0) + quad->invert(); + + objs << quad; + } + } + + if (circleOrDisc && refFile != null) + { + LDSubfilePtr ref = spawn<LDSubfile>(); + ref->setFileInfo (refFile); + ref->setTransform (transform); + ref->setPosition (_drawedVerts[0]); + ref->setColor (maincolor()); + objs << ref; + } + + finishDraw (objs); +} + +void CircleMode::render (QPainter& painter) const +{ + QFontMetrics const metrics (QFont()); + + // If we have not specified the center point of the circle yet, preview it on the screen. + if (_drawedVerts.isEmpty()) + { + renderer()->drawBlip (painter, renderer()->coordconv3_2 (renderer()->position3D())); + } + else + { + QVector<Vertex> verts, verts2; + const double dist0 = getCircleDrawDist (0), + dist1 = (_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, _drawedVerts[0][relX] + (cos (i * angleUnit) * dist0)); + v.setCoordinate (relY, _drawedVerts[0][relY] + (sin (i * angleUnit) * dist0)); + verts << v; + + if (dist1 != -1) + { + v.setCoordinate (relX, _drawedVerts[0][relX] + (cos (i * angleUnit) * dist1)); + v.setCoordinate (relY, _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 ((_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 (_drawedVerts[0]); + QString label = QString::number (dist0); + painter.setPen (renderer()->getTextPen()); + painter.drawText (origin.x() - (metrics.width (label) / 2), origin.y(), label); + + if (_drawedVerts.size() >= 2) + { + painter.drawText (origin.x() - (metrics.width (label) / 2), + origin.y() + metrics.height(), QString::number (dist1)); + } + } +} + +bool CircleMode::mouseReleased (MouseEventData const& data) +{ + if (Super::mouseReleased (data)) + return true; + + if (_drawedVerts.size() < 3) + { + addDrawnVertex (renderer()->position3D()); + return; + } + + buildCircle(); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/editmodes/circlemode.h Fri Jul 04 23:44:44 2014 +0300 @@ -0,0 +1,19 @@ +#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); + bool mouseReleased (const AbstractEditMode::MouseEventData& data) override; + +private: + void buildCircle(); +};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/editmodes/drawmode.cc Fri Jul 04 23:44:44 2014 +0300 @@ -0,0 +1,260 @@ +#include <QPainter> +#include "drawmode.h" +#include "../ldObject.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 = _drawedVerts.size() + 1; + int i = 0; + + for (Vertex& vert : _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 (_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 = (_drawedVerts.size() == 1) ? 1 : _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 : _drawedVerts) + { + if (vert == pos) + { + endDraw (true); + return true; + } + } + + return false; +} + +bool DrawMode::mouseReleased (MouseEventData const& data) +{ + if (Super::mouseReleased (data)) + return true; + + if (_rectdraw) + { + if (_drawedVerts.size() == 2) + { + endDraw (true); + return true; + } + } + else + { + // If we have 4 verts, stop drawing. + if (_drawedVerts.size() >= 4) + { + endDraw (true); + return; + } + + if (_drawedVerts.isEmpty()) + { + _rectdraw = (ev->modifiers() & Qt::ShiftModifier); + updateRectVerts(); + } + } + + addDrawnVertex (renderer()->position3D()); + return true; +} + +// +// Update rect vertices when the mouse moves since the 3d position likely has changed +// +bool DrawMode::mouseMoved (QMouseEvent*) +{ + updateRectVerts(); +} + +void DrawMode::updateRectVerts() +{ + if (not _rectdraw) + return; + + if (_drawedVerts.isEmpty()) + { + for (int i = 0; i < 4; ++i) + m_rectverts[i] = renderer()->position3D(); + + return; + } + + Vertex v0 = _drawedVerts[0], + v1 = (_drawedVerts.size() >= 2) ? _drawedVerts[1] : renderer()->position3D(); + + const Axis localx = renderer()->getCameraAxis (false), + localy = renderer()->getCameraAxis (true), + localz = (Axis) (3 - localx - localy); + + for (int i = 0; i < 4; ++i) + m_rectverts[i].setCoordinate (localz, renderer()->getDepthValue()); + + m_rectverts[0].setCoordinate (localx, v0[localx]); + m_rectverts[0].setCoordinate (localy, v0[localy]); + m_rectverts[1].setCoordinate (localx, v1[localx]); + m_rectverts[1].setCoordinate (localy, v0[localy]); + m_rectverts[2].setCoordinate (localx, v1[localx]); + m_rectverts[2].setCoordinate (localy, v1[localy]); + m_rectverts[3].setCoordinate (localx, v0[localx]); + m_rectverts[3].setCoordinate (localy, v1[localy]); +} + +void DrawMode::endDraw() +{ + // Clean the selection and create the object + QList<Vertex>& verts = _drawedVerts; + LDObjectList objs; + + if (_rectdraw) + { + LDQuadPtr quad (spawn<LDQuad>()); + + updateRectVerts(); + + for (int i = 0; i < quad->numVertices(); ++i) + quad->setVertex (i, m_rectverts[i]); + + quad->setColor (maincolor()); + objs << quad; + } + else + { + switch (verts.size()) + { + case 1: + { + // 1 vertex - add a vertex object + LDVertexPtr obj = spawn<LDVertex>(); + obj->pos = verts[0]; + obj->setColor (maincolor()); + objs << obj; + break; + } + + case 2: + { + // 2 verts - make a line + LDLinePtr obj = spawn<LDLine> (verts[0], verts[1]); + obj->setColor (edgecolor()); + objs << obj; + break; + } + + case 3: + case 4: + { + LDObjectPtr obj = (verts.size() == 3) ? + static_cast<LDObjectPtr> (spawn<LDTriangle>()) : + static_cast<LDObjectPtr> (spawn<LDQuad>()); + + obj->setColor (maincolor()); + + for (int i = 0; i < verts.size(); ++i) + obj->setVertex (i, verts[i]); + + objs << obj; + break; + } + } + } + + finishDraw (objs); + _rectdraw = false; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/editmodes/drawmode.h Fri Jul 04 23:44:44 2014 +0300 @@ -0,0 +1,19 @@ +#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 bool mouseReleased (MouseEventData const& data) override; + virtual bool mouseMoved (QMouseEvent*) override; + +private: + void updateRectVerts(); +};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/editmodes/magicwandmode.cc Fri Jul 04 23:44:44 2014 +0300 @@ -0,0 +1,214 @@ +/* + * 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(); +} + +bool MagicWandMode::mouseReleased (MouseEventData const& data) +{ + if (Super::mouseReleased (data)) + return true; + + 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); + return true; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/editmodes/magicwandmode.h Fri Jul 04 23:44:44 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 bool 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 23:44:44 2014 +0300 @@ -0,0 +1,66 @@ +#include <QMouseEvent> +#include "../glRenderer.h" +#include "selectmode.h" + +SelectMode::SelectMode (GLRenderer* renderer) : + Super (renderer) {} + +EditModeType SelectMode::type() const +{ + return EditModeType::Select; +} + +bool SelectMode::mouseReleased (MouseEventData const& data) +{ + if (Super::mouseReleased (data)) + return true; + + 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()); + + _rangepick = false; +} + +bool SelectMode::mousePressed (QMouseEvent* ev) +{ + if (Super::mousePressed (ev)) + return true; + + if (ev->modifiers() & Qt::ControlModifier) + { + _rangepick = true; + _rangeStart.setX (ev->x()); + _rangeStart.setY (ev->y()); + _addpick = (ev->modifiers() & Qt::AltModifier); + return true; + } + + return false; +} + +bool SelectMode::mouseDoubleClicked (QMouseEvent* ev) +{ + if (Super::mouseDoubleClicked (ev)) + return true; + + if (ev->buttons() & Qt::LeftButton) + { + renderer()->document()->clearSelection(); + LDObjectPtr obj = renderer()->pickOneObject (ev->x(), ev->y()); + + if (obj != null) + { + AddObjectDialog::staticDialog (obj->type(), obj); + g_win->endAction(); + return true; + } + } + + return false; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/editmodes/selectmode.h Fri Jul 04 23:44:44 2014 +0300 @@ -0,0 +1,17 @@ +#include "abstracteditmode.h" + +class SelectMode : public AbstractSelectMode +{ + QPoint _rangeStart; + bool _rangepick; + bool _addpick; + + DEFINE_CLASS (SelectMode, AbstractSelectMode) + +public: + SelectMode (GLRenderer* renderer); + + virtual bool mouseReleased (MouseEventData const& data) override; + virtual bool mouseDoubleClicked (QMouseEvent* ev); + virtual EditModeType type() const override; +}; \ No newline at end of file
--- a/src/glRenderer.cc Fri Jul 04 23:43:57 2014 +0300 +++ b/src/glRenderer.cc Fri Jul 04 23:44:44 2014 +0300 @@ -25,10 +25,9 @@ #include <QContextMenuEvent> #include <QInputDialog> #include <QToolTip> -#include <qtextdocument.h> +#include <QTextDocument> #include <QTimer> #include <GL/glu.h> - #include "main.h" #include "configuration.h" #include "ldDocument.h" @@ -40,10 +39,7 @@ #include "dialogs.h" #include "addObjectDialog.h" #include "messageLog.h" -#include "primitives.h" -#include "misc/ringFinder.h" #include "glCompiler.h" -#include "magicWand.h" static const LDFixedCameraInfo g_FixedCameras[6] = { @@ -55,15 +51,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 +62,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 +101,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 +117,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 +506,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 +543,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 +566,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 +589,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 +603,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 +628,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 +661,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 +683,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 +737,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,143 +763,17 @@ } } - switch (editMode()) + if (not isDrawOnly()) { - 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; - } - - case ECircleMode: - { - if (m_drawedVerts.size() == 3) - { - endDraw (true); - return; - } - - 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; + AbstractEditMode::MouseEventData data; + data.ev = ev; + data.mouseMoved = m_totalmove >= 10; + data.keymods = m_keymods; + data.releasedButtons = releasedbuttons; - 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; - } + if (m_editmode->mouseReleased (data)) + goto end; } - - m_rangepick = false; - } - - 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()) - { - // Remove the last vertex - m_drawedVerts.removeLast(); - - if (m_drawedVerts.isEmpty()) - m_rectdraw = false; } end: @@ -1120,22 +786,14 @@ void GLRenderer::mousePressEvent (QMouseEvent* ev) { m_totalmove = 0; + m_lastButtons = ev->buttons(); - if (ev->modifiers() & Qt::ControlModifier) - { - m_rangepick = true; - m_rangeStart.setX (ev->x()); - m_rangeStart.setY (ev->y()); - m_addpick = (m_keymods & Qt::AltModifier); + if (m_editmode->mousePressed (ev)) ev->accept(); - } - - m_lastButtons = ev->buttons(); } // ============================================================================= -// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -// ============================================================================= +// void GLRenderer::mouseMoveEvent (QMouseEvent* ev) { int dx = ev->x() - m_mousePosition.x(); @@ -1175,10 +833,10 @@ // Calculate 3d position of the cursor m_position3D = (camera() != EFreeCamera) ? coordconv2_3 (m_mousePosition, true) : g_origin; - // Update rect vertices since m_position3D may have changed - updateRectVerts(); + m_editmode->mouseMoved (ev); highlightCursorObject(); update(); + ev->accept(); } // ============================================================================= @@ -1229,6 +887,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 +1030,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 +1048,13 @@ // ============================================================================= // +EditModeType GLRenderer::currentEditModeType() const +{ + return m_editmode->type(); +} + +// ============================================================================= +// void GLRenderer::setDocument (LDDocumentPtr const& a) { m_document = a; @@ -1467,231 +1098,9 @@ // ============================================================================= // -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; - - // Clean the selection and create the object - QList<Vertex>& verts = m_drawedVerts; - LDObjectList objs; - - switch (editMode()) - { - case EDrawMode: - { - if (m_rectdraw) - { - LDQuadPtr quad (spawn<LDQuad>()); - - // Copy the vertices from m_rectverts - updateRectVerts(); - - for (int i = 0; i < quad->numVertices(); ++i) - quad->setVertex (i, m_rectverts[i]); - - quad->setColor (maincolor()); - objs << quad; - } - else - { - switch (verts.size()) - { - case 1: - { - // 1 vertex - add a vertex object - LDVertexPtr obj = spawn<LDVertex>(); - obj->pos = verts[0]; - obj->setColor (maincolor()); - objs << obj; - } break; - - case 2: - { - // 2 verts - make a line - LDLinePtr obj = spawn<LDLine> (verts[0], verts[1]); - obj->setColor (edgecolor()); - objs << obj; - } break; - - case 3: - case 4: - { - LDObjectPtr obj = (verts.size() == 3) ? - static_cast<LDObjectPtr> (spawn<LDTriangle>()) : - static_cast<LDObjectPtr> (spawn<LDQuad>()); - - obj->setColor (maincolor()); - - for (int i = 0; i < obj->numVertices(); ++i) - obj->setVertex (i, verts[i]); - - objs << obj; - } break; - } - } - } break; - - case ECircleMode: - { - const int segs = g_lores, divs = g_lores; // TODO: make customizable - double dist0 = getCircleDrawDist (0), - dist1 = getCircleDrawDist (1); - LDDocumentPtr refFile; - Matrix transform; - bool circleOrDisc = false; - - if (dist1 < dist0) - std::swap<double> (dist0, dist1); - - if (dist0 == dist1) - { - // If the radii are the same, there's no ring space to fill. Use a circle. - refFile = ::getDocument ("4-4edge.dat"); - transform = getCircleDrawMatrix (dist0); - circleOrDisc = true; - } - elif (dist0 == 0 || dist1 == 0) - { - // If either radii is 0, use a disc. - refFile = ::getDocument ("4-4disc.dat"); - transform = getCircleDrawMatrix ((dist0 != 0) ? dist0 : dist1); - circleOrDisc = true; - } - elif (g_RingFinder.findRings (dist0, dist1)) - { - // The ring finder found a solution, use that. Add the component rings to the file. - for (const RingFinder::Component& cmp : g_RingFinder.bestSolution()->getComponents()) - { - // Get a ref file for this primitive. If we cannot find it in the - // LDraw library, generate it. - if ((refFile = ::getDocument (radialFileName (::Ring, g_lores, g_lores, cmp.num))) == null) - { - refFile = generatePrimitive (::Ring, g_lores, g_lores, cmp.num); - refFile->setImplicit (false); - } - - LDSubfilePtr ref = spawn<LDSubfile>(); - ref->setFileInfo (refFile); - ref->setTransform (getCircleDrawMatrix (cmp.scale)); - ref->setPosition (m_drawedVerts[0]); - ref->setColor (maincolor()); - objs << ref; - } - } - else - { - // Ring finder failed, last resort: draw the ring with quads - QList<QLineF> c0, c1; - Axis relX, relY, relZ; - getRelativeAxes (relX, relY); - relZ = (Axis) (3 - relX - relY); - double x0 = m_drawedVerts[0][relX], - y0 = m_drawedVerts[0][relY]; - - Vertex templ; - templ.setCoordinate (relX, x0); - templ.setCoordinate (relY, y0); - templ.setCoordinate (relZ, getDepthValue()); - - // Calculate circle coords - makeCircle (segs, divs, dist0, c0); - makeCircle (segs, divs, dist1, c1); - - for (int i = 0; i < segs; ++i) - { - Vertex v0, v1, v2, v3; - v0 = v1 = v2 = v3 = templ; - v0.setCoordinate (relX, v0[relX] + c0[i].x1()); - v0.setCoordinate (relY, v0[relY] + c0[i].y1()); - v1.setCoordinate (relX, v1[relX] + c0[i].x2()); - v1.setCoordinate (relY, v1[relY] + c0[i].y2()); - v2.setCoordinate (relX, v2[relX] + c1[i].x2()); - v2.setCoordinate (relY, v2[relY] + c1[i].y2()); - v3.setCoordinate (relX, v3[relX] + c1[i].x1()); - v3.setCoordinate (relY, v3[relY] + c1[i].y1()); - - LDQuadPtr quad (spawn<LDQuad> (v0, v1, v2, v3)); - quad->setColor (maincolor()); - - // Ensure the quads always are BFC-front towards the camera - if (camera() % 3 <= 0) - quad->invert(); - - objs << quad; - } - } - - if (circleOrDisc) - { - LDSubfilePtr ref = spawn<LDSubfile>(); - ref->setFileInfo (refFile); - ref->setTransform (transform); - ref->setPosition (m_drawedVerts[0]); - ref->setColor (maincolor()); - objs << ref; - } - } break; - - case ESelectMode: - case EMagicWandMode: - { - // this shouldn't happen - assert (false); - return; - } break; - } - - if (objs.size() > 0) - { - for (LDObjectPtr obj : objs) - { - document()->addObject (obj); - compileObject (obj); - } - - g_win->refresh(); - g_win->endAction(); - } - - m_drawedVerts.clear(); - m_rectdraw = false; -} - -// ============================================================================= -// -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 +1109,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 +1197,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; } @@ -2015,55 +1424,10 @@ // ============================================================================= // -void GLRenderer::updateRectVerts() -{ - if (not m_rectdraw) - return; - - if (m_drawedVerts.isEmpty()) - { - for (int i = 0; i < 4; ++i) - m_rectverts[i] = m_position3D; - - return; - } - - Vertex v0 = m_drawedVerts[0], - v1 = (m_drawedVerts.size() >= 2) ? m_drawedVerts[1] : m_position3D; - - const Axis ax = getCameraAxis (false), - ay = getCameraAxis (true), - az = (Axis) (3 - ax - ay); - - for (int i = 0; i < 4; ++i) - m_rectverts[i].setCoordinate (az, getDepthValue()); - - m_rectverts[0].setCoordinate (ax, v0[ax]); - m_rectverts[0].setCoordinate (ay, v0[ay]); - m_rectverts[1].setCoordinate (ax, v1[ax]); - m_rectverts[1].setCoordinate (ay, v0[ay]); - m_rectverts[2].setCoordinate (ax, v1[ax]); - m_rectverts[2].setCoordinate (ay, v1[ay]); - m_rectverts[3].setCoordinate (ax, v0[ax]); - m_rectverts[3].setCoordinate (ay, v1[ay]); -} - -// ============================================================================= -// void GLRenderer::mouseDoubleClickEvent (QMouseEvent* ev) { - if (not (ev->buttons() & Qt::LeftButton) || editMode() != ESelectMode) - return; - - pick (ev->x(), ev->y()); - - if (selection().isEmpty()) - return; - - LDObjectPtr obj = selection().first(); - AddObjectDialog::staticDialog (obj->type(), obj); - g_win->endAction(); - ev->accept(); + if (m_editmode->mouseDoubleClicked (ev)) + ev->accept(); } // ============================================================================= @@ -2255,3 +1619,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 23:43:57 2014 +0300 +++ b/src/glRenderer.h Fri Jul 04 23:44:44 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,36 @@ 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 +214,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,31 +227,19 @@ 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); LDObjectPtr pickOneObject (int mouseX, int mouseY); inline double& rot (Axis ax); - void updateRectVerts(); inline double& zoom(); void zoomToFit(); void zoomAllToFit();
--- a/src/ldDocument.cc Fri Jul 04 23:43:57 2014 +0300 +++ b/src/ldDocument.cc Fri Jul 04 23:44:44 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 23:43:57 2014 +0300 +++ b/src/macros.h Fri Jul 04 23:44:44 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 23:43:57 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 23:43:57 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 23:43:57 2014 +0300 +++ b/src/mainWindow.cc Fri Jul 04 23:44:44 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); } // =============================================================================