extprogs.cpp

changeset 174
963697b36118
parent 168
96691a009dff
child 175
b094d5e9d6e0
equal deleted inserted replaced
173:2368e3c23ef3 174:963697b36118
20 #include <qtemporaryfile.h> 20 #include <qtemporaryfile.h>
21 #include <qeventloop.h> 21 #include <qeventloop.h>
22 #include <qdialog.h> 22 #include <qdialog.h>
23 #include <qdialogbuttonbox.h> 23 #include <qdialogbuttonbox.h>
24 #include <qspinbox.h> 24 #include <qspinbox.h>
25 #include <qcheckbox.h>
25 #include "common.h" 26 #include "common.h"
26 #include "config.h" 27 #include "config.h"
27 #include "misc.h" 28 #include "misc.h"
28 #include "extprogs.h" 29 #include "extprogs.h"
29 #include "gui.h" 30 #include "gui.h"
35 cfg (str, prog_isecalc, ""); 36 cfg (str, prog_isecalc, "");
36 cfg (str, prog_intersector, ""); 37 cfg (str, prog_intersector, "");
37 cfg (str, prog_coverer, ""); 38 cfg (str, prog_coverer, "");
38 cfg (str, prog_ytruder, ""); 39 cfg (str, prog_ytruder, "");
39 cfg (str, prog_datheader, ""); 40 cfg (str, prog_datheader, "");
40 41 cfg (str, prog_rectifier, "");
41 strconfig* g_extProgPaths[] = {
42 &prog_isecalc,
43 &prog_intersector,
44 &prog_coverer,
45 &prog_ytruder,
46 &prog_datheader,
47 };
48 42
49 const char* g_extProgNames[] = { 43 const char* g_extProgNames[] = {
50 "Isecalc", 44 "Isecalc",
51 "Intersector", 45 "Intersector",
52 "Coverer", 46 "Coverer",
53 "Ytruder", 47 "Ytruder",
48 "Rectifier",
54 "DATHeader", 49 "DATHeader",
55 }; 50 };
56 51
57 // ============================================================================= 52 // =============================================================================
58 static void noPathConfigured (const extprog prog) { 53 static bool checkProgPath (str path, const extprog prog) {
54 if (~path)
55 return true;
56
59 const char* name = g_extProgNames[prog]; 57 const char* name = g_extProgNames[prog];
60 58
61 critical (fmt ("Couldn't run %s as no path has " 59 critical (fmt ("Couldn't run %s as no path has "
62 "been defined for it. Use the configuration dialog's External Programs " 60 "been defined for it. Use the configuration dialog's External Programs "
63 "tab to define a path for %s.", name, name)); 61 "tab to define a path for %s.", name, name));
62 return false;
64 } 63 }
65 64
66 // ============================================================================= 65 // =============================================================================
67 static void processError (const extprog prog, QProcess& proc) { 66 static void processError (const extprog prog, QProcess& proc) {
68 const char* name = g_extProgNames[prog]; 67 const char* name = g_extProgNames[prog];
121 120
122 fclose (fp); 121 fclose (fp);
123 } 122 }
124 123
125 // ============================================================================= 124 // =============================================================================
126 void runUtilityProcess (extprog prog, QStringList argv) { 125 void runUtilityProcess (extprog prog, str path, QString argvstr) {
127 QTemporaryFile input, output; 126 QTemporaryFile input, output;
128 str inputname, outputname; 127 str inputname, outputname;
128 QStringList argv = argvstr.split (" ", QString::SkipEmptyParts);
129
130 printf ("cmdline: %s %s\n", path.chars (), qchars (argvstr));
129 131
130 if (!mkTempFile (input, inputname) || !mkTempFile (output, outputname)) 132 if (!mkTempFile (input, inputname) || !mkTempFile (output, outputname))
131 return; 133 return;
132 134
133 QProcess proc; 135 QProcess proc;
135 // Init stdin 137 // Init stdin
136 FILE* stdinfp = fopen (inputname, "w"); 138 FILE* stdinfp = fopen (inputname, "w");
137 139
138 // Begin! 140 // Begin!
139 proc.setStandardInputFile (inputname); 141 proc.setStandardInputFile (inputname);
140 proc.start (prog_ytruder.value, argv); 142 proc.start (path, argv);
141 143
142 // Write an enter - one is expected 144 // Write an enter - one is expected
143 char enter[2] = "\n"; 145 char enter[2] = "\n";
144 enter[1] = '\0'; 146 enter[1] = '\0';
145 fwrite (enter, 1, sizeof enter, stdinfp); 147 fwrite (enter, 1, sizeof enter, stdinfp);
146 fflush (stdinfp); 148 fflush (stdinfp);
147 149
148 // Wait while it runs 150 // Wait while it runs
149 proc.waitForFinished (); 151 proc.waitForFinished ();
150 152
153 #ifndef RELASE
154 printf ("%s", qchars (QString (proc.readAllStandardOutput ())));
155 #endif
156
151 if (proc.exitStatus () == QProcess::CrashExit) { 157 if (proc.exitStatus () == QProcess::CrashExit) {
152 processError (prog, proc); 158 processError (prog, proc);
153 return; 159 return;
154 } 160 }
155 } 161 }
156 162
157 // ============================================================================= 163 // =============================================================================
158 void insertOutput (str fname, bool replace) { 164 static void insertOutput (str fname, bool replace) {
165 #ifndef RELEASE
166 QFile::copy (fname, "./output.dat");
167 #endif
168
159 // Read the output file 169 // Read the output file
160 FILE* fp = fopen (fname, "r"); 170 FILE* fp = fopen (fname, "r");
161 if (!fp) { 171 if (!fp) {
162 critical (fmt ("Couldn't open temporary file %s for reading.\n", fname.chars ())); 172 critical (fmt ("Couldn't open temporary file %s for reading.\n", fname.chars ()));
163 return; 173 return;
166 ComboHistory* cmb = new ComboHistory ({}); 176 ComboHistory* cmb = new ComboHistory ({});
167 std::vector<LDObject*> objs = loadFileContents (fp, null), 177 std::vector<LDObject*> objs = loadFileContents (fp, null),
168 copies; 178 copies;
169 std::vector<ulong> indices; 179 std::vector<ulong> indices;
170 180
171 ulong idx = g_win->getInsertionPoint ();
172
173 // If we replace the objects, delete the selection now. 181 // If we replace the objects, delete the selection now.
174 if (replace) { 182 if (replace)
175 vector<ulong> indices; 183 *cmb << g_win->deleteSelection ();
176 vector<LDObject*> cache,
177 sel = g_win->sel ();
178
179 for (LDObject* obj : sel) {
180 indices.push_back (obj->getIndex (g_curfile));
181 cache.push_back (obj->clone ());
182
183 g_curfile->forgetObject (obj);
184 delete obj;
185 }
186 }
187 184
188 // Insert the new objects 185 // Insert the new objects
189 g_win->sel ().clear (); 186 g_win->sel ().clear ();
190 for (LDObject* obj : objs) { 187 for (LDObject* obj : objs) {
191 if (!obj->isSchemantic ()) { 188 if (!obj->isSchemantic ()) {
192 delete obj; 189 delete obj;
193 continue; 190 continue;
194 } 191 }
195 192
196 g_curfile->insertObj (idx, obj); 193 ulong idx = g_curfile->addObject (obj);
197 indices.push_back (idx); 194 indices.push_back (idx);
198 copies.push_back (obj->clone ()); 195 copies.push_back (obj->clone ());
199 g_win->sel ().push_back (obj); 196 g_win->sel ().push_back (obj);
200
201 ++idx;
202 } 197 }
203 198
204 if (indices.size() > 0) 199 if (indices.size() > 0)
205 cmb->paEntries.push_back (new AddHistory ({indices, copies})); 200 *cmb << new AddHistory ({indices, copies});
206 201
207 if (cmb->paEntries.size () > 0) 202 if (cmb->paEntries.size () > 0)
208 History::addEntry (cmb); 203 History::addEntry (cmb);
209 else 204 else
210 delete cmb; 205 delete cmb;
211 206
212 fclose (fp); 207 fclose (fp);
213 g_win->refresh (); 208 g_win->refresh ();
214 } 209 }
215 210
216 // ============================================================================= 211 QDialogButtonBox* makeButtonBox (QDialog* dlg) {
212 QDialogButtonBox* bbx_buttons = new QDialogButtonBox (QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
213 QWidget::connect (bbx_buttons, SIGNAL (accepted ()), dlg, SLOT (accept ()));
214 QWidget::connect (bbx_buttons, SIGNAL (rejected ()), dlg, SLOT (reject ()));
215 return bbx_buttons;
216 }
217
218 // =============================================================================
219 // Interface for Ytruder
217 MAKE_ACTION (ytruder, "Ytruder", "ytruder", "Extrude selected lines to a given plane", KEY (F8)) { 220 MAKE_ACTION (ytruder, "Ytruder", "ytruder", "Extrude selected lines to a given plane", KEY (F8)) {
218 if (prog_ytruder.value.len () == 0) { 221 setlocale (LC_ALL, "C");
219 noPathConfigured (Ytruder); 222
220 return; 223 if (!checkProgPath (prog_ytruder, Ytruder))
221 } 224 return;
222 225
223 QDialog* dlg = new QDialog (g_win); 226 QDialog* dlg = new QDialog (g_win);
224 227
225 RadioBox* rb_mode = new RadioBox ("Extrusion mode", {"Distance", "Symmetry", "Projection", "Radial"}, 0, Qt::Horizontal, dlg); 228 RadioBox* rb_mode = new RadioBox ("Extrusion mode", {"Distance", "Symmetry", "Projection", "Radial"}, 0, Qt::Horizontal, dlg);
226 RadioBox* rb_axis = new RadioBox ("Axis", {"X", "Y", "Z"}, 0, Qt::Horizontal, dlg); 229 RadioBox* rb_axis = new RadioBox ("Axis", {"X", "Y", "Z"}, 0, Qt::Horizontal, dlg);
227 LabeledWidget<QDoubleSpinBox>* dsb_depth = new LabeledWidget<QDoubleSpinBox> ("Plane depth", dlg), 230 LabeledWidget<QDoubleSpinBox>* dsb_depth = new LabeledWidget<QDoubleSpinBox> ("Plane depth", dlg),
228 *dsb_condAngle = new LabeledWidget<QDoubleSpinBox> ("Conditional line threshold", dlg); 231 *dsb_condAngle = new LabeledWidget<QDoubleSpinBox> ("Conditional line threshold", dlg);
229 QDialogButtonBox* bbx_buttons = new QDialogButtonBox (QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
230
231 QWidget::connect (bbx_buttons, SIGNAL (accepted ()), dlg, SLOT (accept ()));
232 QWidget::connect (bbx_buttons, SIGNAL (rejected ()), dlg, SLOT (reject ()));
233 232
234 rb_axis->setValue (Y); 233 rb_axis->setValue (Y);
235 dsb_depth->w ()->setMinimum (-10000.0); 234 dsb_depth->w ()->setMinimum (-10000.0);
236 dsb_depth->w ()->setMaximum (10000.0); 235 dsb_depth->w ()->setMaximum (10000.0);
237 dsb_depth->w ()->setDecimals (3); 236 dsb_depth->w ()->setDecimals (3);
240 QVBoxLayout* layout = new QVBoxLayout (dlg); 239 QVBoxLayout* layout = new QVBoxLayout (dlg);
241 layout->addWidget (rb_mode); 240 layout->addWidget (rb_mode);
242 layout->addWidget (rb_axis); 241 layout->addWidget (rb_axis);
243 layout->addWidget (dsb_depth); 242 layout->addWidget (dsb_depth);
244 layout->addWidget (dsb_condAngle); 243 layout->addWidget (dsb_condAngle);
245 layout->addWidget (bbx_buttons); 244 layout->addWidget (makeButtonBox (dlg));
246 245
247 dlg->setWindowIcon (getIcon ("extrude")); 246 dlg->setWindowIcon (getIcon ("extrude"));
248 247
249 if (!dlg->exec ()) 248 if (!dlg->exec ())
250 return; 249 return;
251 250
251 // Read the user's choices
252 const enum modetype { Distance, Symmetry, Projection, Radial } mode = (modetype) rb_mode->value (); 252 const enum modetype { Distance, Symmetry, Projection, Radial } mode = (modetype) rb_mode->value ();
253 const Axis axis = (Axis) rb_axis->value (); 253 const Axis axis = (Axis) rb_axis->value ();
254 const double depth = dsb_depth->w ()->value (), 254 const double depth = dsb_depth->w ()->value (),
255 condAngle = dsb_condAngle->w ()->value (); 255 condAngle = dsb_condAngle->w ()->value ();
256 256
257 QTemporaryFile indat, outdat; 257 QTemporaryFile indat, outdat;
258 str inDATName, outDATName; 258 str inDATName, outDATName;
259 259
260 // Make temp files for the input and output files
260 if (!mkTempFile (indat, inDATName) || !mkTempFile (outdat, outDATName)) 261 if (!mkTempFile (indat, inDATName) || !mkTempFile (outdat, outDATName))
261 return; 262 return;
262 263
263 QStringList argv ({(axis == X) ? "-x" : (axis == Y) ? "-y" : "-z", 264 // Compose the command-line arguments
265 str argv = fmt ("%s %s %f -a %f %s %s",
266 (axis == X) ? "-x" : (axis == Y) ? "-y" : "-z",
264 (mode == Distance) ? "-d" : (mode == Symmetry) ? "-s" : (mode == Projection) ? "-p" : "-r", 267 (mode == Distance) ? "-d" : (mode == Symmetry) ? "-s" : (mode == Projection) ? "-p" : "-r",
265 fmt ("%f", depth), "-a", fmt ("%f", condAngle), inDATName, outDATName 268 depth, condAngle, inDATName.chars (), outDATName.chars ());
266 });
267 269
268 writeSelection (inDATName); 270 writeSelection (inDATName);
269 runUtilityProcess (Ytruder, argv); 271 runUtilityProcess (Ytruder, prog_ytruder, argv);
270 insertOutput (outDATName, false); 272 insertOutput (outDATName, false);
271 } 273 }
274
275 // =============================================================================
276 // Rectifier interface
277 MAKE_ACTION (rectifier, "Rectifier", "rectifier", "Optimizes quads into rect primitives.", (0)) {
278 setlocale (LC_ALL, "C");
279
280 if (!checkProgPath (prog_rectifier, Rectifier))
281 return;
282
283 QDialog* dlg = new QDialog (g_win);
284 QCheckBox* cb_condense = new QCheckBox ("Condense triangles to quads"),
285 *cb_subst = new QCheckBox ("Substitute rect primitives"),
286 *cb_condlineCheck = new QCheckBox ("Don't replace quads with adj. condlines"),
287 *cb_colorize = new QCheckBox ("Colorize resulting objects");
288 LabeledWidget<QDoubleSpinBox>* dsb_coplthres = new LabeledWidget<QDoubleSpinBox> ("Coplanarity threshold", dlg);
289
290 dsb_coplthres->w ()->setMinimum (0.0f);
291 dsb_coplthres->w ()->setMaximum (360.0f);
292 dsb_coplthres->w ()->setDecimals (3);
293 dsb_coplthres->w ()->setValue (0.95f);
294 cb_condense->setChecked (true);
295 cb_subst->setChecked (true);
296
297 QVBoxLayout* layout = new QVBoxLayout (dlg);
298 layout->addWidget (cb_condense);
299 layout->addWidget (cb_subst);
300 layout->addWidget (cb_condlineCheck);
301 layout->addWidget (cb_colorize);
302 layout->addWidget (dsb_coplthres);
303 layout->addWidget (makeButtonBox (dlg));
304
305 if (!dlg->exec ())
306 return;
307
308 const bool condense = cb_condense->isChecked (),
309 subst = cb_subst->isChecked (),
310 condlineCheck = cb_condlineCheck->isChecked (),
311 colorize = cb_colorize->isChecked ();
312 const double coplthres = dsb_coplthres->w ()->value ();
313
314 QTemporaryFile indat, outdat;
315 str inDATName, outDATName;
316
317 // Make temp files for the input and output files
318 if (!mkTempFile (indat, inDATName) || !mkTempFile (outdat, outDATName))
319 return;
320
321 // Compose arguments
322 str argv = fmt ("%s %s %s %s -t %f %s %s",
323 (condense == false) ? "-q" : "",
324 (subst == false) ? "-r" : "",
325 (condlineCheck) ? "-a" : "",
326 (colorize) ? "-c" : "",
327 coplthres, inDATName.chars (), outDATName.chars ());
328
329 writeSelection (inDATName);
330 runUtilityProcess (Rectifier, prog_rectifier, argv);
331 insertOutput (outDATName, true);
332 }

mercurial