Added image overlays, these are offset and scaled photos drawn on top of the part model to help getting part data from pictures.

Tue, 14 May 2013 00:52:20 +0300

author
Santeri Piippo <crimsondusk64@gmail.com>
date
Tue, 14 May 2013 00:52:20 +0300
changeset 195
7a776f6b0d2a
parent 194
cfe9ae5f1124
child 196
47f4f4543152

Added image overlays, these are offset and scaled photos drawn on top of the part model to help getting part data from pictures.

icons/overlay-clear.png file | annotate | diff | comparison | revisions
icons/overlay.png file | annotate | diff | comparison | revisions
ldforge.qrc file | annotate | diff | comparison | revisions
src/docs.cpp file | annotate | diff | comparison | revisions
src/docs.h file | annotate | diff | comparison | revisions
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_actions.cpp file | annotate | diff | comparison | revisions
Binary file icons/overlay-clear.png has changed
Binary file icons/overlay.png has changed
--- a/ldforge.qrc	Mon May 13 19:53:22 2013 +0300
+++ b/ldforge.qrc	Tue May 14 00:52:20 2013 +0300
@@ -65,6 +65,8 @@
 	<file>./icons/move-z-neg.png</file>
 	<file>./icons/move-z-pos.png</file>
 	<file>./icons/open-recent.png</file>
+	<file>./icons/overlay-clear.png</file>
+	<file>./icons/overlay.png</file>
 	<file>./icons/palette.png</file>
 	<file>./icons/paste.png</file>
 	<file>./icons/qt.png</file>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/docs.cpp	Tue May 14 00:52:20 2013 +0300
@@ -0,0 +1,67 @@
+/*
+ *  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 <QDialog>
+#include <QTextEdit>
+#include <QDialogButtonBox>
+#include <QBoxLayout>
+#include "common.h"
+
+class DocumentViewer : public QDialog {
+public:
+	explicit DocumentViewer (QWidget* parent = null, Qt::WindowFlags f = 0) : QDialog (parent, f) {
+		te_text = new QTextEdit (this);
+		te_text->setMinimumSize (QSize (400, 300));
+		te_text->setReadOnly (true);
+		
+		QDialogButtonBox* bbx_buttons = new QDialogButtonBox (QDialogButtonBox::Close);
+		QVBoxLayout* layout = new QVBoxLayout (this);
+		layout->addWidget (te_text);
+		layout->addWidget (bbx_buttons);
+		
+		connect (bbx_buttons, SIGNAL (rejected ()), this, SLOT (reject ()));
+	}
+	
+	void setText (const char* text) {
+		te_text->setText (text);
+	}
+	
+private:
+	QTextEdit* te_text;
+};
+
+const char* g_docs_overlays =
+	"<h1>Overlay images</h1><br />"
+	"<p>" APPNAME " supports drawing transparent images over the part model. This "
+	"can be used to have, for instance, a photo of the part overlaid on top of the "
+	"model and use it for drawing curves somewhat accurately.</p>"
+	"<p>For this purpose, a specific photo has to be taken of the part; it should "
+	"represent the part as true as possible to the actual camera used for editing. "
+	"The image should be taken from straight above the part, at as an orthogonal "
+	"angle as possible. It is recommended to take a lot of pictures this way and "
+	"select the best candidate.</p>"
+	"<p>The image should then be cropped with the knowledge of the image's LDU "
+	"dimensions in mind. The offset should then be identified in the image in pixels.</p>"
+	"<p>Finally, use the \"Set Overlay Image\" dialog and fill in the details. The "
+	"overlay image should then be ready for use.";
+
+void showDocumentation (const char* text) {
+	DocumentViewer dlg;
+	dlg.setText (text);
+	dlg.exec ();
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/docs.h	Tue May 14 00:52:20 2013 +0300
@@ -0,0 +1,26 @@
+/*
+ *  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/>.
+ */
+
+#ifndef DOCS_H
+#define DOCS_H
+
+extern const char* g_docs_overlays;
+
+void showDocumentation (const char* text);
+
+#endif // DOCS_H
\ No newline at end of file
--- a/src/gldraw.cpp	Mon May 13 19:53:22 2013 +0300
+++ b/src/gldraw.cpp	Tue May 14 00:52:20 2013 +0300
@@ -16,8 +16,13 @@
  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#include <QtGui>
 #include <QGLWidget>
+#include <qdialog.h>
+#include <qlineedit.h>
+#include <qspinbox.h>
+#include <qdialogbuttonbox.h>
+#include <QFileDialog>
+#include <qevent.h>
 #include <GL/glu.h>
 #include "common.h"
 #include "config.h"
@@ -28,6 +33,8 @@
 #include "gui.h"
 #include "misc.h"
 #include "history.h"
+#include "radiobox.h"
+#include "docs.h"
 
 static double g_objOffset[3];
 
@@ -44,6 +51,14 @@
 	{ { 0, -1, 0 }, Z, Y, false, true },
 };
 
+struct overlayMeta {
+	vertex v0, v1;
+	ushort ox, oy;
+	double lw, lh;
+	str fname;
+	QImage* img;
+} g_overlays[6];
+
 cfg (str, gl_bgcolor, "#CCCCD9");
 cfg (str, gl_maincolor, "#707078");
 cfg (float, gl_maincolor_alpha, 1.0);
@@ -112,6 +127,9 @@
 		info->cam = cam;
 	}
 	
+	for (int i = 0; i < 6; ++i)
+		g_overlays[i].img = null;
+	
 	calcCameraIcons ();
 }
 
@@ -467,6 +485,17 @@
 	m_hoverpos = g_origin;
 	
 	if (m_camera != Free) {
+		// Paint the overlay image if we have one
+		const overlayMeta& overlay = g_overlays[m_camera];
+		if (overlay.img != null) {
+			QPoint v0 = coordconv3_2 (g_overlays[m_camera].v0),
+				v1 = coordconv3_2 (g_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);
+		}
+		
 		// Calculate 3d position of the cursor
 		m_hoverpos = coordconv2_3 (m_pos, true);
 		
@@ -1205,4 +1234,144 @@
 void GLRenderer::deleteLists (LDObject* obj) {
 	for (const GL::ListType listType : g_glListTypes)
 		glDeleteLists (obj->glLists[listType], 1);
+}
+
+// =============================================================================
+Axis GLRenderer::cameraAxis (bool y) {
+	const staticCameraMeta* cam = &g_staticCameras[m_camera];
+	return (y) ? cam->axisY : cam->axisX;
+}
+
+// =============================================================================
+OverlayDialog::OverlayDialog (QWidget* parent, Qt::WindowFlags f) : QDialog (parent, f) {
+	rb_camera = new RadioBox ("Camera", {}, 0, Qt::Horizontal, this);
+	
+	for (int i = 0; i < 6; ++i) {
+		if (i == 3)
+			rb_camera->rowBreak ();
+		
+		rb_camera->addButton (g_CameraNames[i]);
+	}
+	
+	GL::Camera cam = g_win->R ()->camera ();
+	if (cam != GL::Free)
+		rb_camera->setValue ((int) cam);
+	
+	QGroupBox* gb_image = new QGroupBox ("Image", this);
+	
+	QLabel* lb_fpath = new QLabel ("File:");
+	le_fpath = new QLineEdit;
+	le_fpath->setFocus ();
+	
+	QLabel* lb_ofs = new QLabel ("Origin:");
+	btn_fpath = new QPushButton;
+	btn_fpath->setIcon (getIcon ("folder"));
+	connect (btn_fpath, SIGNAL (clicked ()), this, SLOT (slot_fpath ()));
+	
+	sb_ofsx = new QSpinBox;
+	sb_ofsy = new QSpinBox;
+	sb_ofsx->setRange (0, 10000);
+	sb_ofsy->setRange (0, 10000);
+	
+	QLabel* lb_dimens = new QLabel ("Dimensions (LDU):");
+	dsb_lwidth = new QDoubleSpinBox;
+	dsb_lheight = new QDoubleSpinBox;
+	dsb_lwidth->setRange (0.0f, 10000.0f);
+	dsb_lheight->setRange (0.0f, 10000.0f);
+	
+	QDialogButtonBox* bbx_buttons = makeButtonBox (*this);
+	bbx_buttons->addButton (QDialogButtonBox::Help);
+	connect (bbx_buttons, SIGNAL (helpRequested ()), this, SLOT (slot_help()));
+	
+	QHBoxLayout* fpathlayout = new QHBoxLayout;
+	fpathlayout->addWidget (lb_fpath);
+	fpathlayout->addWidget (le_fpath);
+	fpathlayout->addWidget (btn_fpath);
+	
+	QGridLayout* metalayout = new QGridLayout;
+	metalayout->addWidget (lb_ofs,			0, 0);
+	metalayout->addWidget (sb_ofsx,		0, 1);
+	metalayout->addWidget (sb_ofsy,		0, 2);
+	metalayout->addWidget (lb_dimens,		1, 0);
+	metalayout->addWidget (dsb_lwidth,	1, 1);
+	metalayout->addWidget (dsb_lheight,	1, 2);
+	
+	QVBoxLayout* imagelayout = new QVBoxLayout (gb_image);
+	imagelayout->addLayout (fpathlayout);
+	imagelayout->addLayout (metalayout);
+	
+	QVBoxLayout* layout = new QVBoxLayout (this);
+	layout->addWidget (rb_camera);
+	layout->addWidget (gb_image);
+	layout->addWidget (bbx_buttons);
+}
+	
+str			OverlayDialog::fpath		() const { return le_fpath->text (); }
+ushort		OverlayDialog::ofsx		() const { return sb_ofsx->value (); }
+ushort		OverlayDialog::ofsy		() const { return sb_ofsy->value (); }
+double		OverlayDialog::lwidth		() const { return dsb_lwidth->value (); }
+double		OverlayDialog::lheight		() const { return dsb_lheight->value (); }
+GL::Camera	OverlayDialog::camera		() const { return (GL::Camera) rb_camera->value (); }
+
+void OverlayDialog::slot_fpath () const {
+	le_fpath->setText (QFileDialog::getOpenFileName (null, "Overlay image"));
+}
+
+void OverlayDialog::slot_help () const {
+	showDocumentation (g_docs_overlays);
+}
+
+void GLRenderer::setupOverlay () {
+	if (camera () == Free)
+		return;
+	
+	OverlayDialog dlg;
+	
+	if (!dlg.exec ())
+		return;
+	
+	QImage* img = new QImage (dlg.fpath ().chars ());
+	overlayMeta& info = g_overlays[camera ()];
+	
+	if (img->isNull ()) {
+		critical ("Failed to load overlay image!");
+		delete img;
+		return;
+	}
+	
+	delete info.img; // delete the old image
+		
+	info.fname = dlg.fpath ();
+	info.lw = dlg.lwidth ();
+	info.lh = dlg.lheight ();
+	info.ox = dlg.ofsx ();
+	info.oy = dlg.ofsy ();
+	info.img = img;
+	
+	const Axis x2d = cameraAxis (false),
+		y2d = cameraAxis (true);
+	
+	double negXFac = g_staticCameras[m_camera].negX ? -1 : 1,
+		negYFac = g_staticCameras[m_camera].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));
+	}
+}
+
+void GLRenderer::clearOverlay () {
+	if (camera () == Free)
+		return;
+	
+	overlayMeta& info = g_overlays[camera ()];
+	delete info.img;
 }
\ No newline at end of file
--- a/src/gldraw.h	Mon May 13 19:53:22 2013 +0300
+++ b/src/gldraw.h	Tue May 14 00:52:20 2013 +0300
@@ -21,9 +21,15 @@
 
 #include <QGLWidget>
 #include <qtimer.h>
+#include <qdialog.h>
 #include "common.h"
 #include "ldtypes.h"
 
+class RadioBox;
+class QDoubleSpinBox;
+class QSpinBox;
+class QLineEdit;
+
 // =============================================================================
 // GLRenderer
 // 
@@ -52,6 +58,8 @@
 	
 	void		beginPlaneDraw		();
 	Camera		camera				() const { return m_camera; }
+	Axis		cameraAxis			(bool y);
+	void		clearOverlay		();
 	void		compileObject		(LDObject* obj);
 	void		compileAllObjects	();
 	void		endPlaneDraw		(bool accept);
@@ -63,9 +71,8 @@
 	uchar*		screencap			(ushort& w, ushort& h);
 	void		setBackground		();
 	void		setCamera			(const GLRenderer::Camera cam);
+	void		setupOverlay		();
 	void		setZoom				(const double zoom) { m_zoom = zoom; }
-	void		setWireframe		(const bool set);
-	bool		wireframe			() const;
 	double		zoom				() const { return m_zoom; }
 	
 	static void	deleteLists			(LDObject* obj);
@@ -123,4 +130,29 @@
 	GL::BFCBackList,
 };
 
+class OverlayDialog : public QDialog {
+	Q_OBJECT
+	
+public:
+	explicit OverlayDialog (QWidget* parent = null, Qt::WindowFlags f = 0);
+	
+	str			fpath		() const;
+	ushort		ofsx		() const;
+	ushort		ofsy		() const;
+	double		lwidth		() const;
+	double		lheight		() const;
+	GL::Camera	camera		() const;
+	
+public slots:
+	void slot_fpath () const;
+	void slot_help () const;
+	
+private:
+	RadioBox* rb_camera;
+	QPushButton* btn_fpath;
+	QLineEdit* le_fpath;
+	QSpinBox* sb_ofsx, *sb_ofsy;
+	QDoubleSpinBox* dsb_lwidth, *dsb_lheight;
+};
+
 #endif // GLDRAW_H
\ No newline at end of file
--- a/src/gui.cpp	Mon May 13 19:53:22 2013 +0300
+++ b/src/gui.cpp	Tue May 14 00:52:20 2013 +0300
@@ -162,6 +162,9 @@
 	addMenuAction ("axes");				// Draw Axes
 	addMenuAction ("wireframe");			// Wireframe
 	menu->addSeparator ();					// -----
+	addMenuAction ("setOverlay");			// Set Overlay Image
+	addMenuAction ("clearOverlay");		// Clear Overlay Image
+	menu->addSeparator ();					// -----
 	addMenuAction ("screencap");			// Screencap Part
 	addMenuAction ("showHistory");		// Edit History
 	
@@ -886,6 +889,8 @@
 	if (single)
 		contextMenu->addAction (findAction ("setContents"));
 	contextMenu->addAction (findAction ("makeBorders"));
+	contextMenu->addAction (findAction ("setOverlay"));
+	contextMenu->addAction (findAction ("clearOverlay"));
 	
 	contextMenu->exec (pos);
 }
@@ -975,7 +980,7 @@
 			return *meta.qAct;
 	
 	fprintf (stderr, "%s: couldn't find action named `%s'!\n", __func__, name.chars ());
-	assert (false);
+	abort ();
 	return null;
 }
 
--- a/src/gui_actions.cpp	Mon May 13 19:53:22 2013 +0300
+++ b/src/gui_actions.cpp	Tue May 14 00:52:20 2013 +0300
@@ -415,7 +415,15 @@
 }
 
 // =========================================================================================================================================
-MAKE_ACTION (wireframe, "Toggle Wireframe", "wireframe", "Toggle wireframe view", (0)) {
+MAKE_ACTION (wireframe, "Wireframe", "wireframe", "Toggle wireframe view", (0)) {
 	gl_wireframe = !gl_wireframe;
 	g_win->R ()->refresh ();
+}
+
+MAKE_ACTION (setOverlay, "Set Overlay Image", "overlay", "Set an overlay image", (0)) {
+	g_win->R ()->setupOverlay ();
+}
+
+MAKE_ACTION (clearOverlay, "Clear Overlay Image", "overlay-clear", "Clear the overlay image.", (0)) {
+	g_win->R ()->clearOverlay ();
 }
\ No newline at end of file

mercurial