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]; |
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 } |