1 /* BurrTools
2  *
3  * BurrTools is the legal property of its developers, whose
4  * names are listed in the COPYRIGHT file, which is included
5  * within the source distribution.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11 
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16 
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
20  */
21 #include "imageexport.h"
22 
23 #include "image.h"
24 #include "view3dgroup.h"
25 #include "Layouter.h"
26 #include "blocklistgroup.h"
27 
28 #include "../lib/puzzle.h"
29 #include "../lib/problem.h"
30 #include "../lib/disassembly.h"
31 #include "../lib/disasmtomoves.h"
32 #include "../lib/solution.h"
33 
34 #include <FL/Fl.H>
35 
36 #include <stdlib.h>
37 #include <stdio.h>
38 
39 /* image info contains the information for one image to be exported
40  * this information is the setup required to produce this image, also
41  * the size
42  */
43 class ImageInfo {
44 
45   public:
46 
47     typedef enum {
48       SHOW_SINGLE,
49       SHOW_ASSEMBLY
50     } functions;
51 
52   private:
53 
54     // the parameters to set up the image
55     functions setupFunction;
56 
57     puzzle_c * puzzle;
58 
59     // parameters for single
60     unsigned int shape;
61     voxelFrame_c::colorMode showColors;
62 
63     // parameters for assembly
64     unsigned int problem;
65     unsigned int solution;
66     bool dim;
67 
68     disasmToMoves_c * positions;
69 
70     // the image data
71     image_c * i;  // image generated with the drawer that is with a fixed hight and the required width
72     image_c * i2; // the final image
73     unsigned int i2aa;
74 
75     /* the openGL context to draw to */
76     voxelFrame_c * vv;
77 
78   public:
79 
80     /* create image info to create a single shape image */
ImageInfo(puzzle_c * p,voxelFrame_c::colorMode color,unsigned int s,voxelFrame_c * v)81     ImageInfo(puzzle_c * p, voxelFrame_c::colorMode color,
82         unsigned int s, voxelFrame_c * v) : setupFunction(SHOW_SINGLE), puzzle(p),
83                                           shape(s), showColors(color),
84                                           i(new image_c(600, 200)), i2(0), vv(v) { }
85 
86     /* image info for an assembly, if you don't give pos, you will get the standard assembly with
87      * no piece shifted
88      */
ImageInfo(puzzle_c * p,voxelFrame_c::colorMode color,unsigned int prob,unsigned int sol,voxelFrame_c * v,disasmToMoves_c * pos=0,bool d=false)89     ImageInfo(puzzle_c * p, voxelFrame_c::colorMode color, unsigned int prob,
90         unsigned int sol, voxelFrame_c * v,
91         disasmToMoves_c * pos = 0, bool d = false) : setupFunction(SHOW_ASSEMBLY), puzzle(p),
92                                                    showColors(color), problem(prob),
93                                                    solution(sol), dim(d), positions(pos),
94                                                    i(new image_c(600, 200)),
95                                                    i2(0), vv(v) { }
96 
~ImageInfo()97     ~ImageInfo() {
98       if (i) delete i;
99       if (i2) delete i2;
100     }
101 
102     /* set up the voxelFrame_c so that is shows the information for this image */
103     void setupContent(void);
104 
105     /* preparation to get a tile for the preview image */
106     void preparePreviewImage(void);
107 
108     /* get a tile of the preview image */
109     bool getPreviewImage(void);
110 
111     /* get the width / height ratio of the preview image */
ratio(void)112     double ratio(void) { return ((double)(i->w()))/i->h(); }
113 
114     /* start a new image */
115     void generateImage(unsigned int w, unsigned int h, unsigned char aa);
116 
117     /* returns true, if the image has been started */
imageStarted(void)118     bool imageStarted(void) { return i2; }
119 
120     /* prepare for a new tile of the image */
121     void prepareImage(void);
122 
123     /* get a new tile for the image */
124     image_c * getImage(void);
125 };
126 
setupContent(void)127 void ImageInfo::setupContent(void) {
128 
129   switch (setupFunction) {
130     case SHOW_SINGLE:
131       vv->showSingleShape(puzzle, shape);
132       vv->showColors(puzzle, showColors);
133 
134       break;
135     case SHOW_ASSEMBLY:
136       vv->showAssembly(puzzle->getProblem(problem), solution);
137       vv->showColors(puzzle, showColors);
138 
139       if (positions) {
140         vv->updatePositions(positions);
141         if (dim)
142           vv->dimStaticPieces(positions);
143       }
144   }
145 }
146 
147 /* preparation to get a tile for the preview image */
preparePreviewImage(void)148 void ImageInfo::preparePreviewImage(void) {
149 
150   setupContent();
151   i->prepareOpenGlImagePart(vv);
152   glClearColor(1, 1, 1, 0);
153 }
154 
155 /* get a tile of the preview image */
getPreviewImage(void)156 bool ImageInfo::getPreviewImage(void) {
157 
158   if (!i->getOpenGlImagePart()) {
159 
160     i->transparentize(255, 255, 255);
161     i->minimizeWidth(0);
162 
163     return false;
164   } else
165     return true;
166 }
167 
168 /* start a new image */
generateImage(unsigned int,unsigned int h,unsigned char aa)169 void ImageInfo::generateImage(unsigned int /*w*/, unsigned int h, unsigned char aa) {
170   if (i2)
171     delete i2;
172   i2 = new image_c ((h*3)*aa, h*aa);
173   i2aa = aa;
174 }
175 
176 /* prepare for a new tile of the image */
prepareImage(void)177 void ImageInfo::prepareImage(void) {
178 
179   setupContent();
180   i2->prepareOpenGlImagePart(vv);
181   glClearColor(1, 1, 1, 0);
182 }
183 
184 /* get a new tile for the image */
getImage(void)185 image_c * ImageInfo::getImage(void) {
186 
187   if (!i2->getOpenGlImagePart()) {
188 
189     i2->transparentize(255, 255, 255);
190     i2->minimizeWidth(0, i2aa);
191     i2->scaleDown(i2aa);
192 
193     return i2;
194   } else
195     return 0;
196 }
197 
198 
199 
200 
201 
cb_ImageExportAbort_stub(Fl_Widget *,void * v)202 static void cb_ImageExportAbort_stub(Fl_Widget* /*o*/, void* v) { ((imageExport_c*)(v))->cb_Abort(); }
cb_Abort(void)203 void imageExport_c::cb_Abort(void) {
204   hide();
205 }
206 
PreDraw(void)207 bool imageExport_c::PreDraw(void) {
208 
209   static char statText[50];
210 
211   switch(state) {
212     case 0:
213 
214       snprintf(statText, 50, "create Preview image %u / %u", im, images.size());
215       status->label(statText);
216 
217       images[im]->preparePreviewImage();
218 
219       return true;
220 
221     case 1:
222 
223       if (!images[im]->imageStarted()) {
224 
225         snprintf(statText, 50, "create image %u / %u", im, images.size());
226         status->label(statText);
227 
228         unsigned int w = (unsigned int)(imgHeight * images[im]->ratio() + 0.9);
229 
230         // calculate anti-aliasing factor
231         int aa = 1;
232         if (AA2->value()) aa = 2;
233         if (AA3->value()) aa = 3;
234         if (AA4->value()) aa = 4;
235         if (AA5->value()) aa = 5;
236 
237         images[im]->generateImage(w, imgHeight, aa);
238       }
239 
240       images[im]->prepareImage();
241 
242       return true;
243   }
244 
245   return false;
246 }
247 
nextImage(bool finish)248 void imageExport_c::nextImage(bool finish) {
249 
250   static char statText[20];
251 
252   if (i) {
253 
254     snprintf(statText, 20, "save page %i", curPage);
255     status->label(statText);
256 
257     char name[1000];
258 
259     if (Pname->value() && Pname->value()[0] && Pname->value()[strlen(Pname->value())-1] != '/')
260       snprintf(name, 1000, "%s/%s%03i.png", Pname->value(), Fname->value(), curPage);
261     else
262       snprintf(name, 1000, "%s%s%03i.png", Pname->value(), Fname->value(), curPage);
263 
264     i->saveToPNG(name);
265     delete i;
266     i = 0;
267   }
268 
269   if (!finish) {
270 
271     unsigned int pageHeight = atoi(SizePixelY->value());
272     unsigned int pageWidth = atoi(SizePixelX->value());
273 
274     if (BgWhite->value()) {
275       i = new image_c(pageWidth, pageHeight, 255, 255, 255, 255);
276     } else {
277       i = new image_c(pageWidth, pageHeight, 0, 0, 0, 0);
278     }
279   }
280 }
281 
PostDraw(void)282 void imageExport_c::PostDraw(void) {
283 
284   switch(state) {
285     case 0:
286 
287       if (!images[im]->getPreviewImage()) {
288 
289         // finished preview image, next
290         im++;
291 
292         // all preview images generated?
293         if (im >= images.size()) {
294 
295           // now find out in how many lines the images need to be put onto the pages to
296           // get them all onto the available space
297 
298           unsigned int pageWidth = atoi(SizePixelX->value());
299           imgHeight = atoi(SizePixelY->value());
300 
301           // if we have less images than pages, lower pages
302           unsigned int pages = atoi(NumPages->value());
303           if (pages == 0) pages = 1;
304           if (pages > images.size()) pages = images.size();
305 
306           while (true) {
307 
308             curWidth = 0;
309             curLine = 0;
310             curPage = 0;
311 
312             unsigned int linesPerPage = atoi(SizePixelY->value()) / imgHeight;
313 
314             // check, if everything fits with the current number of lines
315             for (unsigned int im = 0; im < images.size(); im++) {
316               // calculate width of the image when it has the current line hight
317               unsigned int w = (unsigned int)(imgHeight * images[im]->ratio() + 0.9);
318 
319               if (curWidth + w < pageWidth || (curWidth == 0)) {
320                 // image fits onto the line
321                 curWidth += w + pageWidth/60;
322 
323               } else {
324                 // image on the next line
325                 curWidth = w + pageWidth/60;
326                 curLine++;
327                 if (curLine >= linesPerPage) {
328                   curLine = 0;
329                   curPage++;
330                 }
331               }
332             }
333 
334             // check if we fit
335             if ((curPage < pages) || ((curPage == pages) && (curLine == 0) && (curWidth == 0)))
336               break;
337 
338             imgHeight--;
339           }
340 
341           // OK, now lets setup the variable for output
342           curWidth = 0;
343           curLine = 0;
344           curPage = 0;
345 
346           nextImage(false);
347 
348           im = 0;
349           state = 1;
350         }
351       }
352 
353       break;
354 
355     case 1:
356 
357       image_c * i2 = images[im]->getImage();
358 
359       if (i2) {
360 
361         unsigned int pageWidth = atoi(SizePixelX->value());
362         unsigned int linesPerPage = atoi(SizePixelY->value()) / imgHeight;
363 
364         unsigned int w = i2->w();
365         if (curWidth + w < pageWidth || (curWidth == 0)) {
366           // image fits onto the line
367           i->blit(i2, curWidth, curLine * imgHeight);
368           curWidth += w + pageWidth/60;
369         } else {
370           // image on the next line
371           curWidth = w + pageWidth/60;
372           curLine++;
373           if (curLine >= linesPerPage) {
374             curLine = 0;
375             nextImage(false);
376             curPage++;
377           }
378           i->blit(i2, 0, curLine * imgHeight);
379         }
380 
381         im++;
382 
383         if (im >= images.size()) {
384 
385           nextImage(true);
386 
387           // finished,
388           state = 3;
389 
390           // remove callbacks
391           view3D->getView()->setCallback();
392 
393           status->label("Done");
394 
395           working = false;
396         }
397       }
398 
399       break;
400   }
401 }
402 
403 
cb_ImageExportExport_stub(Fl_Widget *,void * v)404 static void cb_ImageExportExport_stub(Fl_Widget* /*o*/, void* v) { ((imageExport_c*)(v))->cb_Export(); }
cb_Export(void)405 void imageExport_c::cb_Export(void) {
406 
407   /* this vector contains all the information of all images that need to appear in the output */
408   images.clear();
409 
410   if (ExpShape->value()) {
411 
412     images.push_back(new ImageInfo(puzzle, getColorMode(),
413         ShapeSelect->getSelection(), view3D->getView()));
414 
415   } else if (ExpAssembly->value()) {
416 
417     images.push_back(new ImageInfo(puzzle, getColorMode(),
418         ProblemSelect->getSelection(), 0, view3D->getView()));
419 
420   } else if (ExpSolutionDisassm->value()) {
421 
422     unsigned int prob = ProblemSelect->getSelection();
423     problem_c * pr = puzzle->getProblem(prob);
424 
425     // generate an image for each step (for the moment only for the last solution)
426     unsigned int s = pr->solutionNumber() - 1;
427     separation_c * t = pr->getSolution(s)->getDisassembly();
428     if (!t) return;
429 
430     for (unsigned int step = 0; step < t->sumMoves(); step++) {
431       disasmToMoves_c * dtm = new disasmToMoves_c(t, 20, pr->pieceNumber());
432       dtm->setStep(step, false, true);
433       images.push_back(new ImageInfo(puzzle, getColorMode(),
434            prob, s, view3D->getView(), dtm, DimStatic->value()));
435     }
436 
437   } else if (ExpSolution->value()) {
438 
439     unsigned int prob = ProblemSelect->getSelection();
440     problem_c * pr = puzzle->getProblem(prob);
441 
442     // generate an image for each step (for the moment only for the last solution)
443     unsigned int s = pr->solutionNumber() - 1;
444     separation_c * t = pr->getSolution(s)->getDisassembly();
445     if (!t) return;
446 
447     for (unsigned int step = t->sumMoves() - 1; step > 0; step--) {
448       disasmToMoves_c * dtm = new disasmToMoves_c(t, 20, pr->pieceNumber());
449       dtm->setStep(step, false, true);
450       images.push_back(new ImageInfo(puzzle, getColorMode(),
451            prob, s, view3D->getView(), dtm, DimStatic->value()));
452     }
453 
454     disasmToMoves_c * dtm = new disasmToMoves_c(t, 20, pr->pieceNumber());
455     dtm->setStep(0, false, true);
456     images.push_back(new ImageInfo(puzzle, getColorMode(),
457           prob, s, view3D->getView(), dtm, false));
458 
459   } else if (ExpProblem->value()) {
460     // generate an image for each piece in the problem
461     unsigned int prob = ProblemSelect->getSelection();
462     problem_c * pr = puzzle->getProblem(prob);
463 
464     if (pr->resultValid())
465       images.push_back(new ImageInfo(puzzle, getColorMode(),
466             pr->getResultId(), view3D->getView()));
467 
468     for (unsigned int p = 0; p < pr->partNumber(); p++)
469       images.push_back(new ImageInfo(puzzle, getColorMode(),
470             pr->getShape(p), view3D->getView()));
471 
472   } else
473 
474     return;
475 
476   im = 0;
477   working = true;
478   BtnStart->deactivate();
479   BtnAbbort->deactivate();
480   state = 0;
481 
482   view3D->getView()->setCallback(this);
483 }
484 
cb_ImageExport3DUpdate_stub(Fl_Widget *,void * v)485 static void cb_ImageExport3DUpdate_stub(Fl_Widget* /*o*/, void* v) { ((imageExport_c*)(v))->cb_Update3DView(); }
cb_Update3DView(void)486 void imageExport_c::cb_Update3DView(void) {
487 
488   bool assemblies = false;
489   bool solutions = false;
490 
491   unsigned int prob = ProblemSelect->getSelection();
492   problem_c * pr = puzzle->getProblem(prob);
493 
494   if (prob < puzzle->problemNumber())
495     if (pr->solutionNumber() > 0) {
496       if (pr->getSolution(0)->getAssembly()) assemblies = true;
497       if (pr->getSolution(0)->getDisassembly()) solutions = true;
498     }
499 
500   if (!solutions) {
501     ExpSolutionDisassm->deactivate();
502     if (ExpSolutionDisassm->value())
503         ExpAssembly->setonly();
504   } else
505     ExpSolutionDisassm->activate();
506   if (!solutions) {
507     ExpSolution->deactivate();
508     if (ExpSolution->value())
509       ExpAssembly->setonly();
510   } else
511     ExpSolution->activate();
512   if (!assemblies) {
513     ExpAssembly->deactivate();
514     if (ExpAssembly->value())
515       ExpProblem->setonly();
516   } else
517     ExpAssembly->activate();
518   if (puzzle->problemNumber() == 0) {
519     ExpProblem->deactivate();
520     if (ExpProblem->value())
521       ExpShape->setonly();
522   } else
523     ExpProblem->activate();
524   if (puzzle->shapeNumber() == 0)
525     ExpShape->deactivate();
526   else
527     ExpShape->activate();
528 
529   if (ExpShape->value()) {
530     view3D->getView()->showSingleShape(puzzle, ShapeSelect->getSelection());
531   } else if (ExpAssembly->value()) {
532     view3D->getView()->showAssembly(puzzle->getProblem(ProblemSelect->getSelection()), 0);
533   } else if (ExpSolution->value()) {
534     view3D->getView()->showAssembly(puzzle->getProblem(ProblemSelect->getSelection()), 0);
535   } else if (ExpSolutionDisassm->value()) {
536     view3D->getView()->showAssembly(puzzle->getProblem(ProblemSelect->getSelection()), 0);
537   } else if (ExpProblem->value() && pr->resultValid()) {
538     view3D->getView()->showSingleShape(puzzle, pr->getResultId());
539   }
540   view3D->getView()->showColors(puzzle, getColorMode());
541 }
542 
cb_ImageExportSzUpdate_stub(Fl_Widget *,void * v)543 static void cb_ImageExportSzUpdate_stub(Fl_Widget * /*o*/, void *v) { ((imageExport_c*)(v))->cb_SzUpdate(); }
cb_SzUpdate(void)544 void imageExport_c::cb_SzUpdate(void) {
545 
546   if (SzA4Land->value()) {
547     SzX->value("297");
548     SzY->value("210");
549   } else if (SzA4Port->value()) {
550     SzX->value("210");
551     SzY->value("297");
552   } else if (SzLetterLand->value()) {
553     SzX->value("279");
554     SzY->value("216");
555   } else if (SzLetterPort->value()) {
556     SzX->value("216");
557     SzY->value("279");
558   }
559 
560   if (SzManual->value()) {
561     SzX->activate();
562     SzY->activate();
563   } else {
564     SzX->deactivate();
565     SzY->deactivate();
566   }
567 
568   if (SzX->value() && SzX->value()[0] && SzDPI->value() && SzDPI->value()[0]) {
569     char tmp[20];
570     snprintf(tmp, 20, "%i", int(atoi(SzDPI->value()) * atoi(SzX->value()) * 0.03937 + 0.5));
571     SizePixelX->value(tmp);
572   }
573 
574   if (SzY->value() && SzY->value()[0] && SzDPI->value() && SzDPI->value()[0]) {
575     char tmp[20];
576     snprintf(tmp, 20, "%i", int(atoi(SzDPI->value()) * atoi(SzY->value()) * 0.03937 + 0.5));
577     SizePixelY->value(tmp);
578   }
579 }
580 
imageExport_c(puzzle_c * p)581 imageExport_c::imageExport_c(puzzle_c * p) : LFl_Double_Window(false), puzzle(p), working(false), state(0), i(0) {
582 
583   label("Export Images");
584 
585   LFl_Frame *fr;
586 
587   {
588     fr = new LFl_Frame(0, 0, 1, 2);
589 
590     BgWhite = new LFl_Radio_Button("White Background", 0, 0);
591     BgTransp = new LFl_Radio_Button("Transparent Background", 0, 1);
592     BgWhite->value(1);
593     (new LFl_Box(0, 2))->weight(0, 1);
594     fr->end();
595   }
596 
597   {
598     // the group that defines the super sampling
599     fr = new LFl_Frame(0, 2);
600 
601     AA1 = new LFl_Radio_Button("No Antialiasing", 0, 0);
602     AA2 = new LFl_Radio_Button("2x2 Supersampling", 0, 1);
603     AA3 = new LFl_Radio_Button("3x3 Supersampling", 0, 2);
604     AA3->value(1);
605     AA4 = new LFl_Radio_Button("4x4 Supersampling", 0, 3);
606     AA5 = new LFl_Radio_Button("5x5 Supersampling", 0, 4);
607 
608     {
609       layouter_c * l = new layouter_c(0, 5, 1, 2);
610 
611       ColPiece = new LFl_Radio_Button("Use piece colours", 0, 5);
612       ColConst = new LFl_Radio_Button("Use colour constraint colours", 0, 6);
613 
614       ColPiece->value(1);
615 
616       ColPiece->callback(cb_ImageExport3DUpdate_stub, this);
617       ColConst->callback(cb_ImageExport3DUpdate_stub, this);
618 
619       l->end();
620     }
621 
622     DimStatic = new LFl_Check_Button("Dim static pieces", 0, 7);
623     (new LFl_Box(0, 8))->weight(0, 1);
624     fr->end();
625   }
626 
627   {
628     // user defined size input
629     fr = new LFl_Frame(1, 1, 1, 2);
630 
631     int y = 0;
632 
633     SzA4Port = new LFl_Radio_Button("A4 Portrait", 0, y++, 5, 1);
634     SzA4Port->callback(cb_ImageExportSzUpdate_stub, this);
635 
636     SzA4Land = new LFl_Radio_Button("A4 Landscape", 0, y++, 5, 1);
637     SzA4Land->callback(cb_ImageExportSzUpdate_stub, this);
638 
639     SzLetterPort = new LFl_Radio_Button("Letter Portrait", 0, y++, 5, 1);
640     SzLetterPort->callback(cb_ImageExportSzUpdate_stub, this);
641 
642     SzLetterLand = new LFl_Radio_Button("Letter Landscape", 0, y++, 5, 1);
643     SzLetterLand->callback(cb_ImageExportSzUpdate_stub, this);
644 
645     SzManual = new LFl_Radio_Button("manual", 0, y++, 5, 1);
646     SzManual->value(1);
647     SzManual->callback(cb_ImageExportSzUpdate_stub, this);
648 
649     (new LFl_Box("Size X", 0, y))->stretchRight();
650     SzX = new LFl_Input(2, y);
651     SzX->callback(cb_ImageExportSzUpdate_stub, this);
652     (new LFl_Box("mm", 4, y))->stretchLeft();
653     (new LFl_Box(3, y))->setMinimumSize(5, 0);
654     (new LFl_Box(1, y++))->setMinimumSize(5, 0);
655 
656     (new LFl_Box("Size Y", 0, y))->stretchRight();
657     SzY = new LFl_Input(2, y);
658     SzY->callback(cb_ImageExportSzUpdate_stub, this);
659     (new LFl_Box("mm", 4, y++))->stretchLeft();
660 
661     (new LFl_Box("DPI", 0, y))->stretchRight();
662     SzDPI = new LFl_Input(2, y++);
663     SzDPI->value("300");
664     SzDPI->callback(cb_ImageExportSzUpdate_stub, this);
665 
666     (new LFl_Box("Pixel X", 0, y))->stretchRight();
667     SizePixelX = new LFl_Int_Input(2, y++);
668     SizePixelX->value("300");
669     SizePixelX->setMinimumSize(50, 0);
670 
671     (new LFl_Box("Pixel Y", 0, y))->stretchRight();
672     SizePixelY = new LFl_Int_Input(2, y++);
673     SizePixelY->value("300");
674 
675     (new LFl_Box(0, y))->weight(0, 1);
676 
677     fr->end();
678   }
679 
680   {
681     fr = new LFl_Frame(0, 3, 2, 1);
682 
683     (new LFl_Box("File name", 0, 0))->stretchLeft();
684     (new LFl_Box("Path", 0, 1))->stretchLeft();
685     (new LFl_Box("Number of files", 0, 2, 3, 1))->stretchLeft();
686     (new LFl_Box("Number of images", 0, 3, 3, 1))->stretchLeft();
687 
688     (new LFl_Box(1, 0))->setMinimumSize(5, 0);
689     (new LFl_Box(3, 0))->setMinimumSize(5, 0);
690 
691     Fname = new LFl_Input(2, 0, 3, 1);
692     Fname->value("test");
693     Fname->weight(1, 0);
694     Pname = new LFl_Input(2, 1, 3, 1);
695     NumPages = new LFl_Int_Input(4, 2);
696     new LFl_Int_Input(4, 3);
697 
698     fr->end();
699   }
700 
701   {
702     // create the radio buttons that select what of the current puzzle file to
703     // export and enable only those of the possibilities that are available in
704     // the current puzzle
705 
706     fr = new LFl_Frame(0, 4, 2, 1);
707 
708     ExpShape = new LFl_Radio_Button("Export Shape", 0, 0);
709     ExpProblem = new LFl_Radio_Button("Export Problem", 0, 1);
710     ExpAssembly = new LFl_Radio_Button("Export Assembly", 0, 2);
711     ExpSolution = new LFl_Radio_Button("Export Solution (Assembly)", 0, 3);
712     ExpSolutionDisassm = new LFl_Radio_Button("Export Solution (Disassembly)", 0, 4);
713     ExpSolution->setonly();
714 
715     (new LFl_Box(0, 5))->weight(0, 1);
716 
717     ExpShape->callback(cb_ImageExport3DUpdate_stub, this);
718     ExpProblem->callback(cb_ImageExport3DUpdate_stub, this);
719     ExpAssembly->callback(cb_ImageExport3DUpdate_stub, this);
720     ExpSolution->callback(cb_ImageExport3DUpdate_stub, this);
721     ExpSolutionDisassm->callback(cb_ImageExport3DUpdate_stub, this);
722 
723     fr->end();
724   }
725 
726   {
727     layouter_c * l = new layouter_c(0, 5, 2, 1);
728 
729     ShapeSelect = new PieceSelector(0, 0, 20, 20, puzzle);
730     ProblemSelect = new ProblemSelector(0, 0, 20, 20, puzzle);
731 
732     ShapeSelect->setSelection(0);
733     ProblemSelect->setSelection(0);
734 
735     LBlockListGroup_c * gr = new LBlockListGroup_c(0, 0, 1, 1, ShapeSelect);
736     gr->callback(cb_ImageExport3DUpdate_stub, this);
737     gr->setMinimumSize(200, 100);
738 
739     gr = new LBlockListGroup_c(1, 0, 1, 1, ProblemSelect);
740     gr->callback(cb_ImageExport3DUpdate_stub, this);
741     gr->setMinimumSize(200, 100);
742 
743     l->end();
744   }
745 
746   {
747     layouter_c * l = new layouter_c(0, 6, 3, 1);
748 
749     status = new LFl_Box();
750     status->weight(1, 0);
751     status->label("Test");
752     status->pitch(7);
753     status->align(FL_ALIGN_LEFT | FL_ALIGN_INSIDE);
754 
755     BtnStart = new LFl_Button("Export image(s)", 1, 0);
756     BtnStart->pitch(7);
757     BtnStart->callback(cb_ImageExportExport_stub, this);
758 
759     BtnAbbort = new LFl_Button("Abort", 2, 0);
760     BtnAbbort->pitch(7);
761     BtnAbbort->callback(cb_ImageExportAbort_stub, this);
762 
763     l->end();
764   }
765 
766   view3D = new LView3dGroup(2, 0, 1, 6);
767   view3D->setMinimumSize(400, 400);
768   cb_Update3DView();
769 
770   set_modal();
771 }
772 
update(void)773 void imageExport_c::update(void) {
774   if (working) {
775     BtnStart->deactivate();
776     BtnAbbort->deactivate();
777     view3D->deactivate();
778     view3D->redraw();
779   } else {
780     BtnStart->activate();
781     BtnAbbort->activate();
782     view3D->activate();
783 
784     if (state == 3) {
785       cb_Update3DView();
786       view3D->getView()->invalidate();
787       view3D->redraw();
788       state = 4;
789     }
790   }
791 }
792 
793