85 "tab to define a path for %1.", name)); |
87 "tab to define a path for %1.", name)); |
86 return false; |
88 return false; |
87 } |
89 } |
88 |
90 |
89 // ============================================================================= |
91 // ============================================================================= |
90 static void processError (const extprog prog, QProcess& proc) { |
92 static str processErrorString (const extprog prog, QProcess& proc) { |
91 const char* name = g_extProgNames[prog]; |
93 switch (proc.error()) { |
92 str errmsg; |
|
93 |
|
94 switch (proc.error ()) { |
|
95 case QProcess::FailedToStart: |
94 case QProcess::FailedToStart: |
96 errmsg = fmt ("Failed to launch %1. Check that you have set the proper path " |
95 return "Failed to start (check your permissions)"; |
97 "to %1 and that you have the proper permissions to launch it.", name); |
|
98 break; |
|
99 |
96 |
100 case QProcess::Crashed: |
97 case QProcess::Crashed: |
101 errmsg = fmt ("%1 crashed.", name); |
98 return "Crashed."; |
102 break; |
|
103 |
99 |
104 case QProcess::WriteError: |
100 case QProcess::WriteError: |
105 case QProcess::ReadError: |
101 case QProcess::ReadError: |
106 errmsg = fmt ("I/O error while interacting with %1.", name); |
102 return "I/O error."; |
107 break; |
|
108 |
103 |
109 case QProcess::UnknownError: |
104 case QProcess::UnknownError: |
110 errmsg = fmt ("Unknown error occurred while executing %1.", name); |
105 return "Unknown error"; |
111 break; |
|
112 |
106 |
113 case QProcess::Timedout: |
107 case QProcess::Timedout: |
114 errmsg = fmt ("%1 timed out.", name); |
108 return fmt( "Timed out (30 seconds)" ); |
115 break; |
109 } |
116 } |
110 |
117 |
111 return ""; |
118 critical (errmsg); |
|
119 } |
112 } |
120 |
113 |
121 // ============================================================================= |
114 // ============================================================================= |
122 static bool mkTempFile (QTemporaryFile& tmp, str& fname) { |
115 static bool mkTempFile (QTemporaryFile& tmp, str& fname) { |
123 if (!tmp.open ()) |
116 if (!tmp.open ()) |
176 } |
169 } |
177 |
170 |
178 writeObjects (objects, fname); |
171 writeObjects (objects, fname); |
179 } |
172 } |
180 |
173 |
181 // ============================================================================= |
174 void waitForProcess( QProcess* proc ) { |
182 void runUtilityProcess (extprog prog, str path, QString argvstr) { |
175 proc->waitForFinished(); |
|
176 |
|
177 #if 0 |
|
178 int msecs = 30000; |
|
179 int msectic = 10; |
|
180 |
|
181 for (int i = 0; i < msecs / msectic; ++i) { |
|
182 if (proc->waitForFinished (msectic)) |
|
183 return; |
|
184 |
|
185 |
|
186 } |
|
187 #endif // 0 |
|
188 } |
|
189 |
|
190 // ============================================================================= |
|
191 bool runUtilityProcess (extprog prog, str path, str argvstr) { |
183 QTemporaryFile input, output; |
192 QTemporaryFile input, output; |
184 str inputname, outputname; |
193 str inputname, outputname; |
185 QStringList argv = argvstr.split (" ", QString::SkipEmptyParts); |
194 QStringList argv = argvstr.split (" ", QString::SkipEmptyParts); |
186 |
195 |
187 print ("cmdline: %1 %2\n", path, argvstr); |
|
188 |
|
189 #ifndef _WIN32 |
196 #ifndef _WIN32 |
190 if (g_extProgWine[prog]) { |
197 if (*g_extProgWine[prog]) { |
191 argv.insert (0, path); |
198 argv.insert (0, path); |
192 path = "wine"; |
199 path = "wine"; |
193 } |
200 } |
194 #endif // _WIN32 |
201 #endif // _WIN32 |
195 |
202 |
196 if (!mkTempFile (input, inputname) || !mkTempFile (output, outputname)) |
203 print ("cmdline: %1 %2\n", path, argv.join (" ")); |
197 return; |
204 |
|
205 // Temporary files for stdin and stdout |
|
206 if( !mkTempFile( input, inputname ) || !mkTempFile( output, outputname )) |
|
207 return false; |
198 |
208 |
199 QProcess proc; |
209 QProcess proc; |
200 |
210 |
201 // Init stdin |
211 // Init stdin |
202 File stdinfp (inputname, File::Write); |
212 File stdinfp (inputname, File::Write); |
203 |
213 |
204 // Begin! |
214 // Begin! |
205 proc.setStandardInputFile (inputname); |
215 proc.setStandardInputFile (inputname); |
206 proc.start (path, argv); |
216 proc.start (path, argv); |
207 |
217 |
208 // Write an enter - one is expected |
218 // Write an enter, the utility tools all expect one |
209 stdinfp.write ("\n"); |
219 stdinfp.write ("\n"); |
210 |
220 |
211 // Wait while it runs |
221 // Wait while it runs |
212 proc.waitForFinished (); |
222 waitForProcess( &proc ); |
213 |
223 |
214 #ifndef RELEASE |
224 #ifndef RELEASE |
215 printf ("%s", qchars (QString (proc.readAllStandardOutput ()))); |
225 print ("%1", str (proc.readAllStandardOutput ())); |
216 #endif // RELEASE |
226 #endif // RELEASE |
217 |
227 |
218 if (proc.exitStatus () == QProcess::CrashExit) { |
228 str err = ""; |
219 processError (prog, proc); |
229 |
220 return; |
230 if ( proc.exitStatus() != QProcess::NormalExit ) |
221 } |
231 err = processErrorString (prog, proc); |
|
232 |
|
233 // Check the return code |
|
234 if (proc.exitCode() != 0) |
|
235 err = fmt ("Program exited abnormally (return code %1).", proc.exitCode()); |
|
236 |
|
237 if (err.length() > 0) { |
|
238 critical (fmt ("%1 failed: %2\n", g_extProgNames[prog], err)); |
|
239 return false; |
|
240 } |
|
241 |
|
242 return true; |
222 } |
243 } |
223 |
244 |
224 // ================================================================================================ |
245 // ================================================================================================ |
225 static void insertOutput (str fname, bool replace, vector<short> colorsToReplace) { |
246 static void insertOutput (str fname, bool replace, vector<short> colorsToReplace) { |
226 #ifndef RELEASE |
247 #ifndef RELEASE |
401 setlocale (LC_ALL, "C"); |
428 setlocale (LC_ALL, "C"); |
402 |
429 |
403 if (!checkProgPath (prog_intersector, Intersector)) |
430 if (!checkProgPath (prog_intersector, Intersector)) |
404 return; |
431 return; |
405 |
432 |
406 QDialog dlg; |
433 QDialog* dlg = new QDialog; |
407 |
434 Ui::IntersectorUI ui; |
408 LabeledWidget<QComboBox>* cmb_incol = buildColorSelector ("Input"), |
435 ui.setupUi( dlg ); |
409 *cmb_cutcol = buildColorSelector ("Cutter"); |
436 |
410 QCheckBox* cb_colorize = new QCheckBox ("Colorize output"), |
437 makeColorSelector( ui.cmb_incol ); |
411 *cb_nocondense = new QCheckBox ("No condensing"), |
438 makeColorSelector( ui.cmb_cutcol ); |
412 *cb_repeatInverse = new QCheckBox ("Repeat inverse"), |
439 ui.cb_repeat->setWhatsThis( "If this is set, " APPNAME " runs Intersector a second time with inverse files to cut the " |
413 *cb_edges = new QCheckBox ("Add edges"); |
440 " cutter group with the input group. Both groups are cut by the intersection." ); |
414 LabeledWidget<QDoubleSpinBox>* dsb_prescale = new LabeledWidget<QDoubleSpinBox> ("Prescaling factor"); |
441 ui.cb_edges->setWhatsThis( "Makes " APPNAME " try run Isecalc to create edgelines for the intersection." ); |
415 |
442 |
416 cb_repeatInverse->setWhatsThis ("If this is set, " APPNAME " runs Intersector a second time with inverse files to cut the " |
443 short inCol, cutCol; |
417 " cutter group with the input group. Both groups are cut by the intersection."); |
444 const bool repeatInverse = ui.cb_repeat->isChecked (); |
418 cb_edges->setWhatsThis ("Makes " APPNAME " try run Isecalc to create edgelines for the intersection."); |
445 |
419 |
446 for( ;; ) |
420 dsb_prescale->w ()->setMinimum (0.0f); |
447 { |
421 dsb_prescale->w ()->setMaximum (10000.0f); |
448 if (!dlg->exec ()) |
422 dsb_prescale->w ()->setSingleStep (0.01f); |
449 return; |
423 dsb_prescale->w ()->setValue (1.0f); |
450 |
424 |
451 inCol = ui.cmb_incol->itemData (ui.cmb_incol->currentIndex ()).toInt (); |
425 QVBoxLayout* layout = new QVBoxLayout (&dlg); |
452 cutCol = ui.cmb_cutcol->itemData (ui.cmb_cutcol->currentIndex ()).toInt (); |
426 layout->addWidget (cmb_incol); |
453 |
427 layout->addWidget (cmb_cutcol); |
454 if (inCol == cutCol) { |
428 |
455 critical ("Cannot use the same color group for both input and cutter!"); |
429 QHBoxLayout* cblayout = new QHBoxLayout; |
456 continue; |
430 cblayout->addWidget (cb_colorize); |
457 } |
431 cblayout->addWidget (cb_nocondense); |
458 |
432 |
459 break; |
433 QHBoxLayout* cb2layout = new QHBoxLayout; |
|
434 cb2layout->addWidget (cb_repeatInverse); |
|
435 cb2layout->addWidget (cb_edges); |
|
436 |
|
437 layout->addLayout (cblayout); |
|
438 layout->addLayout (cb2layout); |
|
439 layout->addWidget (dsb_prescale); |
|
440 layout->addWidget (makeButtonBox (dlg)); |
|
441 |
|
442 exec: |
|
443 if (!dlg.exec ()) |
|
444 return; |
|
445 |
|
446 const short inCol = cmb_incol->w ()->itemData (cmb_incol->w ()->currentIndex ()).toInt (), |
|
447 cutCol = cmb_cutcol->w ()->itemData (cmb_cutcol->w ()->currentIndex ()).toInt (); |
|
448 const bool repeatInverse = cb_repeatInverse->isChecked (); |
|
449 |
|
450 if (inCol == cutCol) { |
|
451 critical ("Cannot use the same color group for both input and cutter!"); |
|
452 goto exec; |
|
453 } |
460 } |
454 |
461 |
455 // Five temporary files! |
462 // Five temporary files! |
456 // indat = input group file |
463 // indat = input group file |
457 // cutdat = cutter group file |
464 // cutdat = cutter group file |
489 outDAT2Name |
496 outDAT2Name |
490 }); |
497 }); |
491 |
498 |
492 writeColorGroup (inCol, inDATName); |
499 writeColorGroup (inCol, inDATName); |
493 writeColorGroup (cutCol, cutDATName); |
500 writeColorGroup (cutCol, cutDATName); |
494 runUtilityProcess (Intersector, prog_intersector, argv_normal); |
501 |
|
502 if (!runUtilityProcess (Intersector, prog_intersector, argv_normal)) |
|
503 return; |
|
504 |
495 insertOutput (outDATName, false, {inCol}); |
505 insertOutput (outDATName, false, {inCol}); |
496 |
506 |
497 if (repeatInverse) { |
507 if (repeatInverse && runUtilityProcess (Intersector, prog_intersector, argv_inverse)) |
498 runUtilityProcess (Intersector, prog_intersector, argv_inverse); |
|
499 insertOutput (outDAT2Name, false, {cutCol}); |
508 insertOutput (outDAT2Name, false, {cutCol}); |
500 } |
509 |
501 |
510 if (ui.cb_edges->isChecked () && runUtilityProcess (Isecalc, prog_isecalc, |
502 if (cb_edges->isChecked ()) { |
511 join ({inDATName, cutDATName, edgesDATName}))) |
503 runUtilityProcess (Isecalc, prog_isecalc, join ({inDATName, cutDATName, edgesDATName})); |
512 { |
504 insertOutput (edgesDATName, false, {}); |
513 insertOutput (edgesDATName, false, {}); |
505 } |
514 } |
506 } |
515 } |
507 |
516 |
508 // ============================================================================= |
517 // ============================================================================= |
512 setlocale (LC_ALL, "C"); |
521 setlocale (LC_ALL, "C"); |
513 |
522 |
514 if (!checkProgPath (prog_coverer, Coverer)) |
523 if (!checkProgPath (prog_coverer, Coverer)) |
515 return; |
524 return; |
516 |
525 |
517 QDialog dlg; |
526 QDialog* dlg = new QDialog; |
518 |
527 Ui::CovererUI ui; |
519 LabeledWidget<QComboBox>* cmb_col1 = buildColorSelector ("Shape 1"), |
528 ui.setupUi( dlg ); |
520 *cmb_col2 = buildColorSelector ("Shape 2"); |
529 makeColorSelector( ui.cmb_col1 ); |
521 |
530 makeColorSelector( ui.cmb_col2 ); |
522 QDoubleSpinBox* dsb_segsplit = new QDoubleSpinBox; |
531 |
523 QLabel* lb_segsplit = new QLabel ("Segment split length:"); |
532 short in1Col, in2Col; |
524 QSpinBox* sb_bias = new QSpinBox; |
533 for (;;) { |
525 QLabel* lb_bias = new QLabel ("Bias:"); |
534 if (!dlg->exec ()) |
526 QCheckBox* cb_reverse = new QCheckBox ("Reverse shape 2"), |
535 return; |
527 *cb_oldsweep = new QCheckBox ("Old sweep method"); |
536 |
528 |
537 in1Col = ui.cmb_col1->itemData (ui.cmb_col1->currentIndex ()).toInt (); |
529 dsb_segsplit->setSpecialValueText ("No splitting"); |
538 in2Col = ui.cmb_col2->itemData (ui.cmb_col2->currentIndex ()).toInt (); |
530 dsb_segsplit->setRange (0.0f, 10000.0f); |
539 |
531 sb_bias->setSpecialValueText ("No bias"); |
540 if (in1Col == in2Col) { |
532 sb_bias->setRange (-100, 100); |
541 critical ("Cannot use the same color group for both input and cutter!"); |
533 |
542 continue; |
534 QGridLayout* spinboxlayout = new QGridLayout; |
543 } |
535 spinboxlayout->addWidget (lb_segsplit, 0, 0); |
544 |
536 spinboxlayout->addWidget (dsb_segsplit, 0, 1); |
545 break; |
537 spinboxlayout->addWidget (lb_bias, 1, 0); |
|
538 spinboxlayout->addWidget (sb_bias, 1, 1); |
|
539 |
|
540 QVBoxLayout* layout = new QVBoxLayout (&dlg); |
|
541 layout->addWidget (cmb_col1); |
|
542 layout->addWidget (cmb_col2); |
|
543 layout->addLayout (spinboxlayout); |
|
544 layout->addWidget (cb_reverse); |
|
545 layout->addWidget (cb_oldsweep); |
|
546 layout->addWidget (makeButtonBox (dlg)); |
|
547 |
|
548 exec: |
|
549 if (!dlg.exec ()) |
|
550 return; |
|
551 |
|
552 const short in1Col = cmb_col1->w ()->itemData (cmb_col1->w ()->currentIndex ()).toInt (), |
|
553 in2Col = cmb_col1->w ()->itemData (cmb_col2->w ()->currentIndex ()).toInt (); |
|
554 |
|
555 if (in1Col == in2Col) { |
|
556 critical ("Cannot use the same color group for both input and cutter!"); |
|
557 goto exec; |
|
558 } |
546 } |
559 |
547 |
560 QTemporaryFile in1dat, in2dat, outdat; |
548 QTemporaryFile in1dat, in2dat, outdat; |
561 str in1DATName, in2DATName, outDATName; |
549 str in1DATName, in2DATName, outDATName; |
562 |
550 |
563 if (!mkTempFile (in1dat, in1DATName) || !mkTempFile (in2dat, in2DATName) || !mkTempFile (outdat, outDATName)) |
551 if (!mkTempFile (in1dat, in1DATName) || !mkTempFile (in2dat, in2DATName) || !mkTempFile (outdat, outDATName)) |
564 return; |
552 return; |
565 |
553 |
566 str argv = join ({ |
554 str argv = join ({ |
567 (cb_oldsweep->isChecked () ? "-s" : ""), |
555 (ui.cb_oldsweep->isChecked () ? "-s" : ""), |
568 (cb_reverse->isChecked () ? "-r" : ""), |
556 (ui.cb_reverse->isChecked () ? "-r" : ""), |
569 (dsb_segsplit->value () != 0 ? fmt ("-l %1", dsb_segsplit->value ()) : ""), |
557 (ui.dsb_segsplit->value () != 0 ? fmt ("-l %1", ui.dsb_segsplit->value ()) : ""), |
570 (sb_bias->value () != 0 ? fmt ("-s %1", sb_bias->value ()) : ""), |
558 (ui.sb_bias->value () != 0 ? fmt ("-s %1", ui.sb_bias->value ()) : ""), |
571 in1DATName, |
559 in1DATName, |
572 in2DATName, |
560 in2DATName, |
573 outDATName |
561 outDATName |
574 }); |
562 }); |
575 |
563 |
576 writeColorGroup (in1Col, in1DATName); |
564 writeColorGroup (in1Col, in1DATName); |
577 writeColorGroup (in2Col, in2DATName); |
565 writeColorGroup (in2Col, in2DATName); |
578 runUtilityProcess (Coverer, prog_coverer, argv); |
566 |
|
567 if (!runUtilityProcess (Coverer, prog_coverer, argv)) |
|
568 return; |
|
569 |
579 insertOutput (outDATName, false, {}); |
570 insertOutput (outDATName, false, {}); |
580 } |
571 } |
581 |
572 |
582 // ============================================================================= |
573 // ============================================================================= |
583 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * |
574 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * |