# HG changeset patch # User Santeri Piippo # Date 1367938270 -10800 # Node ID 963697b3611842fbcf8ca9963e54966fe87176f8 # Parent 2368e3c23ef3762d720f8d5b52b539f698f19dea Added rectifier interface diff -r 2368e3c23ef3 -r 963697b36118 extprogs.cpp --- 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 #include #include +#include #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 indices; - ulong idx = g_win->getInsertionPoint (); - // If we replace the objects, delete the selection now. - if (replace) { - vector indices; - vector 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* dsb_depth = new LabeledWidget ("Plane depth", dlg), *dsb_condAngle = new LabeledWidget ("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* dsb_coplthres = new LabeledWidget ("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 diff -r 2368e3c23ef3 -r 963697b36118 extprogs.h --- 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 }; diff -r 2368e3c23ef3 -r 963697b36118 gui.cpp --- 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 indices; + vector 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 ()); } diff -r 2368e3c23ef3 -r 963697b36118 gui.h --- 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& sel () { return m_sel; } void setQuickColorMeta (std::vector& quickColorMeta) { diff -r 2368e3c23ef3 -r 963697b36118 history.h --- 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 paEntries; ComboHistory (std::vector paEntries) : paEntries (paEntries) {} + + ComboHistory& operator<< (HistoryEntry* entry) { + paEntries.push_back (entry); + return *this; + } }; // ============================================================================= diff -r 2368e3c23ef3 -r 963697b36118 icons/add-bfc.png Binary file icons/add-bfc.png has changed diff -r 2368e3c23ef3 -r 963697b36118 icons/rectifier.png Binary file icons/rectifier.png has changed diff -r 2368e3c23ef3 -r 963697b36118 ldforge.qrc --- 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 @@ ./icons/quad.png ./icons/quad-split.png ./icons/radial.png + ./icons/rectifier.png ./icons/redo.png ./icons/round-coords.png ./icons/screencap.png @@ -86,8 +87,6 @@ ./icons/vertex.png ./icons/visibility.png ./icons/ytruder.png - ./docs/test2.html - ./docs/test.html LICENSE diff -r 2368e3c23ef3 -r 963697b36118 mkqrc.sh --- 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 "\n\n\n" >> $QRCFILE -for img in ./icons/*.* ./docs/*.* LICENSE; do - printf "\t$img\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$f\n" >> $QRCFILE done printf "\n\n" >> $QRCFILE \ No newline at end of file diff -r 2368e3c23ef3 -r 963697b36118 radiobox.cpp --- /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 . + */ + +#include +#include +#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 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 diff -r 2368e3c23ef3 -r 963697b36118 zz_configDialog.cpp --- 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 ();