Tue, 14 May 2013 00:52:20 +0300
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 |
--- 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