Added method for zooming to fit, fixed zoom being inappropriate when parts are first loaded

Fri, 24 May 2013 20:38:55 +0300

author
Santeri Piippo <crimsondusk64@gmail.com>
date
Fri, 24 May 2013 20:38:55 +0300
changeset 255
67d4aedf1041
parent 254
434c9844e45d
child 256
9f7e6e288953

Added method for zooming to fit, fixed zoom being inappropriate when parts are first loaded

src/gldraw.cpp file | annotate | diff | comparison | revisions
src/gldraw.h file | annotate | diff | comparison | revisions
src/gui.cpp file | annotate | diff | comparison | revisions
src/gui.h file | annotate | diff | comparison | revisions
src/gui_actions.cpp file | annotate | diff | comparison | revisions
--- a/src/gldraw.cpp	Fri May 24 19:44:53 2013 +0300
+++ b/src/gldraw.cpp	Fri May 24 20:38:55 2013 +0300
@@ -92,7 +92,6 @@
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 // =============================================================================
 GLRenderer::GLRenderer (QWidget* parent) : QGLWidget (parent) {
-	resetAngles ();
 	m_picking = m_rangepick = false;
 	m_camera = (GL::Camera) gl_camera.value;
 	m_drawToolTip = false;
@@ -100,6 +99,7 @@
 	m_rectdraw = false;
 	setFile (null);
 	setDrawOnly (false);
+	resetAngles ();
 	
 	m_toolTipTimer = new QTimer (this);
 	m_toolTipTimer->setSingleShot (true);
@@ -170,12 +170,7 @@
 	m_rotX = 30.0f;
 	m_rotY = 325.f;
 	m_panX = m_panY = m_rotZ = 0.0f;
-	
-	// Set the default zoom based on the bounding box
-	if (g_BBox.empty () == false)
-		setZoom (g_BBox.size () * 6);
-	else
-		setZoom (30.0f);
+	zoomToFit ();
 }
 
 // =============================================================================
@@ -360,6 +355,7 @@
 		glLoadIdentity ();
 		glOrtho (-m_virtWidth, m_virtWidth, -m_virtHeight, m_virtHeight, -100.0f, 100.0f);
 		glTranslatef (m_panX, m_panY, 0.0f);
+		
 		glRotatef (90.0f, g_staticCameras[m_camera].glrotate[0],
 			g_staticCameras[m_camera].glrotate[1],
 			g_staticCameras[m_camera].glrotate[2]);
@@ -976,11 +972,7 @@
 void GLRenderer::wheelEvent (QWheelEvent* ev) {
 	makeCurrent ();
 	
-	if (zoom () > 15)
-		setZoom (zoom () * (ev->delta () < 0 ? 1.2f : 0.833f));
-	else
-		setZoom (zoom () + ((double) ev->delta () / -100.0f));
-	
+	zoomNotch (ev->delta () > 0);
 	setZoom (clamp<double> (zoom (), 0.01f, 10000.0f));
 	
 	update ();
@@ -1401,4 +1393,88 @@
 void deleteCameraIcons () {
 	for (CameraIcon& info : g_CameraIcons)
 		delete info.img;
+}
+
+void GLRenderer::zoomNotch (bool inward) {
+	if (zoom () > 15)
+		setZoom (zoom () * (inward ? 0.833f : 1.2f));
+	else
+		setZoom (zoom () + (inward ? -1.2f : 1.2f));
+}
+
+void GLRenderer::zoomToFit () {
+	if (file () == null) {
+		setZoom (30.0f);
+		return;
+	}
+	
+	bool lastfilled = false;
+	bool firstrun = true;
+	const uint32 white = 0xFFFFFFFF;
+	bool inward = true;
+	ulong run = 0;
+	const ushort w = m_width, h = m_height;
+	
+	glClearColor (1.0, 1.0, 1.0, 1.0);
+	glDisable (GL_DITHER);
+	
+	// Use the pick list while drawing the scene, this way we can tell whether borders
+	// are background or not.
+	m_picking = true;
+	
+	for (;;) {
+		if (zoom () > 10000.0f || zoom () < 0.0f) {
+			// Obviously, there's nothing to draw if we get here.
+			// Default to 30.0f and break out.
+			setZoom (30.0f);
+			break;
+		}
+		
+		zoomNotch (inward);
+		
+		uchar* cap = new uchar[4 * w * h];
+		drawGLScene ();
+		glReadPixels (0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, cap);
+		uint32* imgdata = reinterpret_cast<uint32*> (cap);
+		bool filled = false;
+		
+		// Check the top and bottom rows
+		for (ushort i = 0; i < w && !filled; ++i)
+			if (imgdata[i] != white || imgdata[((h - 1) * w) + i] != white)
+				filled = true;
+		
+		// Left and right edges
+		for (ushort i = 0; i < h && !filled; ++i)
+			if (imgdata[i * w] != white || imgdata[(i * w) + (w - 1)] != white)
+				filled = true;
+		
+		if (firstrun) {
+			// If this is the first run, we don't know enough to determine
+			// whether the zoom was to fit, so we mark in our knowledge so
+			// far and start over.
+			inward = !filled;
+			firstrun = false;
+		} else {
+			// If this run filled the screen and the last one did not, the
+			// last run had ideal zoom - zoom a bit back and we should reach it.
+			if (filled && !lastfilled) {
+				zoomNotch (false);
+				break;
+			}
+			
+			// If this run did not fill the screen and the last one did, we've
+			// now reached ideal zoom so we're done here.
+			if (!filled && lastfilled)
+				break;
+			
+			inward = !filled;
+		}
+		
+		delete[] cap;
+		lastfilled = filled;
+		++run;
+	}
+	
+	setBackground ();
+	m_picking = false;
 }
\ No newline at end of file
--- a/src/gldraw.h	Fri May 24 19:44:53 2013 +0300
+++ b/src/gldraw.h	Fri May 24 20:38:55 2013 +0300
@@ -88,6 +88,8 @@
 	void           setCamera           (const Camera cam);
 	void           setDepthValue       (double depth);
 	void           setupOverlay        ();
+	void           zoomNotch           (bool inward);
+	void           zoomToFit           ();
 	
 	static void    deleteLists         (LDObject* obj);
 
--- a/src/gui.cpp	Fri May 24 19:44:53 2013 +0300
+++ b/src/gui.cpp	Fri May 24 20:38:55 2013 +0300
@@ -1131,4 +1131,9 @@
 	
 	assert (g_metacursor < MAX_ACTIONS);
 	g_actionMeta[g_metacursor++] = meta;
+}
+
+QImage imageFromScreencap (uchar* data, ushort w, ushort h) {
+	// GL and Qt formats have R and B swapped. Also, GL flips Y - correct it as well.
+	return QImage (data, w, h, QImage::Format_ARGB32).rgbSwapped ().mirrored ();
 }
\ No newline at end of file
--- a/src/gui.h	Fri May 24 19:44:53 2013 +0300
+++ b/src/gui.h	Fri May 24 20:38:55 2013 +0300
@@ -180,6 +180,7 @@
 void makeColorSelector (QComboBox* box);
 QDialogButtonBox* makeButtonBox (QDialog& dlg);
 CheckBoxGroup* makeAxesBox ();
+QImage imageFromScreencap (uchar* data, ushort w, ushort h);
 
 // -----------------------------------------------------------------------------
 // Pointer to the instance of ForgeWindow.
--- a/src/gui_actions.cpp	Fri May 24 19:44:53 2013 +0300
+++ b/src/gui_actions.cpp	Fri May 24 20:38:55 2013 +0300
@@ -379,10 +379,8 @@
 	setlocale (LC_ALL, "C");
 	
 	ushort w, h;
-	uchar* imagedata = g_win->R ()->screencap (w, h);
-	
-	// GL and Qt formats have R and B swapped. Also, GL flips Y - correct it as well.
-	QImage img = QImage (imagedata, w, h, QImage::Format_ARGB32).rgbSwapped ().mirrored ();
+	uchar* imgdata = g_win->R ()->screencap (w, h);
+	QImage img = imageFromScreencap (imgdata, w, h);
 	
 	str root = basename (g_curfile->name ());
 	if (~root >= 4 && root.substr (~root - 4, -1) == ".dat")
@@ -395,7 +393,7 @@
 	if (~fname > 0 && !img.save (fname))
 		critical (fmt ("Couldn't open %s for writing to save screencap: %s", fname.c (), strerror (errno)));
 	
-	delete[] imagedata;
+	delete[] imgdata;
 }
 
 // =========================================================================================================================================
@@ -470,9 +468,10 @@
 	rend->compileAllObjects ();
 	rend->initGLData ();
 	rend->drawGLScene ();
-	uchar* imagedata = rend->screencap (w, h);
 	
-	QImage img = QImage (imagedata, w, h, QImage::Format_ARGB32).rgbSwapped ().mirrored ();
+	uchar* imgdata = rend->screencap (w, h);
+	QImage img = imageFromScreencap (imgdata, w, h);
+	
 	if (img.isNull ()) {
 		critical ("Failed to create the image!\n");
 	} else {
@@ -484,6 +483,6 @@
 		dlg->exec ();
 	}
 	
-	delete[] imagedata;
+	delete[] imgdata;
 	rend->deleteLater ();
 }
\ No newline at end of file

mercurial