Converted magic wand mode and other selection stuff to mvc

Thu, 22 Feb 2018 11:41:58 +0200

author
Santeri Piippo
date
Thu, 22 Feb 2018 11:41:58 +0200
changeset 1251
e75cc5bff076
parent 1250
e2755ccf3667
child 1252
db1d4917626f

Converted magic wand mode and other selection stuff to mvc

CMakeLists.txt file | annotate | diff | comparison | revisions
src/editmodes/abstractEditMode.cpp file | annotate | diff | comparison | revisions
src/editmodes/magicWandMode.cpp file | annotate | diff | comparison | revisions
src/editmodes/magicWandMode.h file | annotate | diff | comparison | revisions
src/editmodes/selectMode.cpp file | annotate | diff | comparison | revisions
src/geometry/linesegment.cpp file | annotate | diff | comparison | revisions
src/geometry/linesegment.h file | annotate | diff | comparison | revisions
src/glcompiler.cpp file | annotate | diff | comparison | revisions
src/glcompiler.h file | annotate | diff | comparison | revisions
src/glrenderer.cpp file | annotate | diff | comparison | revisions
src/glrenderer.h file | annotate | diff | comparison | revisions
src/hierarchyelement.cpp file | annotate | diff | comparison | revisions
src/hierarchyelement.h file | annotate | diff | comparison | revisions
src/linetypes/conditionaledge.h file | annotate | diff | comparison | revisions
src/linetypes/modelobject.cpp file | annotate | diff | comparison | revisions
src/linetypes/modelobject.h file | annotate | diff | comparison | revisions
src/mainwindow.cpp file | annotate | diff | comparison | revisions
src/mainwindow.h file | annotate | diff | comparison | revisions
src/toolsets/viewtoolset.cpp file | annotate | diff | comparison | revisions
--- a/CMakeLists.txt	Thu Feb 15 14:59:04 2018 +0200
+++ b/CMakeLists.txt	Thu Feb 22 11:41:58 2018 +0200
@@ -70,6 +70,7 @@
 	src/editmodes/magicWandMode.cpp
 	src/editmodes/rectangleMode.cpp
 	src/editmodes/selectMode.cpp
+	src/geometry/linesegment.cpp
 	src/linetypes/comment.cpp
 	src/linetypes/conditionaledge.cpp
 	src/linetypes/edgeline.cpp
@@ -136,6 +137,7 @@
 	src/editmodes/rectangleMode.h
 	src/editmodes/selectMode.h
 	src/generics/reverse.h
+	src/geometry/linesegment.h
 	src/linetypes/comment.h
 	src/linetypes/conditionaledge.h
 	src/linetypes/edgeline.h
--- a/src/editmodes/abstractEditMode.cpp	Thu Feb 15 14:59:04 2018 +0200
+++ b/src/editmodes/abstractEditMode.cpp	Thu Feb 22 11:41:58 2018 +0200
@@ -76,8 +76,7 @@
 {
 	canvas->setContextMenuPolicy(Qt::NoContextMenu); // We need the right mouse button for removing vertices
 	canvas->setCursor(Qt::CrossCursor);
-	m_window->currentDocument()->clearSelection();
-	m_window->updateSelection();
+	canvas->selectionModel()->clear();
 	m_drawedVerts.clear();
 }
 
--- a/src/editmodes/magicWandMode.cpp	Thu Feb 15 14:59:04 2018 +0200
+++ b/src/editmodes/magicWandMode.cpp	Thu Feb 22 11:41:58 2018 +0200
@@ -22,17 +22,36 @@
 #include "../mainwindow.h"
 #include "../canvas.h"
 
-MagicWandMode::MagicWandMode (Canvas* canvas) :
-    Super (canvas)
+MagicWandMode::MagicWandMode(Canvas* canvas) :
+	Super {canvas}
 {
+	QSet<LineSegment> boundarySegments;
+
 	// Get vertex<->object data
-	for (LDObject* obj : currentDocument()->objects())
+	for (const QModelIndex& index : currentDocument()->indices())
 	{
 		// Note: this deliberately only takes vertex-objects into account.
 		// The magic wand does not process subparts.
-		for (int i = 0; i < obj->numVertices(); ++i)
-			m_vertices[obj->vertex (i)] << obj;
+		LDObject* object = currentDocument()->lookup(index);
+
+		for (int i = 0; i < object->numVertices(); ++i)
+		{
+			LineSegment segment {
+				object->vertex(i),
+				object->vertex((i + 1) % object->numVertices())
+			};
+			m_vertices[object->vertex(i)].insert(index);
+
+			if (object->type() == LDObjectType::EdgeLine)
+				boundarySegments.insert(segment);
+			else
+				this->segments[segment].insert(index);
+		}
 	}
+
+	// Remove all edge lines from the set of available segments because they get in the way.
+	for (const LineSegment& boundarySegment : boundarySegments)
+		this->segments.remove(boundarySegment);
 }
 
 EditModeType MagicWandMode::type() const
@@ -40,157 +59,82 @@
 	return EditModeType::MagicWand;
 }
 
-void MagicWandMode::fillBoundaries (LDObject* obj, QVector<BoundaryType>& boundaries, QSet<LDObject*>& candidates)
-{
-	// All boundaries obviously share vertices with the object, therefore they're all in the list
-	// of candidates.
-	for (LDObject* candidate : candidates)
+void MagicWandMode::edgeFill(
+	QModelIndex index,
+	QItemSelection& selection,
+	QSet<QModelIndex>& processed
+) const {
+	QItemSelection result;
+	processed.insert(index);
+	result.select(index, index);
+	QSet<QPersistentModelIndex> candidates;
+	LDObject* object = currentDocument()->lookup(index);
+
+	// Get the list of objects that touch this object, i.e. share a vertex with it.
+	for (int i = 0; i < object->numVertices(); ++i)
+		candidates |= m_vertices[object->vertex(i)];
+
+	candidates.remove(index);
+
+	for (const QModelIndex& candidate : candidates)
 	{
-		if (not isOneOf(candidate->type(), LDObjectType::EdgeLine, LDObjectType::ConditionalEdge)
-			or candidate->vertex (0) == candidate->vertex (1))
-		{
-			continue;
-		}
+		LDObject* candidateObject = currentDocument()->lookup(candidate);
 
-		int matches = 0;
+		if (candidateObject->type() == LDObjectType::EdgeLine
+			and candidateObject->color() == object->color()
+		) {
+			selection.select(candidate, candidate);
 
-		for (int i = 0; i < obj->numVertices(); ++i)
+			if (not processed.contains(candidate))
+				edgeFill(candidate, selection, processed);
+		}
+	}
+}
+
+void MagicWandMode::surfaceFill(
+	QModelIndex index,
+	QItemSelection& selection,
+	QSet<QModelIndex>& processed
+) const {
+	LDObject* object = currentDocument()->lookup(index);
+	selection.select(index, index);
+	processed.insert(index);
+
+	for (int i = 0; i < object->numVertices(); i += 1)
+	{
+		Vertex v_1 = object->vertex(i);
+		Vertex v_2 = object->vertex((i + 1) % object->numVertices());
+		LineSegment segment {v_1, v_2};
+
+		for (const QModelIndex& candidate : this->segments[segment])
 		{
-			if (not isOneOf (obj->vertex (i), candidate->vertex (0), candidate->vertex (1)))
-				continue;
-
-			if (++matches == 2)
+			if (currentDocument()->lookup(candidate)->color() == object->color())
 			{
-				// Boundary found. If it's an edgeline, add it to the boundaries list, if a
-				// conditional line, select it.
-				if (candidate->type() == LDObjectType::ConditionalEdge)
-					m_selection << candidate;
-				else
-					boundaries.append (std::make_tuple (candidate->vertex (0), candidate->vertex (1)));
+				selection.select(candidate, candidate);
 
-				break;
+				if (not processed.contains(candidate))
+					surfaceFill(candidate, selection, processed);
 			}
 		}
 	}
 }
 
-void MagicWandMode::doMagic (LDObject* obj, MagicWandMode::MagicType type)
+QItemSelection MagicWandMode::doMagic(const QModelIndex& index) const
 {
-	if (obj == nullptr)
-	{
-		if (type == Set)
-			currentDocument()->clearSelection();
+	QItemSelection selection;
+	LDObject* object = currentDocument()->lookup(index);
 
-		return;
-	}
-
-	int matchesneeded = 0;
-	QVector<BoundaryType> boundaries;
-	LDObjectType objtype = obj->type();
-
-	if (type != InternalRecursion)
+	if (object)
 	{
-		m_selection.clear();
-		m_selection.append (obj);
-	}
+		QSet<QModelIndex> processed;
 
-	switch (obj->type())
-	{
-		case LDObjectType::EdgeLine:
-		case LDObjectType::ConditionalEdge:
-			matchesneeded = 1;
-			break;
-
-		case LDObjectType::Triangle:
-		case LDObjectType::Quadrilateral:
-			matchesneeded = 2;
-			break;
-
-		default:
-			return;
+		if (object->type() == LDObjectType::EdgeLine)
+			edgeFill(index, selection, processed);
+		else if (object->numPolygonVertices() >= 3)
+			surfaceFill(index, selection, processed);
 	}
 
-	QSet<LDObject*> 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 += m_vertices[obj->vertex (i)];
-
-	// If we're dealing with surfaces, get a list of boundaries.
-	if (matchesneeded > 1)
-		fillBoundaries (obj, boundaries, candidates);
-
-	for (LDObject* 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) or
-				(candidate->color() != obj->color()) or
-				(m_selection.contains (candidate)) or
-				(matchesneeded == 1 and (candidate->type() != objtype)) or
-				((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 (countof(matches) < matchesneeded)
-				throw 0; // Not enough matches.
-
-			// Check if a boundary gets in between the objects.
-			for (auto boundary : boundaries)
-			{
-				if (isOneOf (matches[0], std::get<0> (boundary), std::get<1> (boundary)) and
-					isOneOf (matches[1], std::get<0> (boundary), std::get<1> (boundary)))
-				{
-					throw 0;
-				}
-			}
-
-			m_selection.append (candidate);
-			doMagic (candidate, InternalRecursion);
-		}
-		catch (int&)
-		{
-			continue;
-		}
-	}
-
-	switch (type)
-	{
-	case Set:
-		currentDocument()->clearSelection();
-	case Additive:
-		for (LDObject* obj : m_selection)
-			currentDocument()->addToSelection(obj);
-		break;
-
-	case Subtractive:
-		for (LDObject* obj : m_selection)
-			currentDocument()->removeFromSelection(obj);
-		break;
-
-	case InternalRecursion:
-		break;
-	}
+	return selection;
 }
 
 bool MagicWandMode::mouseReleased (MouseEventData const& data)
@@ -200,14 +144,15 @@
 
 	if (data.releasedButtons & Qt::LeftButton and not data.mouseMoved)
 	{
-		MagicType wandtype = MagicWandMode::Set;
+		QItemSelection selection = this->doMagic(renderer()->pick(data.ev->x(), data.ev->y()));
+		QItemSelectionModel::SelectionFlags command = QItemSelectionModel::ClearAndSelect;
 
 		if (data.keymods & Qt::ShiftModifier)
-			wandtype = MagicWandMode::Additive;
+			command = QItemSelectionModel::Select;
 		else if (data.keymods & Qt::ControlModifier)
-			wandtype = MagicWandMode::Subtractive;
+			command = QItemSelectionModel::Deselect;
 
-		doMagic (renderer()->pick (data.ev->x(), data.ev->y()), wandtype);
+		renderer()->selectionModel()->select(selection, command);
 		return true;
 	}
 
--- a/src/editmodes/magicWandMode.h	Thu Feb 15 14:59:04 2018 +0200
+++ b/src/editmodes/magicWandMode.h	Thu Feb 22 11:41:58 2018 +0200
@@ -19,31 +19,32 @@
 #pragma once
 #include "abstractEditMode.h"
 #include "../basics.h"
+#include "../geometry/linesegment.h"
 #include <QMap>
 #include <QVector>
 
 class MagicWandMode : public AbstractSelectMode
 {
-	QMap<Vertex, QSet<LDObject*>> m_vertices;
-	QVector<LDObject*> m_selection;
+	QMap<Vertex, QSet<QPersistentModelIndex>> m_vertices;
+	QMap<LineSegment, QSet<QPersistentModelIndex>> segments;
+	QItemSelection m_selection;
 
 	DEFINE_CLASS (MagicWandMode, AbstractSelectMode)
 
 public:
-	using BoundaryType = std::tuple<Vertex, Vertex>;
-	enum MagicType
-	{
-		Set,
-		Additive,
-		Subtractive,
-		InternalRecursion
-	};
+	MagicWandMode(Canvas* canvas);
 
-	MagicWandMode (Canvas* canvas);
-	void doMagic (LDObject* obj, MagicType type);
+	QItemSelection doMagic(const QModelIndex& index) const;
 	virtual EditModeType type() const override;
-	virtual bool mouseReleased (MouseEventData const& data) override;
+	virtual bool mouseReleased(MouseEventData const& data) override;
 
 private:
-	void fillBoundaries (LDObject* obj, QVector<BoundaryType>& boundaries, QSet<LDObject *> &candidates);
+	void edgeFill(
+		QModelIndex index,
+		QItemSelection& selection,
+		QSet<QModelIndex>& processed) const;
+	void surfaceFill(
+		QModelIndex index,
+		QItemSelection& selection,
+		QSet<QModelIndex>& processed) const;
 };
--- a/src/editmodes/selectMode.cpp	Thu Feb 15 14:59:04 2018 +0200
+++ b/src/editmodes/selectMode.cpp	Thu Feb 22 11:41:58 2018 +0200
@@ -94,25 +94,15 @@
 
 void SelectMode::doSelection(const QRect& area)
 {
-	QSet<LDObject*> priorSelection = selectedObjects();
-	QSet<LDObject*> newSelection = renderer()->pick(area);
-
-	// If we're doing an additive pick, use a symmetric difference to keep the existing ones, and filter out objects that were selected
-	// once over again.
-	if (m_addpick)
-		newSelection = (newSelection - priorSelection) | (priorSelection - newSelection);
-
-	newSelection.unite(renderer()->pick(area));
+	QItemSelectionModel* model = renderer()->selectionModel();
+	QItemSelectionModel::SelectionFlags mode;
 
-	// Select all objects that we now have selected that were not selected before.
-	for (LDObject* object : newSelection - priorSelection)
-		currentDocument()->addToSelection(object);
+	if (m_addpick)
+		mode = QItemSelectionModel::Toggle;
+	else
+		mode = QItemSelectionModel::ClearAndSelect;
 
-	// Likewise, deselect whatever was selected that isn't anymore.
-	for (LDObject* object : priorSelection - newSelection)
-		currentDocument()->removeFromSelection(object);
-
-	m_window->updateSelection();
+	model->select(renderer()->pick(area), mode);
 }
 
 bool SelectMode::mousePressed (QMouseEvent* ev)
@@ -140,9 +130,9 @@
 	if (ev->buttons() & Qt::LeftButton)
 	{
 		currentDocument()->clearSelection();
-		LDObject* obj = renderer()->pick (ev->x(), ev->y());
+		QModelIndex index = renderer()->pick(ev->x(), ev->y());
 
-		if (obj)
+		if (index.isValid())
 		{
 			// TODO:
 			m_window->endAction();
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/geometry/linesegment.cpp	Thu Feb 22 11:41:58 2018 +0200
@@ -0,0 +1,65 @@
+/*
+ *  LDForge: LDraw parts authoring CAD
+ *  Copyright (C) 2013 - 2017 Teemu Piippo
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "linesegment.h"
+
+/*
+ * Returns the vertices of this line segment as a QPair.
+ */
+QPair<Vertex, Vertex> LineSegment::toPair() const
+{
+	return {this->v_1, this->v_2};
+}
+
+/*
+ * Possibly swaps the vertices of given line segment so that equivalent line segments become equal.
+ */
+LineSegment normalized(const LineSegment& segment)
+{
+	LineSegment result = segment;
+
+	if (result.v_2 < result.v_1)
+		qSwap(result.v_1, result.v_2);
+
+	return result;
+}
+
+/*
+ * Overload of qHash for line segments.
+ */
+unsigned int qHash(const LineSegment& segment)
+{
+	return qHash(normalized(segment).toPair());
+}
+
+/*
+ * Comparison operator definition to allow line segments to be used in QSets.
+ */
+bool operator<(const LineSegment& one, const LineSegment& other)
+{
+	return normalized(one).toPair() < normalized(other).toPair();
+}
+
+/*
+ * Checks whether two line segments are equal.
+ */
+bool operator==(const LineSegment& one, const LineSegment& other)
+{
+	return (one.v_1 == other.v_1 and one.v_2 == other.v_2)
+		or (one.v_2 == other.v_1 and one.v_1 == other.v_2);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/geometry/linesegment.h	Thu Feb 22 11:41:58 2018 +0200
@@ -0,0 +1,36 @@
+/*
+ *  LDForge: LDraw parts authoring CAD
+ *  Copyright (C) 2013 - 2017 Teemu Piippo
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+#include "../basics.h"
+
+/*
+ * Models a 3D line segment.
+ */
+struct LineSegment
+{
+	Vertex v_1;
+	Vertex v_2;
+
+	QPair<Vertex, Vertex> toPair() const;
+};
+
+LineSegment normalized(const LineSegment& segment);
+unsigned int qHash(const LineSegment& segment);
+bool operator==(const LineSegment& one, const LineSegment& other);
+bool operator<(const LineSegment& one, const LineSegment& other);
--- a/src/glcompiler.cpp	Thu Feb 15 14:59:04 2018 +0200
+++ b/src/glcompiler.cpp	Thu Feb 22 11:41:58 2018 +0200
@@ -209,7 +209,7 @@
 		// We may wish to apply blending on the color to indicate selection or highlight.
 		double blendAlpha = 0.0;
 
-		if (this->selectionModel and this->selectionModel->isSelected(polygonOwnerIndex))
+		if (this->_selectionModel and this->_selectionModel->isSelected(polygonOwnerIndex))
 			blendAlpha = 1.0;
 		else if (polygonOwnerIndex == m_renderer->objectAtCursor())
 			blendAlpha = 0.5;
@@ -538,23 +538,28 @@
 	m_renderer->update();
 }
 
+QItemSelectionModel* GLCompiler::selectionModel() const
+{
+	return _selectionModel;
+}
+
 void GLCompiler::setSelectionModel(QItemSelectionModel* selectionModel)
 {
-	if (this->selectionModel)
-		disconnect(this->selectionModel, 0, 0, 0);
+	if (this->_selectionModel)
+		disconnect(this->_selectionModel, 0, 0, 0);
 
-	this->selectionModel = selectionModel;
+	this->_selectionModel = selectionModel;
 
-	if (this->selectionModel)
+	if (this->_selectionModel)
 	{
 		connect(
-			this->selectionModel,
+			this->_selectionModel,
 			SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)),
 			this,
 			SLOT(selectionChanged(const QItemSelection&, const QItemSelection&))
 		);
 		connect(
-			this->selectionModel,
+			this->_selectionModel,
 			SIGNAL(destroyed(QObject*)),
 			this,
 			SLOT(clearSelectionModel())
--- a/src/glcompiler.h	Thu Feb 15 14:59:04 2018 +0200
+++ b/src/glcompiler.h	Thu Feb 22 11:41:58 2018 +0200
@@ -38,7 +38,8 @@
 	void prepareVBO (int vbonum);
 	GLuint vbo (int vbonum) const;
 	int vboSize (int vbonum) const;
-	void setSelectionModel(QItemSelectionModel* selectionModel);
+	QItemSelectionModel* selectionModel() const;
+	void setSelectionModel(QItemSelectionModel* _selectionModel);
 
 	static int vboNumber (VboClass surface, VboSubclass complement);
 
@@ -77,7 +78,7 @@
 	bool m_vboChanged[NumVbos] = {true};
 	int m_vboSizes[NumVbos] = {0};
 	GLRenderer* m_renderer;
-	QItemSelectionModel* selectionModel = nullptr;
+	QItemSelectionModel* _selectionModel = nullptr;
 
 private slots:
 	void handleRowInsertion(const QModelIndex&, int first, int last);
--- a/src/glrenderer.cpp	Thu Feb 15 14:59:04 2018 +0200
+++ b/src/glrenderer.cpp	Thu Feb 22 11:41:58 2018 +0200
@@ -703,10 +703,10 @@
 /*
  * Returns the set of objects found in the specified pixel area.
  */
-QSet<LDObject*> GLRenderer::pick(const QRect& range)
+QItemSelection GLRenderer::pick(const QRect& range)
 {
 	makeCurrent();
-	QSet<LDObject*> newSelection;
+	QItemSelection result;
 
 	// Paint the picking scene
 	setPicking(true);
@@ -733,46 +733,46 @@
 	// Read pixels from the color buffer.
 	glReadPixels(x0, height() - y1, areawidth, areaheight, GL_RGBA, GL_UNSIGNED_BYTE, pixelData.data());
 
-	QSet<qint32> indices;
+	QSet<qint32> ids;
 
 	// Go through each pixel read and add them to the selection.
 	// Each pixel maps to an LDObject index injectively.
 	// Note: black is background, those indices are skipped.
 	for (unsigned char *pixelCursor = pixelData.begin(); pixelCursor < pixelData.end(); pixelCursor += 4)
 	{
-		qint32 index = pixelCursor[0] * 0x10000 + pixelCursor[1] * 0x100 + pixelCursor[2] * 0x1;
-		if (index != 0)
-			indices.insert(index);
+		qint32 id = pixelCursor[0] * 0x10000 + pixelCursor[1] * 0x100 + pixelCursor[2] * 0x1;
+		if (id != 0)
+			ids.insert(id);
 	}
 
 	// For each index read, resolve the LDObject behind it and add it to the selection.
-	for (qint32 index : indices)
+	for (qint32 id : ids)
 	{
-		LDObject* object = LDObject::fromID(index);
+		QModelIndex index = m_model->indexFromId(id);
 
-		if (object != nullptr)
-			newSelection.insert(object);
+		if (index.isValid())
+			result.select(index, index);
 	}
 
 	setPicking(false);
 	repaint();
-	return newSelection;
+	return result;
 }
 
 /*
  * Simpler version of GLRenderer::pick which simply picks whatever object on the cursor
  */
-LDObject* GLRenderer::pick(int mouseX, int mouseY)
+QModelIndex GLRenderer::pick(int mouseX, int mouseY)
 {
-	unsigned char pixel[4];
 	makeCurrent();
 	setPicking(true);
 	drawGLScene();
+	unsigned char pixel[4];
 	glReadPixels(mouseX, height() - mouseY, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixel);
-	LDObject* object = LDObject::fromID(pixel[0] * 0x10000 + pixel[1] * 0x100 + pixel[2]);
+	QModelIndex result = m_model->indexFromId(pixel[0] * 0x10000 + pixel[1] * 0x100 + pixel[2]);
 	setPicking(false);
 	repaint();
-	return object;
+	return result;
 }
 
 // =============================================================================
@@ -1018,6 +1018,11 @@
  */
 void GLRenderer::drawFixedCameraBackdrop() {}
 
+QItemSelectionModel* GLRenderer::selectionModel() const
+{
+	return m_compiler->selectionModel();
+}
+
 void GLRenderer::setSelectionModel(QItemSelectionModel* selectionModel)
 {
 	this->m_compiler->setSelectionModel(selectionModel);
--- a/src/glrenderer.h	Thu Feb 15 14:59:04 2018 +0200
+++ b/src/glrenderer.h	Thu Feb 22 11:41:58 2018 +0200
@@ -64,14 +64,15 @@
 	QPoint const& mousePosition() const;
 	QPointF const& mousePositionF() const;
 	QPersistentModelIndex objectAtCursor() const;
-	QSet<LDObject*> pick(const QRect& range);
-	LDObject* pick(int mouseX, int mouseY);
+	QItemSelection pick(const QRect& range);
+	QModelIndex pick(int mouseX, int mouseY);
 	void resetAllAngles();
 	void resetAngles();
 	QImage screenCapture();
 	void setBackground();
 	void setCamera(Camera cam);
 	QPen textPen() const;
+	QItemSelectionModel* selectionModel() const;
 	void setSelectionModel(QItemSelectionModel* selectionModel);
 
 	static const QPen thinBorderPen;
--- a/src/hierarchyelement.cpp	Thu Feb 15 14:59:04 2018 +0200
+++ b/src/hierarchyelement.cpp	Thu Feb 22 11:41:58 2018 +0200
@@ -52,13 +52,13 @@
 }
 
 
-LDDocument* HierarchyElement::currentDocument()
+LDDocument* HierarchyElement::currentDocument() const
 {
 	return m_window->currentDocument();
 }
 
 
-const QSet<LDObject*>& HierarchyElement::selectedObjects()
+QSet<LDObject*> HierarchyElement::selectedObjects()
 {
 	return m_window->selectedObjects();
 }
--- a/src/hierarchyelement.h	Thu Feb 15 14:59:04 2018 +0200
+++ b/src/hierarchyelement.h	Thu Feb 22 11:41:58 2018 +0200
@@ -40,8 +40,8 @@
 public:
 	HierarchyElement (QObject* parent);
 
-	const QSet<LDObject*>& selectedObjects();
-	LDDocument* currentDocument();
+	QSet<LDObject *> selectedObjects();
+	LDDocument* currentDocument() const;
 	GuiUtilities* guiUtilities() const;
 	PrimitiveManager* primitives();
 	Grid* grid() const;
--- a/src/linetypes/conditionaledge.h	Thu Feb 15 14:59:04 2018 +0200
+++ b/src/linetypes/conditionaledge.h	Thu Feb 22 11:41:58 2018 +0200
@@ -35,6 +35,7 @@
 	virtual QString asText() const override;
 	virtual void invert() override;
 	int numVertices() const override { return 4; }
+	int numPolygonVertices() const override { return 2; }
 	LDColor defaultColor() const override { return EdgeColor; }
 	QString typeName() const override { return "condline"; }
 
--- a/src/linetypes/modelobject.cpp	Thu Feb 15 14:59:04 2018 +0200
+++ b/src/linetypes/modelobject.cpp	Thu Feb 22 11:41:58 2018 +0200
@@ -135,6 +135,11 @@
 	return 0;
 }
 
+int LDObject::numPolygonVertices() const
+{
+	return this->numVertices();
+}
+
 // =============================================================================
 //
 LDBezierCurve::LDBezierCurve(const Vertex& v0, const Vertex& v1, const Vertex& v2, const Vertex& v3, Model* model) :
--- a/src/linetypes/modelobject.h	Thu Feb 15 14:59:04 2018 +0200
+++ b/src/linetypes/modelobject.h	Thu Feb 22 11:41:58 2018 +0200
@@ -76,6 +76,7 @@
 	void move (Vertex vect);
 	LDObject* next() const;
 	virtual int numVertices() const;
+	virtual int numPolygonVertices() const;
 	virtual QString objectListText() const;
 	LDObject* previous() const;
 	QColor randomColor() const;
--- a/src/mainwindow.cpp	Thu Feb 15 14:59:04 2018 +0200
+++ b/src/mainwindow.cpp	Thu Feb 22 11:41:58 2018 +0200
@@ -404,13 +404,6 @@
 
 // ---------------------------------------------------------------------------------------------------------------------
 //
-void MainWindow::updateSelection()
-{
-#warning stub method
-}
-
-// ---------------------------------------------------------------------------------------------------------------------
-//
 // Returns the uniform selected color (i.e. 4 if everything selected is red), -1 if there is no such consensus.
 //
 LDColor MainWindow::getUniformSelectedColor()
--- a/src/mainwindow.h	Thu Feb 15 14:59:04 2018 +0200
+++ b/src/mainwindow.h	Thu Feb 22 11:41:58 2018 +0200
@@ -111,7 +111,6 @@
 	void updateEditModeActions();
 	void updateGridToolBar();
 	void updateRecentFilesMenu();
-	void updateSelection();
 	void updateTitle();
 
 	static QPixmap getIcon(QString iconName);
--- a/src/toolsets/viewtoolset.cpp	Thu Feb 15 14:59:04 2018 +0200
+++ b/src/toolsets/viewtoolset.cpp	Thu Feb 22 11:41:58 2018 +0200
@@ -247,7 +247,6 @@
 		{
 			currentDocument()->clearSelection();
 			currentDocument()->addToSelection(object);
-			m_window->updateSelection();
 		}
 	}
 }

mercurial