src/gldraw.cpp

changeset 557
04e140bdeb0b
parent 556
5f4395ec5db0
child 558
5f6e30e0450c
--- a/src/gldraw.cpp	Fri Dec 13 00:39:49 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1995 +0,0 @@
-/*
- *  LDForge: LDraw parts authoring CAD
- *  Copyright (C) 2013 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 <QGLWidget>
-#include <QWheelEvent>
-#include <QMouseEvent>
-#include <QContextMenuEvent>
-#include <QInputDialog>
-#include <QToolTip>
-#include <QTimer>
-#include <GL/glu.h>
-
-#include "main.h"
-#include "config.h"
-#include "document.h"
-#include "gldraw.h"
-#include "colors.h"
-#include "gui.h"
-#include "misc.h"
-#include "history.h"
-#include "dialogs.h"
-#include "addObjectDialog.h"
-#include "messagelog.h"
-#include "primitives.h"
-#include "moc_gldraw.cpp"
-
-static const LDFixedCameraInfo g_FixedCameras[6] =
-{	{{  1,  0, 0 }, X, Z, false, false },
-	{{  0,  0, 0 }, X, Y, false,  true },
-	{{  0,  1, 0 }, Z, Y,  true,  true },
-	{{ -1,  0, 0 }, X, Z, false,  true },
-	{{  0,  0, 0 }, X, Y,  true,  true },
-	{{  0, -1, 0 }, Z, Y, false,  true },
-};
-
-static const matrix g_circleDrawTransforms[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 },
-};
-
-cfg (String, gl_bgcolor, "#CCCCD9");
-cfg (String, gl_maincolor, "#707078");
-cfg (Float, gl_maincolor_alpha, 1.0);
-cfg (Int, gl_linethickness, 2);
-cfg (Bool, gl_colorbfc, false);
-cfg (Int, gl_camera, GLRenderer::EFreeCamera);
-cfg (Bool, gl_blackedges, false);
-cfg (Bool, gl_axes, false);
-cfg (Bool, gl_wireframe, false);
-cfg (Bool, gl_logostuds, false);
-cfg (Bool, gl_aa, true);
-
-// argh
-const char* g_CameraNames[7] =
-{	QT_TRANSLATE_NOOP ("GLRenderer",  "Top"),
-	QT_TRANSLATE_NOOP ("GLRenderer",  "Front"),
-	QT_TRANSLATE_NOOP ("GLRenderer",  "Left"),
-	QT_TRANSLATE_NOOP ("GLRenderer",  "Bottom"),
-	QT_TRANSLATE_NOOP ("GLRenderer",  "Back"),
-	QT_TRANSLATE_NOOP ("GLRenderer",  "Right"),
-	QT_TRANSLATE_NOOP ("GLRenderer",  "Free")
-};
-
-const GL::EFixedCamera g_Cameras[7] =
-{	GL::ETopCamera,
-	GL::EFrontCamera,
-	GL::ELeftCamera,
-	GL::EBottomCamera,
-	GL::EBackCamera,
-	GL::ERightCamera,
-	GL::EFreeCamera
-};
-
-const struct LDGLAxis
-{	const QColor col;
-	const vertex vert;
-} g_GLAxes[3] =
-{	{ QColor (255,   0,   0), vertex (10000, 0, 0) },
-	{ QColor (80,  192,   0), vertex (0, 10000, 0) },
-	{ QColor (0,   160, 192), vertex (0, 0, 10000) },
-};
-
-static bool g_glInvert = false;
-static QList<int> g_warnedColors;
-
-// =============================================================================
-// -----------------------------------------------------------------------------
-GLRenderer::GLRenderer (QWidget* parent) : QGLWidget (parent)
-{	m_Picking = m_rangepick = false;
-	m_camera = (GL::EFixedCamera) gl_camera.value;
-	m_drawToolTip = false;
-	m_EditMode = ESelectMode;
-	m_rectdraw = false;
-	m_panning = false;
-	setFile (null);
-	setDrawOnly (false);
-	setMessageLog (null);
-	m_width = m_height = -1;
-	m_hoverpos = g_origin;
-
-	m_toolTipTimer = new QTimer (this);
-	m_toolTipTimer->setSingleShot (true);
-	connect (m_toolTipTimer, SIGNAL (timeout()), this, SLOT (slot_toolTipTimer()));
-
-	m_thickBorderPen = QPen (QColor (0, 0, 0, 208), 2, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin);
-	m_thinBorderPen = m_thickBorderPen;
-	m_thinBorderPen.setWidth (1);
-
-	// Init camera icons
-	for (const GL::EFixedCamera cam : g_Cameras)
-	{	str iconname = fmt ("camera-%1", tr (g_CameraNames[cam]).toLower());
-
-		CameraIcon* info = &m_cameraIcons[cam];
-		info->img = new QPixmap (getIcon (iconname));
-		info->cam = cam;
-	}
-
-	for (int i = 0; i < 6; ++i)
-	{	m_overlays[i].img = null;
-		m_depthValues[i] = 0.0f;
-	}
-
-	calcCameraIcons();
-}
-
-// =============================================================================
-// -----------------------------------------------------------------------------
-GLRenderer::~GLRenderer()
-{	for (int i = 0; i < 6; ++i)
-		delete m_overlays[i].img;
-
-	for (CameraIcon& info : m_cameraIcons)
-		delete info.img;
-}
-
-// =============================================================================
-// Calculates the "hitboxes" of the camera icons so that we can tell when the
-// cursor is pointing at the camera icon.
-// -----------------------------------------------------------------------------
-void GLRenderer::calcCameraIcons()
-{	int i = 0;
-
-	for (CameraIcon& info : m_cameraIcons)
-	{	// MATH
-		const long x1 = (m_width - (info.cam != EFreeCamera ? 48 : 16)) + ((i % 3) * 16) - 1,
-			y1 = ((i / 3) * 16) + 1;
-
-		info.srcRect = QRect (0, 0, 16, 16);
-		info.destRect = QRect (x1, y1, 16, 16);
-		info.selRect = QRect (
-			info.destRect.x(),
-			info.destRect.y(),
-			info.destRect.width() + 1,
-			info.destRect.height() + 1
-		);
-
-		++i;
-	}
-}
-
-// =============================================================================
-// -----------------------------------------------------------------------------
-void GLRenderer::initGLData()
-{	glEnable (GL_BLEND);
-	glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
-	glEnable (GL_POLYGON_OFFSET_FILL);
-	glPolygonOffset (1.0f, 1.0f);
-
-	glEnable (GL_DEPTH_TEST);
-	glShadeModel (GL_SMOOTH);
-	glEnable (GL_MULTISAMPLE);
-
-	if (gl_aa)
-	{	glEnable (GL_LINE_SMOOTH);
-		glEnable (GL_POLYGON_SMOOTH);
-		glHint (GL_LINE_SMOOTH_HINT, GL_NICEST);
-		glHint (GL_POLYGON_SMOOTH_HINT, GL_NICEST);
-	} else
-	{	glDisable (GL_LINE_SMOOTH);
-		glDisable (GL_POLYGON_SMOOTH);
-	}
-}
-
-// =============================================================================
-// -----------------------------------------------------------------------------
-void GLRenderer::resetAngles()
-{	rot (X) = 30.0f;
-	rot (Y) = 325.f;
-	pan (X) = pan (Y) = rot (Z) = 0.0f;
-	zoomToFit();
-}
-
-// =============================================================================
-// -----------------------------------------------------------------------------
-void GLRenderer::resetAllAngles()
-{	EFixedCamera oldcam = camera();
-
-	for (int i = 0; i < 7; ++i)
-	{	setCamera ((EFixedCamera) i);
-		resetAngles();
-	}
-
-	setCamera (oldcam);
-}
-
-// =============================================================================
-// -----------------------------------------------------------------------------
-void GLRenderer::initializeGL()
-{	setBackground();
-
-	glLineWidth (gl_linethickness);
-
-	setAutoFillBackground (false);
-	setMouseTracking (true);
-	setFocusPolicy (Qt::WheelFocus);
-	compileAllObjects();
-}
-
-// =============================================================================
-// -----------------------------------------------------------------------------
-QColor GLRenderer::getMainColor()
-{	QColor col (gl_maincolor);
-
-	if (!col.isValid())
-		return QColor (0, 0, 0);
-
-	col.setAlpha (gl_maincolor_alpha * 255.f);
-	return col;
-}
-
-// =============================================================================
-// -----------------------------------------------------------------------------
-void GLRenderer::setBackground()
-{	QColor col (gl_bgcolor);
-
-	if (!col.isValid())
-		return;
-
-	col.setAlpha (255);
-
-	m_darkbg = luma (col) < 80;
-	m_bgcolor = col;
-	qglClearColor (col);
-}
-
-// =============================================================================
-// -----------------------------------------------------------------------------
-void GLRenderer::setObjectColor (LDObject* obj, const ListType list)
-{	QColor qcol;
-
-	if (!obj->isColored())
-		return;
-
-	if (list == GL::PickList)
-	{	// Make the color by the object's ID if we're picking, so we can make the
-		// ID again from the color we get from the picking results. Be sure to use
-		// the top level parent's index since we want a subfile's children point
-		// to the subfile itself.
-		long i = obj->topLevelParent()->getID();
-
-		// 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 * 256)) % 256,
-			   g = (i / 256) % 256,
-			   b = i % 256;
-
-		qglColor (QColor (r, g, b));
-		return;
-	}
-
-	if ((list == BFCFrontList || list == BFCBackList) &&
-		obj->getType() != LDObject::Line &&
-		obj->getType() != LDObject::CondLine)
-	{	if (list == GL::BFCFrontList)
-			qcol = QColor (40, 192, 0);
-		else
-			qcol = QColor (224, 0, 0);
-	}
-	else
-	{	if (obj->getColor() == maincolor)
-			qcol = getMainColor();
-		else
-		{	LDColor* col = getColor (obj->getColor());
-
-			if (col)
-				qcol = col->faceColor;
-		}
-
-		if (obj->getColor() == edgecolor)
-		{	qcol = luma (m_bgcolor) < 40 ? QColor (64, 64, 64) : Qt::black;
-			LDColor* col;
-
-			if (!gl_blackedges && obj->getParent() && (col = getColor (obj->getParent()->getColor())))
-				qcol = col->edgeColor;
-		}
-
-		if (qcol.isValid() == false)
-		{	// The color was unknown. Use main color to make the object at least
-			// not appear pitch-black.
-			if (obj->getColor() != edgecolor)
-				qcol = getMainColor();
-
-			// Warn about the unknown colors, but only once.
-			for (int i : g_warnedColors)
-				if (obj->getColor() == i)
-					return;
-
-			log ("%1: Unknown color %2!\n", __func__, obj->getColor());
-			g_warnedColors << obj->getColor();
-			return;
-		}
-	}
-
-	long r = qcol.red(),
-		 g = qcol.green(),
-		 b = qcol.blue(),
-		 a = qcol.alpha();
-
-	if (obj->topLevelParent()->isSelected())
-	{	// Brighten it up for the select list.
-		const uchar add = 51;
-
-		r = min (r + add, 255l);
-		g = min (g + add, 255l);
-		b = min (b + add, 255l);
-	}
-
-	glColor4f (
-		((double) r) / 255.0f,
-		((double) g) / 255.0f,
-		((double) b) / 255.0f,
-		((double) a) / 255.0f);
-}
-
-// =============================================================================
-// -----------------------------------------------------------------------------
-void GLRenderer::refresh()
-{	update();
-	swapBuffers();
-}
-
-// =============================================================================
-// -----------------------------------------------------------------------------
-void GLRenderer::hardRefresh()
-{	compileAllObjects();
-	refresh();
-
-	glLineWidth (gl_linethickness);
-}
-
-// =============================================================================
-// -----------------------------------------------------------------------------
-void GLRenderer::resizeGL (int w, int h)
-{	m_width = w;
-	m_height = h;
-
-	calcCameraIcons();
-
-	glViewport (0, 0, w, h);
-	glMatrixMode (GL_PROJECTION);
-	glLoadIdentity();
-	gluPerspective (45.0f, (double) w / (double) h, 1.0f, 10000.0f);
-	glMatrixMode (GL_MODELVIEW);
-}
-
-// =============================================================================
-// -----------------------------------------------------------------------------
-void GLRenderer::drawGLScene()
-{	if (getFile() == null)
-		return;
-
-	if (gl_wireframe && !isPicking())
-		glPolygonMode (GL_FRONT_AND_BACK, GL_LINE);
-
-	glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
-	glEnable (GL_DEPTH_TEST);
-
-	if (m_camera != EFreeCamera)
-	{	glMatrixMode (GL_PROJECTION);
-		glPushMatrix();
-
-		glLoadIdentity();
-		glOrtho (-m_virtWidth, m_virtWidth, -m_virtHeight, m_virtHeight, -100.0f, 100.0f);
-		glTranslatef (pan (X), pan (Y), 0.0f);
-
-		if (m_camera != EFrontCamera && m_camera != EBackCamera)
-		{	glRotatef (90.0f, g_FixedCameras[camera()].glrotate[0],
-				g_FixedCameras[camera()].glrotate[1],
-				g_FixedCameras[camera()].glrotate[2]);
-		}
-
-		// Back camera needs to be handled differently
-		if (m_camera == GLRenderer::EBackCamera)
-		{	glRotatef (180.0f, 1.0f, 0.0f, 0.0f);
-			glRotatef (180.0f, 0.0f, 0.0f, 1.0f);
-		}
-	}
-	else
-	{	glMatrixMode (GL_MODELVIEW);
-		glPushMatrix();
-		glLoadIdentity();
-
-		glTranslatef (0.0f, 0.0f, -2.0f);
-		glTranslatef (pan (X), pan (Y), -zoom());
-		glRotatef (rot (X), 1.0f, 0.0f, 0.0f);
-		glRotatef (rot (Y), 0.0f, 1.0f, 0.0f);
-		glRotatef (rot (Z), 0.0f, 0.0f, 1.0f);
-	}
-
-	const GL::ListType list = (!isDrawOnly() && isPicking()) ? PickList : NormalList;
-
-	if (gl_colorbfc && !isPicking() && !isDrawOnly())
-	{	glEnable (GL_CULL_FACE);
-
-		for (LDObject* obj : getFile()->getObjects())
-		{	if (obj->isHidden())
-				continue;
-
-			glCullFace (GL_BACK);
-			glCallList (obj->glLists[BFCFrontList]);
-
-			glCullFace (GL_FRONT);
-			glCallList (obj->glLists[BFCBackList]);
-		}
-
-		glDisable (GL_CULL_FACE);
-	}
-	else
-	{	for (LDObject* obj : getFile()->getObjects())
-		{	if (obj->isHidden())
-				continue;
-
-			glCallList (obj->glLists[list]);
-		}
-	}
-
-	if (gl_axes && !isPicking() && !isDrawOnly())
-		glCallList (m_axeslist);
-
-	glPopMatrix();
-	glMatrixMode (GL_MODELVIEW);
-	glPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
-}
-
-// =============================================================================
-// -----------------------------------------------------------------------------
-// This converts a 2D point on the screen to a 3D point in the model. If 'snap'
-// is true, the 3D point will snap to the current grid.
-// -----------------------------------------------------------------------------
-vertex GLRenderer::coordconv2_3 (const QPoint& pos2d, bool snap) const
-{	assert (camera() != EFreeCamera);
-
-	vertex pos3d;
-	const LDFixedCameraInfo* cam = &g_FixedCameras[m_camera];
-	const Axis axisX = cam->axisX;
-	const Axis axisY = cam->axisY;
-	const int negXFac = cam->negX ? -1 : 1,
-				negYFac = cam->negY ? -1 : 1;
-
-	// Calculate cx and cy - these are the LDraw unit coords the cursor is at.
-	double cx = (-m_virtWidth + ((2 * pos2d.x() * m_virtWidth) / m_width) - pan (X));
-	double cy = (m_virtHeight - ((2 * pos2d.y() * m_virtHeight) / m_height) - pan (Y));
-
-	if (snap)
-	{	cx = Grid::snap (cx, (Grid::Config) axisX);
-		cy = Grid::snap (cy, (Grid::Config) axisY);
-	}
-
-	cx *= negXFac;
-	cy *= negYFac;
-
-	str tmp;
-	// Create the vertex from the coordinates
-	pos3d[axisX] = tmp.sprintf ("%.3f", cx).toDouble();
-	pos3d[axisY] = tmp.sprintf ("%.3f", cy).toDouble();
-	pos3d[3 - axisX - axisY] = getDepthValue();
-	return pos3d;
-}
-
-// =============================================================================
-// -----------------------------------------------------------------------------
-// Inverse operation for the above - convert a 3D position to a 2D screen
-// position
-// -----------------------------------------------------------------------------
-QPoint GLRenderer::coordconv3_2 (const vertex& pos3d) const
-{	GLfloat m[16];
-	const LDFixedCameraInfo* cam = &g_FixedCameras[m_camera];
-	const Axis axisX = cam->axisX;
-	const Axis axisY = cam->axisY;
-	const int negXFac = cam->negX ? -1 : 1,
-				negYFac = cam->negY ? -1 : 1;
-
-	glGetFloatv (GL_MODELVIEW_MATRIX, m);
-
-	const double x = pos3d.x();
-	const double y = pos3d.y();
-	const double z = pos3d.z();
-
-	vertex transformed;
-	transformed[X] = (m[0] * x) + (m[1] * y) + (m[2] * z) + m[3];
-	transformed[Y] = (m[4] * x) + (m[5] * y) + (m[6] * z) + m[7];
-	transformed[Z] = (m[8] * x) + (m[9] * y) + (m[10] * z) + m[11];
-
-	double rx = (((transformed[axisX] * negXFac) + m_virtWidth + pan (X)) * m_width) / (2 * m_virtWidth);
-	double ry = (((transformed[axisY] * negYFac) - m_virtHeight + pan (Y)) * m_height) / (2 * m_virtHeight);
-
-	return QPoint (rx, -ry);
-}
-
-// =============================================================================
-// -----------------------------------------------------------------------------
-void GLRenderer::paintEvent (QPaintEvent* ev)
-{	Q_UNUSED (ev)
-
-	makeCurrent();
-	m_virtWidth = zoom();
-	m_virtHeight = (m_height * m_virtWidth) / m_width;
-
-	initGLData();
-	drawGLScene();
-
-	const QPen textpen = getTextPen();
-	const QBrush polybrush (QColor (64, 192, 0, 128));
-	QPainter paint (this);
-	QFontMetrics metrics = QFontMetrics (QFont());
-	paint.setRenderHint (QPainter::HighQualityAntialiasing);
-
-	// If we wish to only draw the brick, stop here
-	if (isDrawOnly())
-		return;
-
-	if (m_camera != EFreeCamera && !isPicking())
-	{	// Paint the overlay image if we have one
-		const LDGLOverlay& overlay = m_overlays[m_camera];
-
-		if (overlay.img != null)
-		{	QPoint v0 = coordconv3_2 (m_overlays[m_camera].v0),
-					   v1 = coordconv3_2 (m_overlays[m_camera].v1);
-
-			QRect targRect (v0.x(), v0.y(), abs (v1.x() - v0.x()), abs (v1.y() - v0.y())),
-				  srcRect (0, 0, overlay.img->width(), overlay.img->height());
-			paint.drawImage (targRect, *overlay.img, srcRect);
-		}
-
-		// Paint the coordinates onto the screen.
-		str text = fmt (tr ("X: %1, Y: %2, Z: %3"), m_hoverpos[X], m_hoverpos[Y], m_hoverpos[Z]);
-
-		QFontMetrics metrics = QFontMetrics (font());
-		QRect textSize = metrics.boundingRect (0, 0, m_width, m_height, Qt::AlignCenter, text);
-
-		paint.setPen (getTextPen());
-		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);
-
-		// If we're drawing, draw the vertices onto the screen.
-		if (getEditMode() == EDrawMode)
-		{	int numverts = 4;
-
-			if (!m_rectdraw)
-				numverts = m_drawedVerts.size() + 1;
-
-			if (numverts > 0)
-			{	QPoint poly[4];
-				vertex polyverts[4];
-
-				if (!m_rectdraw)
-				{	uchar i = 0;
-
-					for (vertex& vert : m_drawedVerts)
-					{	poly[i] = coordconv3_2 (vert);
-						polyverts[i] = vert;
-						++i;
-					}
-
-					// Draw the cursor vertex as the last one in the list.
-					if (numverts <= 4)
-					{	poly[i] = coordconv3_2 (m_hoverpos);
-						polyverts[i] = m_hoverpos;
-					}
-					else
-					{	numverts = 4;
-					}
-				}
-				else
-				{	if (m_drawedVerts.size() > 0)
-					{	// Get vertex information from m_rectverts
-						for (int i = 0; i < numverts; ++i)
-						{	polyverts[i] = m_rectverts[i];
-							poly[i] = coordconv3_2 (polyverts[i]);
-						}
-					}
-					else
-					{	poly[0] = coordconv3_2 (m_hoverpos);
-						polyverts[0] = m_hoverpos;
-					}
-				}
-
-				// Draw the polygon-to-be
-				paint.setPen (linepen);
-				paint.setBrush (polybrush);
-				paint.drawPolygon (poly, numverts);
-
-				// Draw vertex blips
-				for (int i = 0; i < numverts; ++i)
-				{	QPoint& blip = poly[i];
-					drawBlip (paint, blip);
-
-					// Draw their coordinates
-					paint.drawText (blip.x(), blip.y() - 8, polyverts[i].stringRep (true));
-				}
-			}
-		}
-		elif (getEditMode() == 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_hoverpos));
-			else
-			{	QVector<vertex> verts, verts2;
-				const double dist0 = getCircleDrawDist (0),
-					dist1 = (m_drawedVerts.size() >= 2) ? getCircleDrawDist (1) : -1;
-				const int segs = 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[relX] = m_drawedVerts[0][relX] + (cos (i * angleUnit) * dist0);
-					v[relY] = m_drawedVerts[0][relY] + (sin (i * angleUnit) * dist0);
-					verts << v;
-
-					if (dist1 != -1)
-					{	v[relX] = m_drawedVerts[0][relX] + (cos (i * angleUnit) * dist1);
-						v[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]);
-					str label = str::number (dist0);
-					paint.setPen (textpen);
-					paint.drawText (origin.x() - (metrics.width (label) / 2), origin.y(), label);
-
-					if (m_drawedVerts.size() >= 2)
-					{	label = str::number (dist1);
-						paint.drawText (origin.x() - (metrics.width (label) / 2), origin.y() + metrics.height(), label);
-					}
-				}
-			}
-		}
-	}
-
-	// Camera icons
-	if (!isPicking())
-	{	// 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
-		for (CameraIcon& info : m_cameraIcons)
-		{	// Don't draw the free camera icon when in draw mode
-			if (&info == &m_cameraIcons[GL::EFreeCamera] && getEditMode() != ESelectMode)
-				continue;
-
-			paint.drawPixmap (info.destRect, *info.img, info.srcRect);
-		}
-
-		str fmtstr = tr ("%1 Camera");
-
-		// Draw a label for the current camera in the bottom left corner
-		{	const int margin = 4;
-
-			str label;
-			label = fmt (fmtstr, tr (g_CameraNames[camera()]));
-			paint.setPen (textpen);
-			paint.drawText (QPoint (margin, height() - (margin + metrics.descent())), label);
-		}
-
-		// Tool tips
-		if (m_drawToolTip)
-		{	if (m_cameraIcons[m_toolTipCamera].destRect.contains (m_pos) == false)
-				m_drawToolTip = false;
-			else
-			{	str label = fmt (fmtstr, tr (g_CameraNames[m_toolTipCamera]));
-				QToolTip::showText (m_globalpos, label);
-			}
-		}
-	}
-
-	// Message log
-	if (getMessageLog())
-	{	int y = 0;
-		const int margin = 2;
-		QColor penColor = getTextPen();
-
-		for (const MessageManager::Line& line : getMessageLog()->getLines())
-		{	penColor.setAlphaF (line.alpha);
-			paint.setPen (penColor);
-			paint.drawText (QPoint (margin, y + margin + metrics.ascent()), line.text);
-			y += metrics.height();
-		}
-	}
-
-	// If we're range-picking, draw a rectangle encompassing the selection area.
-	if (m_rangepick && !isPicking() && m_totalmove >= 10)
-	{	int x0 = m_rangeStart.x(),
-			y0 = m_rangeStart.y(),
-			x1 = m_pos.x(),
-			y1 = m_pos.y();
-
-		QRect rect (x0, y0, x1 - x0, y1 - y0);
-		QColor fillColor = (m_addpick ? "#40FF00" : "#00CCFF");
-		fillColor.setAlphaF (0.2f);
-
-		paint.setPen (m_thickBorderPen);
-		paint.setBrush (QBrush (fillColor));
-		paint.drawRect (rect);
-	}
-}
-
-// =============================================================================
-// -----------------------------------------------------------------------------
-void GLRenderer::drawBlip (QPainter& paint, QPoint pos) const
-{	QPen pen = m_thinBorderPen;
-	const int blipsize = 8;
-	pen.setWidth (1);
-	paint.setPen (pen);
-	paint.setBrush (QColor (64, 192, 0));
-	paint.drawEllipse (pos.x() - blipsize / 2, pos.y() - blipsize / 2, blipsize, blipsize);
-}
-
-// =============================================================================
-// -----------------------------------------------------------------------------
-QColor GLRenderer::getTextPen () const
-{	return m_darkbg ? Qt::white : Qt::black;
-}
-
-// =============================================================================
-// -----------------------------------------------------------------------------
-void GLRenderer::compileAllObjects()
-{	if (!getFile())
-		return;
-
-	// Compiling all is a big job, use a busy cursor
-	setCursor (Qt::BusyCursor);
-
-	m_knownVerts.clear();
-
-	for (LDObject* obj : getFile()->getObjects())
-		compileObject (obj);
-
-	// Compile axes
-	glDeleteLists (m_axeslist, 1);
-	m_axeslist = glGenLists (1);
-	glNewList (m_axeslist, GL_COMPILE);
-	glBegin (GL_LINES);
-
-	for (const LDGLAxis& ax : g_GLAxes)
-	{	qglColor (ax.col);
-		compileVertex (ax.vert);
-		compileVertex (-ax.vert);
-	}
-
-	glEnd();
-	glEndList();
-
-	setCursor (Qt::ArrowCursor);
-}
-
-// =============================================================================
-// -----------------------------------------------------------------------------
-void GLRenderer::compileSubObject (LDObject* obj, const GLenum gltype)
-{	glBegin (gltype);
-
-	const int numverts = (obj->getType() != LDObject::CondLine) ? obj->vertices() : 2;
-
-	if (g_glInvert == false)
-		for (int i = 0; i < numverts; ++i)
-			compileVertex (obj->getVertex (i));
-	else
-		for (int i = numverts - 1; i >= 0; --i)
-			compileVertex (obj->getVertex (i));
-
-	glEnd();
-}
-
-// =============================================================================
-// -----------------------------------------------------------------------------
-void GLRenderer::compileList (LDObject* obj, const GLRenderer::ListType list)
-{	setObjectColor (obj, list);
-
-	switch (obj->getType())
-	{	case LDObject::Line:
-			compileSubObject (obj, GL_LINES);
-			break;
-
-		case LDObject::CondLine:
-
-			// Draw conditional lines with a dash pattern - however, use a full
-			// line when drawing a pick list to make selecting them easier.
-			if (list != GL::PickList)
-			{	glLineStipple (1, 0x6666);
-				glEnable (GL_LINE_STIPPLE);
-			}
-
-			compileSubObject (obj, GL_LINES);
-
-			glDisable (GL_LINE_STIPPLE);
-			break;
-
-		case LDObject::Triangle:
-			compileSubObject (obj, GL_TRIANGLES);
-			break;
-
-		case LDObject::Quad:
-			compileSubObject (obj, GL_QUADS);
-			break;
-
-		case LDObject::Subfile:
-		{	LDSubfile* ref = static_cast<LDSubfile*> (obj);
-			QList<LDObject*> objs;
-
-			objs = ref->inlineContents (
-					   LDSubfile::DeepInline |
-					   LDSubfile::CacheInline |
-					   LDSubfile::RendererInline);
-			bool oldinvert = g_glInvert;
-
-			if (ref->getTransform().getDeterminant() < 0)
-				g_glInvert = !g_glInvert;
-
-			LDObject* prev = ref->prev();
-
-			if (prev && prev->getType() == LDObject::BFC && static_cast<LDBFC*> (prev)->type == LDBFC::InvertNext)
-				g_glInvert = !g_glInvert;
-
-		for (LDObject * obj : objs)
-			{	compileList (obj, list);
-				delete obj;
-			}
-
-			g_glInvert = oldinvert;
-		}
-		break;
-
-		default:
-			break;
-	}
-}
-
-// =============================================================================
-// -----------------------------------------------------------------------------
-void GLRenderer::compileVertex (const vertex& vrt)
-{	glVertex3d (vrt[X], -vrt[Y], -vrt[Z]);
-}
-
-// =============================================================================
-// -----------------------------------------------------------------------------
-void GLRenderer::clampAngle (double& angle) const
-{	while (angle < 0)
-		angle += 360.0;
-
-	while (angle > 360.0)
-		angle -= 360.0;
-}
-
-// =============================================================================
-// -----------------------------------------------------------------------------
-void GLRenderer::addDrawnVertex (vertex pos)
-{	// If we picked an already-existing vertex, stop drawing
-	if (getEditMode() != ECircleMode)
-	{	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) && ! (ev->buttons() & Qt::LeftButton),
-				   wasRight = (m_lastButtons & Qt::RightButton) && ! (ev->buttons() & Qt::RightButton),
-				   wasMid = (m_lastButtons & Qt::MidButton) && ! (ev->buttons() & Qt::MidButton);
-
-	if (m_panning)
-		m_panning = false;
-
-	if (wasLeft)
-	{	// Check if we selected a camera icon
-		if (!m_rangepick)
-		{	for (CameraIcon & info : m_cameraIcons)
-			{	if (info.destRect.contains (ev->pos()))
-				{	setCamera (info.cam);
-					goto end;
-				}
-			}
-		}
-
-		switch (getEditMode())
-		{	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_hoverpos);
-			} break;
-
-			case ECircleMode:
-			{	if (m_drawedVerts.size() == 3)
-				{	endDraw (true);
-					return;
-				}
-
-				addDrawnVertex (m_hoverpos);
-			} break;
-
-			case ESelectMode:
-			{	if (!isDrawOnly())
-				{	if (m_totalmove < 10)
-						m_rangepick = false;
-
-					if (!m_rangepick)
-						m_addpick = (m_keymods & Qt::ControlModifier);
-
-					if (m_totalmove < 10 || m_rangepick)
-						pick (ev->x(), ev->y());
-				}
-			} break;
-		}
-
-		m_rangepick = false;
-	}
-
-	if (wasMid && getEditMode() != ESelectMode && m_drawedVerts.size() < 4 && m_totalmove < 10)
-	{	// Find the closest vertex to our cursor
-		double mindist = 1024.0f;
-		vertex closest;
-		bool valid = false;
-
-		QPoint curspos = coordconv3_2 (m_hoverpos);
-
-		for (const vertex& pos3d: m_knownVerts)
-		{	QPoint pos2d = coordconv3_2 (pos3d);
-
-			// Measure squared distance
-			const double dx = abs (pos2d.x() - curspos.x()),
-						 dy = abs (pos2d.y() - curspos.y()),
-						 distsq = (dx * dx) + (dy * dy);
-
-			if (distsq >= 1024.0f) // 32.0f ** 2
-				continue; // too far away
-
-			if (distsq < mindist)
-			{	mindist = distsq;
-				closest = pos3d;
-				valid = true;
-
-				// If it's only 4 pixels away, I think we found our vertex now.
-				if (distsq <= 16.0f) // 4.0f ** 2
-					break;
-			}
-		}
-
-		if (valid)
-			addDrawnVertex (closest);
-	}
-
-	if (wasRight && !m_drawedVerts.isEmpty())
-	{	// Remove the last vertex
-		m_drawedVerts.removeLast();
-
-		if (m_drawedVerts.isEmpty())
-			m_rectdraw = false;
-	}
-
-end:
-	update();
-	m_totalmove = 0;
-}
-
-// =============================================================================
-// -----------------------------------------------------------------------------
-void GLRenderer::mousePressEvent (QMouseEvent* ev)
-{	m_totalmove = 0;
-
-	if (ev->modifiers() & Qt::ControlModifier)
-	{	m_rangepick = true;
-		m_rangeStart.setX (ev->x());
-		m_rangeStart.setY (ev->y());
-		m_addpick = (m_keymods & Qt::AltModifier);
-		ev->accept();
-	}
-
-	m_lastButtons = ev->buttons();
-}
-
-// =============================================================================
-// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
-// =============================================================================
-void GLRenderer::mouseMoveEvent (QMouseEvent* ev)
-{	int dx = ev->x() - m_pos.x();
-	int dy = ev->y() - m_pos.y();
-	m_totalmove += abs (dx) + abs (dy);
-
-	const bool left = ev->buttons() & Qt::LeftButton,
-			   mid = ev->buttons() & Qt::MidButton,
-			   shift = ev->modifiers() & Qt::ShiftModifier;
-
-	if (mid || (left && shift))
-	{	pan (X) += 0.03f * dx * (zoom() / 7.5f);
-		pan (Y) -= 0.03f * dy * (zoom() / 7.5f);
-		m_panning = true;
-	} elif (left && !m_rangepick && camera() == EFreeCamera)
-	{	rot (X) = rot (X) + dy;
-		rot (Y) = rot (Y) + dx;
-
-		clampAngle (rot (X));
-		clampAngle (rot (Y));
-	}
-
-	// Start the tool tip timer
-	if (!m_drawToolTip)
-		m_toolTipTimer->start (500);
-
-	// Update 2d position
-	m_pos = ev->pos();
-	m_globalpos = ev->globalPos();
-
-	// Calculate 3d position of the cursor
-	m_hoverpos = (camera() != EFreeCamera) ? coordconv2_3 (m_pos, true) : g_origin;
-
-	// Update rect vertices since m_hoverpos may have changed
-	updateRectVerts();
-
-	update();
-}
-
-// =============================================================================
-// -----------------------------------------------------------------------------
-void GLRenderer::keyPressEvent (QKeyEvent* ev)
-{	m_keymods = ev->modifiers();
-}
-
-// =============================================================================
-// -----------------------------------------------------------------------------
-void GLRenderer::keyReleaseEvent (QKeyEvent* ev)
-{	m_keymods = ev->modifiers();
-}
-
-// =============================================================================
-// -----------------------------------------------------------------------------
-void GLRenderer::wheelEvent (QWheelEvent* ev)
-{	makeCurrent();
-
-	zoomNotch (ev->delta() > 0);
-	zoom() = clamp (zoom(), 0.01, 10000.0);
-
-	update();
-	ev->accept();
-}
-
-// =============================================================================
-// -----------------------------------------------------------------------------
-void GLRenderer::leaveEvent (QEvent* ev)
-{	(void) ev;
-	m_drawToolTip = false;
-	m_toolTipTimer->stop();
-	update();
-}
-
-// =============================================================================
-// -----------------------------------------------------------------------------
-void GLRenderer::contextMenuEvent (QContextMenuEvent* ev)
-{	g_win->spawnContextMenu (ev->globalPos());
-}
-
-// =============================================================================
-// -----------------------------------------------------------------------------
-void GLRenderer::setCamera (const GLRenderer::EFixedCamera cam)
-{	m_camera = cam;
-	gl_camera = (int) cam;
-	g_win->updateEditModeActions();
-}
-
-// =============================================================================
-// -----------------------------------------------------------------------------
-void GLRenderer::pick (int mouseX, int mouseY)
-{	GLint viewport[4];
-	makeCurrent();
-
-	// Use particularly thick lines while picking ease up selecting lines.
-	glLineWidth (max<double> (gl_linethickness, 6.5f));
-
-	// Clear the selection if we do not wish to add to it.
-	if (!m_addpick)
-	{	QList<LDObject*> oldsel = selection();
-		getCurrentDocument()->clearSelection();
-
-		for (LDObject* obj : oldsel)
-			compileObject (obj);
-	}
-
-	setPicking (true);
-
-	// Paint the picking scene
-	glDisable (GL_DITHER);
-	glClearColor (1.0f, 1.0f, 1.0f, 1.0f);
-
-	drawGLScene();
-
-	glGetIntegerv (GL_VIEWPORT, viewport);
-
-	int x0 = mouseX,
-		  y0 = mouseY;
-	int x1, y1;
-
-	// Determine how big an area to read - with range picking, we pick by
-	// the area given, with single pixel picking, we use an 1 x 1 area.
-	if (m_rangepick)
-	{	x1 = m_rangeStart.x();
-		y1 = m_rangeStart.y();
-	}
-	else
-	{	x1 = x0 + 1;
-		y1 = y0 + 1;
-	}
-
-	// x0 and y0 must be less than x1 and y1, respectively.
-	if (x0 > x1)
-		qSwap (x0, x1);
-
-	if (y0 > y1)
-		qSwap (y0, y1);
-
-	// Clamp the values to ensure they're within bounds
-	x0 = max (0, x0);
-	y0 = max (0, y0);
-	x1 = min (x1, m_width);
-	y1 = min (y1, m_height);
-
-	const int areawidth = (x1 - x0);
-	const int areaheight = (y1 - y0);
-	const qint32 numpixels = areawidth * areaheight;
-
-	// Allocate space for the pixel data.
-	uchar* const pixeldata = new uchar[4 * numpixels];
-	uchar* pixelptr = &pixeldata[0];
-
-	assert (viewport[3] == m_height);
-
-	// Read pixels from the color buffer.
-	glReadPixels (x0, viewport[3] - y1, areawidth, areaheight, GL_RGBA, GL_UNSIGNED_BYTE, pixeldata);
-
-	LDObject* removedObj = null;
-
-	// Go through each pixel read and add them to the selection.
-	for (qint32 i = 0; i < numpixels; ++i)
-	{	qint32 idx =
-			(*(pixelptr + 0) * 0x10000) +
-			(*(pixelptr + 1) * 0x00100) +
-			(*(pixelptr + 2) * 0x00001);
-		pixelptr += 4;
-
-		if (idx == 0xFFFFFF)
-			continue; // White is background; skip
-
-		LDObject* obj = LDObject::fromID (idx);
-
-		// If this is an additive single pick and the object is currently selected,
-		// we remove it from selection instead.
-		if (!m_rangepick && m_addpick)
-		{	if (obj->isSelected())
-			{	obj->unselect();
-				removedObj = obj;
-				break;
-			}
-		}
-
-		obj->select();
-	}
-
-	delete[] pixeldata;
-
-	// Update everything now.
-	g_win->updateSelection();
-
-	// Recompile the objects now to update their color
-	for (LDObject* obj : selection())
-		compileObject (obj);
-
-	if (removedObj)
-		compileObject (removedObj);
-
-	// Restore line thickness
-	glLineWidth (gl_linethickness);
-
-	setPicking (false);
-	m_rangepick = false;
-	glEnable (GL_DITHER);
-
-	setBackground();
-	repaint();
-}
-
-// =============================================================================
-// -----------------------------------------------------------------------------
-void GLRenderer::setEditMode (EditMode const& a)
-{	m_EditMode = a;
-
-	switch (a)
-	{	case ESelectMode:
-		{	unsetCursor();
-			setContextMenuPolicy (Qt::DefaultContextMenu);
-		} break;
-
-		case EDrawMode:
-		case ECircleMode:
-		{	// Cannot draw into the free camera - use top instead.
-			if (m_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.
-			QList<LDObject*> priorsel = selection();
-			getCurrentDocument()->clearSelection();
-
-			for (LDObject* obj : priorsel)
-				compileObject (obj);
-
-			g_win->updateSelection();
-			m_drawedVerts.clear();
-		} break;
-	}
-
-	g_win->updateEditModeActions();
-	update();
-}
-
-void GLRenderer::setFile (LDDocument* const& a)
-{	m_File = a;
-
-	if (a != null)
-		initOverlaysFromObjects();
-}
-
-// =============================================================================
-// -----------------------------------------------------------------------------
-matrix GLRenderer::getCircleDrawMatrix (double scale)
-{	matrix transform = g_circleDrawTransforms[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;
-	QList<LDObject*> objs;
-
-	switch (getEditMode())
-	{	case EDrawMode:
-		{	if (m_rectdraw)
-			{	LDQuad* quad = new LDQuad;
-
-				// Copy the vertices from m_rectverts
-				updateRectVerts();
-
-				for (int i = 0; i < quad->vertices(); ++i)
-					quad->setVertex (i, m_rectverts[i]);
-
-				quad->setColor (maincolor);
-				objs << quad;
-			}
-			else
-			{	switch (verts.size())
-				{	case 1:
-					{	// 1 vertex - add a vertex object
-						LDVertex* obj = new LDVertex;
-						obj->pos = verts[0];
-						obj->setColor (maincolor);
-						objs << obj;
-					} break;
-
-					case 2:
-					{	// 2 verts - make a line
-						LDLine* obj = new LDLine (verts[0], verts[1]);
-						obj->setColor (edgecolor);
-						objs << obj;
-					} break;
-
-					case 3:
-					case 4:
-					{	LDObject* obj = (verts.size() == 3) ?
-							  static_cast<LDObject*> (new LDTriangle) :
-							  static_cast<LDObject*> (new LDQuad);
-
-						obj->setColor (maincolor);
-
-						for (int i = 0; i < obj->vertices(); ++i)
-							obj->setVertex (i, verts[i]);
-
-						objs << obj;
-					} break;
-				}
-			}
-		} break;
-
-		case ECircleMode:
-		{	const int segs = lores, divs = lores; // TODO: make customizable
-			double dist0 = getCircleDrawDist (0),
-				dist1 = getCircleDrawDist (1);
-			LDDocument* refFile = null;
-			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 (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, lores, lores, cmp.num))) == null)
-					{	refFile = generatePrimitive (::Ring, lores, lores, cmp.num);
-						refFile->setImplicit (false);
-					}
-
-					LDSubfile* ref = new 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[relX] = x0;
-				templ[relY] = y0;
-				templ[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[relX] += c0[i].x1();
-					v0[relY] += c0[i].y1();
-					v1[relX] += c0[i].x2();
-					v1[relY] += c0[i].y2();
-					v2[relX] += c1[i].x2();
-					v2[relY] += c1[i].y2();
-					v3[relX] += c1[i].x1();
-					v3[relY] += c1[i].y1();
-
-					LDQuad* q = new LDQuad (v0, v1, v2, v3);
-					q->setColor (maincolor);
-
-					// Ensure the quads always are BFC-front towards the camera
-					if (camera() % 3 <= 0)
-						q->invert();
-
-					objs << q;
-				}
-			}
-
-			if (circleOrDisc)
-			{	LDSubfile* ref = new LDSubfile;
-				ref->setFileInfo (refFile);
-				ref->setTransform (transform);
-				ref->setPosition (m_drawedVerts[0]);
-				ref->setColor (maincolor);
-				objs << ref;
-			}
-		} break;
-
-		case ESelectMode:
-		{	// this shouldn't happen
-			assert (false);
-			return;
-		} break;
-	}
-
-	if (objs.size() > 0)
-	{	g_win->beginAction (null);
-
-		for (LDObject* obj : objs)
-		{	getFile()->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);
-	const vertex& v1 = (m_drawedVerts.size() >= pos + 2) ? m_drawedVerts[pos + 1] : m_hoverpos;
-	Axis relX, relY;
-	getRelativeAxes (relX, relY);
-
-	const double dx = m_drawedVerts[0][relX] - v1[relX];
-	const double dy = m_drawedVerts[0][relY] - v1[relY];
-	return sqrt ((dx * dx) + (dy * dy));
-}
-
-// =============================================================================
-// -----------------------------------------------------------------------------
-void GLRenderer::getRelativeAxes (Axis& relX, Axis& relY) const
-{	const LDFixedCameraInfo* cam = &g_FixedCameras[m_camera];
-	relX = cam->axisX;
-	relY = cam->axisY;
-}
-
-// =============================================================================
-// -----------------------------------------------------------------------------
-static QList<vertex> getVertices (LDObject* obj)
-{	QList<vertex> verts;
-
-	if (obj->vertices() >= 2)
-	{	for (int i = 0; i < obj->vertices(); ++i)
-			verts << obj->getVertex (i);
-	} elif (obj->getType() == LDObject::Subfile)
-	{	LDSubfile* ref = static_cast<LDSubfile*> (obj);
-		QList<LDObject*> objs = ref->inlineContents (LDSubfile::DeepCacheInline);
-
-		for (LDObject* obj : objs)
-		{	verts << getVertices (obj);
-			delete obj;
-		}
-	}
-
-	return verts;
-}
-
-// =============================================================================
-// -----------------------------------------------------------------------------
-void GLRenderer::compileObject (LDObject* obj)
-{	deleteLists (obj);
-
-	for (const GL::ListType listType : g_glListTypes)
-	{	if (isDrawOnly() && listType != GL::NormalList)
-			continue;
-
-		GLuint list = glGenLists (1);
-		glNewList (list, GL_COMPILE);
-
-		obj->glLists[listType] = list;
-		compileList (obj, listType);
-
-		glEndList();
-	}
-
-	// Mark in known vertices of this object
-	QList<vertex> verts = getVertices (obj);
-	m_knownVerts << verts;
-	removeDuplicates (m_knownVerts);
-
-	obj->m_glinit = true;
-}
-
-// =============================================================================
-// -----------------------------------------------------------------------------
-uchar* GLRenderer::getScreencap (int& w, int& h)
-{	w = m_width;
-	h = m_height;
-	uchar* cap = new uchar[4 * w * h];
-
-	m_screencap = true;
-	update();
-	m_screencap = false;
-
-	// Capture the pixels
-	glReadPixels (0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, cap);
-
-	return cap;
-}
-
-// =============================================================================
-// -----------------------------------------------------------------------------
-void GLRenderer::slot_toolTipTimer()
-{	// We come here if the cursor has stayed in one place for longer than a
-	// a second. Check if we're holding it over a camera icon - if so, draw
-	// a tooltip.
-for (CameraIcon & icon : m_cameraIcons)
-	{	if (icon.destRect.contains (m_pos))
-		{	m_toolTipCamera = icon.cam;
-			m_drawToolTip = true;
-			update();
-			break;
-		}
-	}
-}
-
-// =============================================================================
-// -----------------------------------------------------------------------------
-void GLRenderer::deleteLists (LDObject* obj)
-{	// Delete the lists but only if they have been initialized
-	if (!obj->m_glinit)
-		return;
-
-for (const GL::ListType listType : g_glListTypes)
-		glDeleteLists (obj->glLists[listType], 1);
-
-	obj->m_glinit = false;
-}
-
-// =============================================================================
-// -----------------------------------------------------------------------------
-Axis GLRenderer::getCameraAxis (bool y, GLRenderer::EFixedCamera camid)
-{	if (camid == (GL::EFixedCamera) - 1)
-		camid = m_camera;
-
-	const LDFixedCameraInfo* cam = &g_FixedCameras[camid];
-	return (y) ? cam->axisY : cam->axisX;
-}
-
-// =============================================================================
-// -----------------------------------------------------------------------------
-bool GLRenderer::setupOverlay (EFixedCamera cam, str file, int x, int y, int w, int h)
-{	QImage* img = new QImage (file);
-	LDGLOverlay& info = getOverlay (cam);
-
-	if (img->isNull())
-	{	critical (tr ("Failed to load overlay image!"));
-		delete img;
-		return false;
-	}
-
-	delete info.img; // delete the old image
-
-	info.fname = file;
-	info.lw = w;
-	info.lh = h;
-	info.ox = x;
-	info.oy = y;
-	info.img = img;
-
-	if (info.lw == 0)
-		info.lw = (info.lh * img->width()) / img->height();
-	elif (info.lh == 0)
-		info.lh = (info.lw * img->height()) / img->width();
-
-	const Axis x2d = getCameraAxis (false, cam),
-		y2d = getCameraAxis (true, cam);
-	const double negXFac = g_FixedCameras[cam].negX ? -1 : 1,
-		negYFac = g_FixedCameras[cam].negY ? -1 : 1;
-
-	info.v0 = info.v1 = g_origin;
-	info.v0[x2d] = - (info.ox * info.lw * negXFac) / img->width();
-	info.v0[y2d] = (info.oy * info.lh * negYFac) / img->height();
-	info.v1[x2d] = info.v0[x2d] + info.lw;
-	info.v1[y2d] = info.v0[y2d] + info.lh;
-
-	// Set alpha of all pixels to 0.5
-	for (long i = 0; i < img->width(); ++i)
-		for (long j = 0; j < img->height(); ++j)
-		{	uint32 pixel = img->pixel (i, j);
-			img->setPixel (i, j, 0x80000000 | (pixel & 0x00FFFFFF));
-		}
-
-	updateOverlayObjects();
-	return true;
-}
-
-// =============================================================================
-// -----------------------------------------------------------------------------
-void GLRenderer::clearOverlay()
-{	if (camera() == EFreeCamera)
-		return;
-
-	LDGLOverlay& info = m_overlays[camera()];
-	delete info.img;
-	info.img = null;
-
-	updateOverlayObjects();
-}
-
-// =============================================================================
-// -----------------------------------------------------------------------------
-void GLRenderer::setDepthValue (double depth)
-{	assert (camera() < EFreeCamera);
-	m_depthValues[camera()] = depth;
-}
-
-// =============================================================================
-// -----------------------------------------------------------------------------
-double GLRenderer::getDepthValue() const
-{	assert (camera() < EFreeCamera);
-	return m_depthValues[camera()];
-}
-
-// =============================================================================
-// -----------------------------------------------------------------------------
-const char* GLRenderer::getCameraName() const
-{	return g_CameraNames[camera()];
-}
-
-// =============================================================================
-// -----------------------------------------------------------------------------
-LDGLOverlay& GLRenderer::getOverlay (int newcam)
-{	return m_overlays[newcam];
-}
-
-// =============================================================================
-// -----------------------------------------------------------------------------
-void GLRenderer::zoomNotch (bool inward)
-{	if (zoom() > 15)
-		zoom() *= inward ? 0.833f : 1.2f;
-	else
-		zoom() += inward ? -1.2f : 1.2f;
-}
-
-// =============================================================================
-// -----------------------------------------------------------------------------
-void GLRenderer::zoomToFit()
-{	if (getFile() == null || m_width == -1 || m_height == -1)
-	{	zoom() = 30.0f;
-		return;
-	}
-
-	bool lastfilled = false;
-	bool firstrun = true;
-	const uint32 white = 0xFFFFFFFF;
-	bool inward = true;
-	const int 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.
-	setPicking (true);
-
-	for (;;)
-	{	if (zoom() > 10000.0 || zoom() < 0.0)
-		{	// Obviously, there's nothing to draw if we get here.
-			// Default to 30.0f and break out.
-			zoom() = 30.0;
-			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 (int i = 0; i < w && !filled; ++i)
-			if (imgdata[i] != white || imgdata[((h - 1) * w) + i] != white)
-				filled = true;
-
-		// Left and right edges
-		for (int i = 0; i < h && !filled; ++i)
-			if (imgdata[i * w] != white || imgdata[(i * w) + w - 1] != white)
-				filled = true;
-
-		delete[] cap;
-
-		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;
-		}
-
-		lastfilled = filled;
-	}
-
-	setBackground();
-	setPicking (false);
-}
-
-// =============================================================================
-// -----------------------------------------------------------------------------
-void GLRenderer::zoomAllToFit()
-{	EFixedCamera oldcam = camera();
-
-	for (int i = 0; i < 7; ++i)
-	{	setCamera ((EFixedCamera) i);
-		zoomToFit();
-	}
-
-	setCamera (oldcam);
-}
-
-// =============================================================================
-// -----------------------------------------------------------------------------
-void GLRenderer::updateRectVerts()
-{	if (!m_rectdraw)
-		return;
-
-	if (m_drawedVerts.isEmpty())
-	{	for (int i = 0; i < 4; ++i)
-			m_rectverts[i] = m_hoverpos;
-
-		return;
-	}
-
-	vertex v0 = m_drawedVerts[0],
-		   v1 = (m_drawedVerts.size() >= 2) ? m_drawedVerts[1] : m_hoverpos;
-
-	const Axis ax = getCameraAxis (false),
-			   ay = getCameraAxis (true),
-			   az = (Axis) (3 - ax - ay);
-
-	for (int i = 0; i < 4; ++i)
-		m_rectverts[i][az] = getDepthValue();
-
-	m_rectverts[0][ax] = v0[ax];
-	m_rectverts[0][ay] = v0[ay];
-	m_rectverts[1][ax] = v1[ax];
-	m_rectverts[1][ay] = v0[ay];
-	m_rectverts[2][ax] = v1[ax];
-	m_rectverts[2][ay] = v1[ay];
-	m_rectverts[3][ax] = v0[ax];
-	m_rectverts[3][ay] = v1[ay];
-}
-
-// =============================================================================
-// -----------------------------------------------------------------------------
-void GLRenderer::mouseDoubleClickEvent (QMouseEvent* ev)
-{	if (!(ev->buttons() & Qt::LeftButton) || getEditMode() != ESelectMode)
-		return;
-
-	pick (ev->x(), ev->y());
-
-	if (selection().isEmpty())
-		return;
-
-	g_win->beginAction (null);
-	LDObject* obj = selection().first();
-	AddObjectDialog::staticDialog (obj->getType(), obj);
-	g_win->endAction();
-	ev->accept();
-}
-
-// =============================================================================
-// -----------------------------------------------------------------------------
-LDOverlay* GLRenderer::findOverlayObject (EFixedCamera cam)
-{	LDOverlay* ovlobj = null;
-
-	for (LDObject * obj : getFile()->getObjects())
-	{	if (obj->getType() == LDObject::Overlay && static_cast<LDOverlay*> (obj)->getCamera() == cam)
-		{	ovlobj = static_cast<LDOverlay*> (obj);
-			break;
-		}
-	}
-
-	return ovlobj;
-}
-
-// =============================================================================
-// -----------------------------------------------------------------------------
-// Read in overlays from the current file and update overlay info accordingly.
-// -----------------------------------------------------------------------------
-void GLRenderer::initOverlaysFromObjects()
-{	for (EFixedCamera cam : g_Cameras)
-	{	if (cam == EFreeCamera)
-			continue;
-
-		LDGLOverlay& meta = m_overlays[cam];
-		LDOverlay* ovlobj = findOverlayObject (cam);
-
-		if (!ovlobj && meta.img)
-		{	delete meta.img;
-			meta.img = null;
-		} elif (ovlobj && (!meta.img || meta.fname != ovlobj->getFileName()))
-			setupOverlay (cam, ovlobj->getFileName(), ovlobj->getX(),
-				ovlobj->getY(), ovlobj->getWidth(), ovlobj->getHeight());
-	}
-}
-
-// =============================================================================
-// -----------------------------------------------------------------------------
-void GLRenderer::updateOverlayObjects()
-{	for (EFixedCamera cam : g_Cameras)
-	{	if (cam == EFreeCamera)
-			continue;
-
-		LDGLOverlay& meta = m_overlays[cam];
-		LDOverlay* ovlobj = findOverlayObject (cam);
-
-		if (!meta.img && ovlobj)
-		{	// If this is the last overlay image, we need to remove the empty space after it as well.
-			LDObject* nextobj = ovlobj->next();
-
-			if (nextobj && nextobj->getType() == LDObject::Empty)
-			{	getFile()->forgetObject (nextobj);
-				delete nextobj;
-			}
-
-			// If the overlay object was there and the overlay itself is
-			// not, remove the object.
-			getFile()->forgetObject (ovlobj);
-			delete ovlobj;
-		} elif (meta.img && !ovlobj)
-		{	// Inverse case: image is there but the overlay object is
-			// not, thus create the object.
-			ovlobj = new LDOverlay;
-
-			// Find a suitable position to place this object. We want to place
-			// this into the header, which is everything up to the first scemantic
-			// object. If we find another overlay object, place this object after
-			// the last one found. Otherwise, place it before the first schemantic
-			// object and put an empty object after it (though don't do this if
-			// there was no schemantic elements at all)
-			int i, lastOverlay = -1;
-			bool found = false;
-
-			for (i = 0; i < getFile()->getObjectCount(); ++i)
-			{	LDObject* obj = getFile()->getObject (i);
-
-				if (obj->isScemantic())
-				{	found = true;
-					break;
-				}
-
-				if (obj->getType() == LDObject::Overlay)
-					lastOverlay = i;
-			}
-
-			if (lastOverlay != -1)
-				getFile()->insertObj (lastOverlay + 1, ovlobj);
-			else
-			{	getFile()->insertObj (i, ovlobj);
-
-				if (found)
-					getFile()->insertObj (i + 1, new LDEmpty);
-			}
-		}
-
-		if (meta.img && ovlobj)
-		{	ovlobj->setCamera (cam);
-			ovlobj->setFileName (meta.fname);
-			ovlobj->setX (meta.ox);
-			ovlobj->setY (meta.oy);
-			ovlobj->setWidth (meta.lw);
-			ovlobj->setHeight (meta.lh);
-		}
-	}
-
-	if (g_win->R() == this)
-		g_win->refresh();
-}

mercurial