Area selection! This sure was a *pain* to add.

Wed, 24 Apr 2013 19:59:41 +0300

author
Santeri Piippo <crimsondusk64@gmail.com>
date
Wed, 24 Apr 2013 19:59:41 +0300
changeset 133
5a8073d713a3
parent 132
577e8e89d8de
child 134
7fd0784471df

Area selection! This sure was a *pain* to add.

gldraw.cpp file | annotate | diff | comparison | revisions
gldraw.h file | annotate | diff | comparison | revisions
misc.h file | annotate | diff | comparison | revisions
--- a/gldraw.cpp	Mon Apr 22 17:46:09 2013 +0300
+++ b/gldraw.cpp	Wed Apr 24 19:59:41 2013 +0300
@@ -26,6 +26,7 @@
 #include "bbox.h"
 #include "colors.h"
 #include "gui.h"
+#include "misc.h"
 
 static double g_faObjectOffset[3];
 static double g_StoredBBoxSize;
@@ -46,7 +47,7 @@
 // =============================================================================
 GLRenderer::GLRenderer (QWidget* parent) : QGLWidget (parent) {
 	resetAngles ();
-	picking = false;
+	picking = rangepick = false;
 	
 	qPulseTimer = new QTimer (this);
 	connect (qPulseTimer, SIGNAL (timeout ()), this, SLOT (slot_timerUpdate ()));
@@ -139,9 +140,9 @@
 		// Calculate a color based from this index. This method caters for
 		// 16777216 objects. I don't think that'll be exceeded anytime soon. :)
 		// ATM biggest is 53588.dat with 12600 lines.
-		double r = i % 256;
-		double g = (i / 256) % 256;
-		double b = (i / (256 * 256)) % 256;
+		double r = (i / (256 * 256)) % 256,
+			g = (i / 256) % 256,
+			b = i % 256;
 		
 		glColor3f (r / 255.f, g / 255.f, b / 255.f);
 		return;
@@ -238,12 +239,17 @@
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 // =============================================================================
 void GLRenderer::resizeGL (int w, int h) {
+	width = w;
+	height = h;
+	
 	glViewport (0, 0, w, h);
 	glLoadIdentity ();
 	glMatrixMode (GL_PROJECTION);
 	gluPerspective (45.0f, (double)w / (double)h, 0.1f, 100.0f);
 }
 
+template<class T> using initlist = std::initializer_list<T>;
+
 // =============================================================================
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 // =============================================================================
@@ -267,6 +273,37 @@
 		for (LDObject* obj : g_CurrentFile->objects)
 			glCallList ((picking == false) ? obj->uGLList : obj->uGLPickList);
 	glPopMatrix ();
+	
+	// If we're range-picking, draw a rectangle encompassing the selection area.
+	if (rangepick && !picking) {
+		const short x0 = rangeStart.x (),
+			y0 = rangeStart.y (),
+			x1 = pos.x (),
+			y1 = pos.y ();
+		
+		glMatrixMode (GL_PROJECTION);
+		
+		glPushMatrix ();
+			glLoadIdentity ();
+			glOrtho (.0, width, height, .0, -1.0, 1.0);
+			
+			for (int x : vector<int> ({GL_QUADS, GL_LINE_LOOP})) {
+				if (x == GL_QUADS)
+					glColor4f (.0, .8, 1.0, .6);
+				else
+					glColor4f (1.0, 1.0, 1.0, 1.0);
+				
+				glBegin (x);
+					glVertex2i (x0, y0);
+					glVertex2i (x1, y0);
+					glVertex2i (x1, y1);
+					glVertex2i (x0, y1);
+				glEnd ();
+			}
+		glPopMatrix ();
+		
+		glMatrixMode (GL_MODELVIEW);
+	}
 }
 
 // =============================================================================
@@ -437,49 +474,51 @@
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 // =============================================================================
 void GLRenderer::mouseReleaseEvent (QMouseEvent* ev) {
-	if ((qMouseButtons & Qt::LeftButton) && !(ev->buttons() & Qt::LeftButton)) {
-		if (ulTotalMouseMove < 10)
-			pick (ev->x(), ev->y(), (qKeyMods & Qt::ControlModifier));
+	if ((lastButtons & Qt::LeftButton) && !(ev->buttons() & Qt::LeftButton)) {
+		if (ulTotalMouseMove < 10 || rangepick)
+			pick (ev->x (), ev->y (), (qKeyMods & Qt::ControlModifier));
 		
+		rangepick = false;
 		ulTotalMouseMove = 0;
 	}
 }
 
 // =============================================================================
 void GLRenderer::mousePressEvent (QMouseEvent* ev) {
-	qMouseButtons = ev->buttons();
-	if (ev->buttons() & Qt::LeftButton)
+	if (ev->buttons () & Qt::LeftButton)
 		ulTotalMouseMove = 0;
+	
+	if (ev->modifiers () & Qt::ShiftModifier) {
+		rangepick = true;
+		rangeStart.setX (ev->x ());
+		rangeStart.setY (ev->y ());
+	}
+	
+	lastButtons = ev->buttons ();
 }
 
 // =============================================================================
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 // =============================================================================
 void GLRenderer::mouseMoveEvent (QMouseEvent* ev) {
-	int dx = ev->x () - lastPos.x ();
-	int dy = ev->y () - lastPos.y ();
+	int dx = ev->x () - pos.x ();
+	int dy = ev->y () - pos.y ();
 	ulTotalMouseMove += abs (dx) + abs (dy);
 	
-	if (ev->buttons () & Qt::LeftButton) {
+	if (ev->buttons () & Qt::LeftButton && !rangepick) {
 		rotX = rotX + (dy);
 		rotY = rotY + (dx);
+		
 		clampAngle (rotX);
 		clampAngle (rotY);
 	}
 	
-	if (ev->buttons () & Qt::RightButton) {
-		rotX = rotX + (dy);
-		rotZ = rotZ + (dx);
-		clampAngle (rotX);
-		clampAngle (rotZ);
-	}
-	
 	if (ev->buttons () & Qt::MidButton) {
 		panX += 0.03f * dx;
 		panY -= 0.03f * dy;
 	}
 	
-	lastPos = ev->pos ();
+	pos = ev->pos ();
 	updateGL ();
 }
 
@@ -516,8 +555,10 @@
 // ========================================================================= //
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
 // ========================================================================= //
-void GLRenderer::pick (uint uMouseX, uint uMouseY, bool bAdd) {
-	if (bAdd == false) {
+void GLRenderer::pick (uint mx, uint my, bool add) {
+	GLint viewport[4];
+	
+	if (add == false) {
 		// Clear the selection if we don't wish to add to it.
 		std::vector<LDObject*> paOldSelection = g_ForgeWindow->paSelection;
 		g_ForgeWindow->paSelection.clear ();
@@ -527,32 +568,75 @@
 			recompileObject (obj);
 	}
 	
+	picking = true;
+	
+	// Paint the picking scene
 	glDisable (GL_DITHER);
 	glClearColor (1.0f, 1.0f, 1.0f, 1.0f);
-	
-	picking = true;
-	
 	paintGL ();
 	
-	GLubyte ucaPixel[3];
-	GLint daViewport[4];
-	glGetIntegerv (GL_VIEWPORT, daViewport);
-	glReadPixels (uMouseX, daViewport[3] - uMouseY, 1, 1, GL_RGB, GL_UNSIGNED_BYTE,
-		reinterpret_cast<GLvoid*> (ucaPixel));
+	glGetIntegerv (GL_VIEWPORT, viewport);
+	
+	short x0 = mx,
+		y0 = my;
+	short x1, y1;
+	
+	if (rangepick) {
+		x1 = rangeStart.x ();
+		y1 = rangeStart.y ();
+	} else {
+		x1 = x0 + 1;
+		y1 = y0 + 1;
+	}
+	
+	if (x0 > x1)
+		dataswap (x0, x1);
+	
+	if (y0 > y1)
+		dataswap (y0, y1);
 	
-	// If we hit a white pixel, we selected the background. This no object is selected.
-	const bool bHasSelection = (ucaPixel[0] != 255 || ucaPixel[1] != 255 || ucaPixel[2] != 255);
+	// Clamp the values to ensure they're within bounds
+	x0 = max<short> (0, x0);
+	y0 = max<short> (0, y0);
+	x1 = min<short> (x1, width);
+	y1 = min<short> (y1, height);
+	
+	short areawidth = (x1 - x0);
+	short areaheight = (y1 - y0);
+	
+	const long numpixels = areawidth * areaheight;
+	uchar* const pixeldata = new uchar[4 * numpixels];
+	uchar* pixelptr = &pixeldata[0];
 	
-	if (bHasSelection) {
-		ulong idx = ucaPixel[0] + (ucaPixel[1] * 256) + (ucaPixel[2] * 256 * 256);
+	assert (viewport[3] == height);
+	
+	glReadPixels (x0, viewport[3] - y1, areawidth, areaheight, GL_RGBA, GL_UNSIGNED_BYTE, pixeldata);
+	
+	for (long i = 0; i < numpixels; ++i) {
+		uint32 idx =
+			(*(pixelptr) * 0x10000) +
+			(*(pixelptr + 1) * 0x00100) +
+			(*(pixelptr + 2) * 0x00001);
+		pixelptr += 4;
+		
+		if (idx == 0xFFFFFF)
+			continue; // White is background; skip
 		
 		LDObject* obj = g_CurrentFile->object (idx);
 		g_ForgeWindow->paSelection.push_back (obj);
 	}
 	
+	delete[] pixeldata;
+	
+	std::vector<LDObject*>& sel = g_ForgeWindow->paSelection;
+	std::sort (sel.begin(), sel.end ());
+	std::vector<LDObject*>::iterator pos = std::unique (sel.begin (), sel.end ());
+	sel.resize (std::distance (sel.begin (), pos));
+	
 	g_ForgeWindow->buildObjList ();
 	
 	picking = false;
+	rangepick = false;
 	glEnable (GL_DITHER);
 	
 	setBackground ();
--- a/gldraw.h	Mon Apr 22 17:46:09 2013 +0300
+++ b/gldraw.h	Wed Apr 24 19:59:41 2013 +0300
@@ -16,8 +16,8 @@
  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#ifndef __GLDRAW_H__
-#define __GLDRAW_H__
+#ifndef GLDRAW_H
+#define GLDRAW_H
 
 #include <QGLWidget>
 #include <qtimer.h>
@@ -39,7 +39,7 @@
 	void hardRefresh ();
 	void compileObjects ();
 	void setBackground ();
-	void pick (uint uMouseX, uint uMouseY, bool bAdd);
+	void pick (uint mouseX, uint mouseY, bool add);
 	QColor getMainColor ();
 	void recompileObject (LDObject* obj);
 	void refresh ();
@@ -48,9 +48,12 @@
 	
 	double rotX, rotY, rotZ;
 	double panX, panY;
-	QPoint lastPos;
+	QPoint pos;
 	double zoom;
 	bool picking;
+	bool rangepick;
+	short width, height;
+	QPoint rangeStart;
 
 protected:
 	void initializeGL ();
@@ -75,7 +78,7 @@
 	
 	QTimer* qPulseTimer;
 	
-	Qt::MouseButtons qMouseButtons;
+	Qt::MouseButtons lastButtons;
 	Qt::KeyboardModifiers qKeyMods;
 	ulong ulTotalMouseMove;
 	
@@ -83,4 +86,4 @@
 	void slot_timerUpdate ();
 };
 
-#endif // __GLDRAW_H__
\ No newline at end of file
+#endif // GLDRAW_H
\ No newline at end of file
--- a/misc.h	Mon Apr 22 17:46:09 2013 +0300
+++ b/misc.h	Wed Apr 24 19:59:41 2013 +0300
@@ -65,6 +65,12 @@
 	return g_GridInfo[grid];
 }
 
+template<class T> void dataswap (T& a, T& b) {
+	T c = a;
+	a = b;
+	b = c;
+}
+
 // =============================================================================
 // StringParser
 //

mercurial