Added rectifier interface

Tue, 07 May 2013 17:51:10 +0300

author
Santeri Piippo <crimsondusk64@gmail.com>
date
Tue, 07 May 2013 17:51:10 +0300
changeset 174
963697b36118
parent 173
2368e3c23ef3
child 175
b094d5e9d6e0

Added rectifier interface

extprogs.cpp file | annotate | diff | comparison | revisions
extprogs.h file | annotate | diff | comparison | revisions
gui.cpp file | annotate | diff | comparison | revisions
gui.h file | annotate | diff | comparison | revisions
history.h file | annotate | diff | comparison | revisions
icons/add-bfc.png file | annotate | diff | comparison | revisions
icons/rectifier.png file | annotate | diff | comparison | revisions
ldforge.qrc file | annotate | diff | comparison | revisions
mkqrc.sh file | annotate | diff | comparison | revisions
radiobox.cpp file | annotate | diff | comparison | revisions
zz_configDialog.cpp file | annotate | diff | comparison | revisions
--- a/extprogs.cpp	Tue May 07 16:12:15 2013 +0300
+++ b/extprogs.cpp	Tue May 07 17:51:10 2013 +0300
@@ -22,6 +22,7 @@
 #include <qdialog.h>
 #include <qdialogbuttonbox.h>
 #include <qspinbox.h>
+#include <qcheckbox.h>
 #include "common.h"
 #include "config.h"
 #include "misc.h"
@@ -37,30 +38,28 @@
 cfg (str, prog_coverer, "");
 cfg (str, prog_ytruder, "");
 cfg (str, prog_datheader, "");
-
-strconfig* g_extProgPaths[] = {
-	&prog_isecalc,
-	&prog_intersector,
-	&prog_coverer,
-	&prog_ytruder,
-	&prog_datheader,
-};
+cfg (str, prog_rectifier, "");
 
 const char* g_extProgNames[] = {
 	"Isecalc",
 	"Intersector",
 	"Coverer",
 	"Ytruder",
+	"Rectifier",
 	"DATHeader",
 };
 
 // =============================================================================
-static void noPathConfigured (const extprog prog) {
+static bool checkProgPath (str path, const extprog prog) {
+	if (~path)
+		return true;
+	
 	const char* name = g_extProgNames[prog];
 	
 	critical (fmt ("Couldn't run %s as no path has "
 		"been defined for it. Use the configuration dialog's External Programs "
 		"tab to define a path for %s.", name, name));
+	return false;
 }
 
 // =============================================================================
@@ -123,9 +122,12 @@
 }
 
 // =============================================================================
-void runUtilityProcess (extprog prog, QStringList argv) {
+void runUtilityProcess (extprog prog, str path, QString argvstr) {
 	QTemporaryFile input, output;
 	str inputname, outputname;
+	QStringList argv = argvstr.split (" ", QString::SkipEmptyParts);
+	
+	printf ("cmdline: %s %s\n", path.chars (), qchars (argvstr));
 	
 	if (!mkTempFile (input, inputname) || !mkTempFile (output, outputname))
 		return;
@@ -137,7 +139,7 @@
 	
 	// Begin!
 	proc.setStandardInputFile (inputname);
-	proc.start (prog_ytruder.value, argv);
+	proc.start (path, argv);
 	
 	// Write an enter - one is expected
 	char enter[2] = "\n";
@@ -148,6 +150,10 @@
 	// Wait while it runs
 	proc.waitForFinished ();
 	
+#ifndef RELASE
+	printf ("%s", qchars (QString (proc.readAllStandardOutput ())));
+#endif
+	
 	if (proc.exitStatus () == QProcess::CrashExit) {
 		processError (prog, proc);
 		return;
@@ -155,7 +161,11 @@
 }
 
 // =============================================================================
-void insertOutput (str fname, bool replace) {
+static void insertOutput (str fname, bool replace) {
+#ifndef RELEASE
+	QFile::copy (fname, "./output.dat");
+#endif
+	
 	// Read the output file
 	FILE* fp = fopen (fname, "r");
 	if (!fp) {
@@ -168,22 +178,9 @@
 		copies;
 	std::vector<ulong> indices;
 	
-	ulong idx = g_win->getInsertionPoint ();
-	
 	// If we replace the objects, delete the selection now.
-	if (replace) {
-		vector<ulong> indices;
-		vector<LDObject*> cache,
-			sel = g_win->sel ();
-		
-		for (LDObject* obj : sel) {
-			indices.push_back (obj->getIndex (g_curfile));
-			cache.push_back (obj->clone ());
-			
-			g_curfile->forgetObject (obj);
-			delete obj;
-		}
-	}
+	if (replace)
+		*cmb << g_win->deleteSelection ();
 	
 	// Insert the new objects
 	g_win->sel ().clear ();
@@ -193,16 +190,14 @@
 			continue;
 		}
 		
-		g_curfile->insertObj (idx, obj);
+		ulong idx = g_curfile->addObject (obj);
 		indices.push_back (idx);
 		copies.push_back (obj->clone ());
 		g_win->sel ().push_back (obj);
-		
-		++idx;
 	}
 	
 	if (indices.size() > 0)
-		cmb->paEntries.push_back (new AddHistory ({indices, copies}));
+		*cmb << new AddHistory ({indices, copies});
 	
 	if (cmb->paEntries.size () > 0)
 		History::addEntry (cmb);
@@ -213,12 +208,20 @@
 	g_win->refresh ();
 }
 
+QDialogButtonBox* makeButtonBox (QDialog* dlg) {
+	QDialogButtonBox* bbx_buttons = new QDialogButtonBox (QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
+	QWidget::connect (bbx_buttons, SIGNAL (accepted ()), dlg, SLOT (accept ()));
+	QWidget::connect (bbx_buttons, SIGNAL (rejected ()), dlg, SLOT (reject ()));
+	return bbx_buttons;
+}
+
 // =============================================================================
+// Interface for Ytruder
 MAKE_ACTION (ytruder, "Ytruder", "ytruder", "Extrude selected lines to a given plane", KEY (F8)) {
-	if (prog_ytruder.value.len () == 0) {
-		noPathConfigured (Ytruder);
+	setlocale (LC_ALL, "C");
+	
+	if (!checkProgPath (prog_ytruder, Ytruder))
 		return;
-	}
 	
 	QDialog* dlg = new QDialog (g_win);
 	
@@ -226,10 +229,6 @@
 	RadioBox* rb_axis = new RadioBox ("Axis", {"X", "Y", "Z"}, 0, Qt::Horizontal, dlg);
 	LabeledWidget<QDoubleSpinBox>* dsb_depth = new LabeledWidget<QDoubleSpinBox> ("Plane depth", dlg),
 		*dsb_condAngle = new LabeledWidget<QDoubleSpinBox> ("Conditional line threshold", dlg);
-	QDialogButtonBox* bbx_buttons = new QDialogButtonBox (QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
-	
-	QWidget::connect (bbx_buttons, SIGNAL (accepted ()), dlg, SLOT (accept ()));
-	QWidget::connect (bbx_buttons, SIGNAL (rejected ()), dlg, SLOT (reject ()));
 	
 	rb_axis->setValue (Y);
 	dsb_depth->w ()->setMinimum (-10000.0);
@@ -242,13 +241,14 @@
 	layout->addWidget (rb_axis);
 	layout->addWidget (dsb_depth);
 	layout->addWidget (dsb_condAngle);
-	layout->addWidget (bbx_buttons);
+	layout->addWidget (makeButtonBox (dlg));
 	
 	dlg->setWindowIcon (getIcon ("extrude"));
 	
 	if (!dlg->exec ())
 		return;
 	
+	// Read the user's choices
 	const enum modetype { Distance, Symmetry, Projection, Radial } mode = (modetype) rb_mode->value ();
 	const Axis axis = (Axis) rb_axis->value ();
 	const double depth = dsb_depth->w ()->value (),
@@ -257,15 +257,76 @@
 	QTemporaryFile indat, outdat;
 	str inDATName, outDATName;
 	
+	// Make temp files for the input and output files
 	if (!mkTempFile (indat, inDATName) || !mkTempFile (outdat, outDATName))
 		return;
 	
-	QStringList argv ({(axis == X) ? "-x" : (axis == Y) ? "-y" : "-z",
+	// Compose the command-line arguments
+	str argv = fmt ("%s %s %f -a %f %s %s",
+		(axis == X) ? "-x" : (axis == Y) ? "-y" : "-z",
 		(mode == Distance) ? "-d" : (mode == Symmetry) ? "-s" : (mode == Projection) ? "-p" : "-r",
-		fmt ("%f", depth), "-a", fmt ("%f", condAngle), inDATName, outDATName
-	});
+		depth, condAngle, inDATName.chars (), outDATName.chars ());
 	
 	writeSelection (inDATName);
-	runUtilityProcess (Ytruder, argv);
+	runUtilityProcess (Ytruder, prog_ytruder, argv);
 	insertOutput (outDATName, false);
+}
+
+// =============================================================================
+// Rectifier interface
+MAKE_ACTION (rectifier, "Rectifier", "rectifier", "Optimizes quads into rect primitives.", (0)) {
+	setlocale (LC_ALL, "C");
+	
+	if (!checkProgPath (prog_rectifier, Rectifier))
+		return;
+	
+	QDialog* dlg = new QDialog (g_win);
+	QCheckBox* cb_condense = new QCheckBox ("Condense triangles to quads"),
+		*cb_subst = new QCheckBox ("Substitute rect primitives"),
+		*cb_condlineCheck = new QCheckBox ("Don't replace quads with adj. condlines"),
+		*cb_colorize = new QCheckBox ("Colorize resulting objects");
+	LabeledWidget<QDoubleSpinBox>* dsb_coplthres = new LabeledWidget<QDoubleSpinBox> ("Coplanarity threshold", dlg);
+	
+	dsb_coplthres->w ()->setMinimum (0.0f);
+	dsb_coplthres->w ()->setMaximum (360.0f);
+	dsb_coplthres->w ()->setDecimals (3);
+	dsb_coplthres->w ()->setValue (0.95f);
+	cb_condense->setChecked (true);
+	cb_subst->setChecked (true);
+	
+	QVBoxLayout* layout = new QVBoxLayout (dlg);
+	layout->addWidget (cb_condense);
+	layout->addWidget (cb_subst);
+	layout->addWidget (cb_condlineCheck);
+	layout->addWidget (cb_colorize);
+	layout->addWidget (dsb_coplthres);
+	layout->addWidget (makeButtonBox (dlg));
+	
+	if (!dlg->exec ())
+		return;
+	
+	const bool condense = cb_condense->isChecked (),
+		subst = cb_subst->isChecked (),
+		condlineCheck = cb_condlineCheck->isChecked (),
+		colorize = cb_colorize->isChecked ();
+	const double coplthres = dsb_coplthres->w ()->value ();
+	
+	QTemporaryFile indat, outdat;
+	str inDATName, outDATName;
+	
+	// Make temp files for the input and output files
+	if (!mkTempFile (indat, inDATName) || !mkTempFile (outdat, outDATName))
+		return;
+	
+	// Compose arguments
+	str argv = fmt ("%s %s %s %s -t %f %s %s",
+		(condense == false) ? "-q" : "",
+		(subst == false) ? "-r" : "",
+		(condlineCheck) ? "-a" : "",
+		(colorize) ? "-c" : "",
+		coplthres, inDATName.chars (), outDATName.chars ());
+	
+	writeSelection (inDATName);
+	runUtilityProcess (Rectifier, prog_rectifier, argv);
+	insertOutput (outDATName, true);
 }
\ No newline at end of file
--- a/extprogs.h	Tue May 07 16:12:15 2013 +0300
+++ b/extprogs.h	Tue May 07 17:51:10 2013 +0300
@@ -26,6 +26,7 @@
 	Intersector,
 	Coverer,
 	Ytruder,
+	Rectifier,
 	DATHeader
 };
 
--- a/gui.cpp	Tue May 07 16:12:15 2013 +0300
+++ b/gui.cpp	Tue May 07 17:51:10 2013 +0300
@@ -257,6 +257,7 @@
 	
 	initMenu ("E&xternal Programs");
 	addMenuAction ("ytruder");
+	addMenuAction ("rectifier");
 	
 #ifndef RELEASE
 	// Debug menu
@@ -407,6 +408,7 @@
 	
 	initSingleToolBar ("External Programs");
 	addToolBarAction ("ytruder");
+	addToolBarAction ("rectifier");
 	
 	initSingleToolBar ("Groups");
 	
@@ -946,6 +948,25 @@
 }
 
 // =============================================================================
+DelHistory* ForgeWindow::deleteSelection () {
+	vector<ulong> indices;
+	vector<LDObject*> cache, sel = g_win->sel ();
+	
+	for (LDObject* obj : sel) {
+		indices.push_back (obj->getIndex (g_curfile));
+		cache.push_back (obj->clone ());
+		
+		g_curfile->forgetObject (obj);
+		delete obj;
+	}
+	
+	if (indices.size () > 0)
+		return new DelHistory (indices, cache);
+	
+	return null;
+}
+
+// =============================================================================
 void ObjectList::contextMenuEvent (QContextMenuEvent* ev) {
 	g_win->spawnContextMenu (ev->globalPos ());
 }
--- a/gui.h	Tue May 07 16:12:15 2013 +0300
+++ b/gui.h	Tue May 07 17:51:10 2013 +0300
@@ -35,6 +35,7 @@
 class ForgeWindow;
 class color;
 class QSplitter;
+class DelHistory;
 
 // Stuff for dialogs
 #define IMPLEMENT_DIALOG_BUTTONS \
@@ -136,6 +137,7 @@
 	LDObject::Type uniformSelectedType ();
 	void scrollToSelection ();
 	void spawnContextMenu (const QPoint pos);
+	DelHistory* deleteSelection ();
 	GLRenderer* R () { return m_renderer; }
 	std::vector<LDObject*>& sel () { return m_sel; }
 	void setQuickColorMeta (std::vector<quickColorMetaEntry>& quickColorMeta) {
--- a/history.h	Tue May 07 16:12:15 2013 +0300
+++ b/history.h	Tue May 07 17:51:10 2013 +0300
@@ -192,6 +192,11 @@
 	std::vector<HistoryEntry*> paEntries;
 	
 	ComboHistory (std::vector<HistoryEntry*> paEntries) : paEntries (paEntries) {}
+	
+	ComboHistory& operator<< (HistoryEntry* entry) {
+		paEntries.push_back (entry);
+		return *this;
+	}
 };
 
 // =============================================================================
Binary file icons/add-bfc.png has changed
Binary file icons/rectifier.png has changed
--- a/ldforge.qrc	Tue May 07 16:12:15 2013 +0300
+++ b/ldforge.qrc	Tue May 07 17:51:10 2013 +0300
@@ -70,6 +70,7 @@
 	<file>./icons/quad.png</file>
 	<file>./icons/quad-split.png</file>
 	<file>./icons/radial.png</file>
+	<file>./icons/rectifier.png</file>
 	<file>./icons/redo.png</file>
 	<file>./icons/round-coords.png</file>
 	<file>./icons/screencap.png</file>
@@ -86,8 +87,6 @@
 	<file>./icons/vertex.png</file>
 	<file>./icons/visibility.png</file>
 	<file>./icons/ytruder.png</file>
-	<file>./docs/test2.html</file>
-	<file>./docs/test.html</file>
 	<file>LICENSE</file>
 </qresource>
 </RCC>
--- a/mkqrc.sh	Tue May 07 16:12:15 2013 +0300
+++ b/mkqrc.sh	Tue May 07 17:51:10 2013 +0300
@@ -1,12 +1,23 @@
 #!/bin/bash
 
 QRCFILE=ldforge.qrc
+FILES=$(echo ./icons/*.* LICENSE)
+
 printf "" > $QRCFILE
 
 printf "<!DOCTYPE RCC>\n<RCC version=\"1.0\">\n<qresource>\n" >> $QRCFILE
 
-for img in ./icons/*.* ./docs/*.* LICENSE; do
-	printf "\t<file>$img</file>\n" >> $QRCFILE
+# Make sure that whatever goes to QRC is added to the repo.
+# I keep forgetting to do this myself.
+for line in $(hg status $FILES |grep "?"); do
+	if [ "$line" != "?" ]; then
+		echo "hg add $line"
+		hg add $line;
+	fi
+done
+
+for f in $FILES; do
+	printf "\t<file>$f</file>\n" >> $QRCFILE
 done
 
 printf "</qresource>\n</RCC>\n" >> $QRCFILE
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/radiobox.cpp	Tue May 07 17:51:10 2013 +0300
@@ -0,0 +1,97 @@
+/*
+ *  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 <qboxlayout.h>
+#include <qradiobutton.h>
+#include "radiobox.h"
+
+static QBoxLayout::Direction makeDirection (Qt::Orientation orient, bool invert = false) {
+	return (orient == (invert ? Qt::Vertical : Qt::Horizontal)) ? QBoxLayout::LeftToRight : QBoxLayout::TopToBottom;
+}
+
+void RadioBox::init (Qt::Orientation orient) {
+	dir = makeDirection (orient);
+	
+	buttonGroup = new QButtonGroup;
+	currentId = 0;
+	coreLayout = null;
+	
+	coreLayout = new QBoxLayout (makeDirection (orient, true));
+	setLayout (coreLayout);
+	
+	// Init the first row with a break
+	rowBreak ();
+	
+	connect (buttonGroup, SIGNAL (buttonPressed (QAbstractButton*)), this, SLOT (slot_buttonPressed (QAbstractButton*)));
+	connect (buttonGroup, SIGNAL (buttonPressed (int)), this, SLOT (slot_buttonPressed (int)));
+}
+
+RadioBox::RadioBox (const QString& title, initlist<char const*> entries, int const defaultId,
+	const Qt::Orientation orient, QWidget* parent) : QGroupBox (title, parent), defaultId (defaultId)
+{
+	init (orient);
+	
+	for (char const* entry : entries)
+		addButton (entry);
+}
+
+void RadioBox::rowBreak () {
+	QBoxLayout* newLayout = new QBoxLayout (dir);
+	currentLayout = newLayout;
+	layouts.push_back (newLayout);
+	
+	coreLayout->addLayout (newLayout);
+}
+
+void RadioBox::addButton (const char* entry) {
+	QRadioButton* button = new QRadioButton (entry);
+	addButton (button);
+}
+
+void RadioBox::addButton (QRadioButton* button) {
+	bool const selectThis = (currentId == defaultId);
+	
+	objects.push_back (button);
+	buttonGroup->addButton (button, currentId++);
+	currentLayout->addWidget (button);
+	
+	if (selectThis)
+		button->setChecked (true);
+}
+
+RadioBox& RadioBox::operator<< (QRadioButton* button) {
+	addButton (button);
+	return *this;
+}
+
+RadioBox& RadioBox::operator<< (const char* entry) {
+	addButton (entry);
+	return *this;
+}
+
+void RadioBox::setCurrentRow (uint row) {
+	currentLayout = layouts[row];
+}
+
+void RadioBox::slot_buttonPressed (int btn) {
+	emit sig_buttonPressed (btn);
+}
+
+void RadioBox::slot_buttonPressed (QAbstractButton* btn) {
+	emit sig_buttonPressed (btn);
+}
\ No newline at end of file
--- a/zz_configDialog.cpp	Tue May 07 16:12:15 2013 +0300
+++ b/zz_configDialog.cpp	Tue May 07 17:51:10 2013 +0300
@@ -338,6 +338,7 @@
 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 // =============================================================================
 extern_cfg (str, prog_ytruder);
+extern_cfg (str, prog_rectifier);
 static const struct extProgInfo {
 	const char* const name, *iconname;
 	strconfig* const path;
@@ -345,13 +346,14 @@
 	mutable QPushButton* setPathButton;
 } g_extProgInfo[] = {
 	{ "Ytruder", "ytruder", &prog_ytruder, null, null },
+	{ "Rectifier", "rectifier", &prog_rectifier, null, null },
 };
 
 void ConfigDialog::initExtProgTab () {
 	QWidget* tab = new QWidget;
 	QGridLayout* pathsLayout = new QGridLayout;
 	QGroupBox* pathsBox = new QGroupBox ("Paths", this);
-	QVBoxLayout* layout = new QVBoxLayout;
+	QVBoxLayout* layout = new QVBoxLayout (this);
 	
 	ulong row = 0;
 	for (const extProgInfo& info : g_extProgInfo) {
@@ -372,6 +374,7 @@
 		pathsLayout->addWidget (progLabel, row, 1);
 		pathsLayout->addWidget (input, row, 2);
 		pathsLayout->addWidget (setPathButton, row, 3);
+		++row;
 	}
 	
 	pathsBox->setLayout (pathsLayout);
@@ -460,7 +463,7 @@
 			idx = quickColorItems.size();
 		
 		quickColorMeta.insert (quickColorMeta.begin() + idx, entry);
-		entry = &quickColorMeta[idx];
+		entry = quickColorMeta[idx];
 	}
 	
 	updateQuickColorList (entry);
@@ -732,6 +735,10 @@
 			for (int j = 0; j < 4; ++j)
 				g_GridInfo[i].confs[j]->value = dlg.dsb_gridData[i][j]->value ();
 		
+		// Ext program settings
+		for (const extProgInfo& info : g_extProgInfo)
+			*info.path = info.input->text ();
+		
 		// Save the config
 		config::save ();
 		

mercurial