00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035 #ifdef G4VIS_BUILD_OPENGLWT_DRIVER
00036
00037 #include "G4OpenGLWtViewer.hh"
00038 #include "G4VViewer.hh"
00039 #include "G4VSceneHandler.hh"
00040 #include "G4OpenGLSceneHandler.hh"
00041
00042 #include "G4ios.hh"
00043 #include "G4VisExtent.hh"
00044 #include "G4LogicalVolume.hh"
00045 #include "G4VSolid.hh"
00046 #include "G4Point3D.hh"
00047 #include "G4Normal3D.hh"
00048 #include "G4Scene.hh"
00049
00050
00051 #include "G4UnitsTable.hh"
00052 #include "G4Wt.hh"
00053 #include "G4UIWt.hh"
00054 #include "G4UImanager.hh"
00055 #include "G4UIcommandTree.hh"
00056
00057 #include <WT/WHBoxLayout>
00058 #include <WT/WApplication>
00059
00060
00061
00062
00064
00067 void G4OpenGLWtViewer::SetView (
00068 )
00069
00070
00071 {
00072 G4OpenGLViewer::SetView ();
00073 }
00074
00075
00076
00077
00079 void G4OpenGLWtViewer::CreateMainWindow (
00080 Wt::WGLWidget* glWidget
00081 ,Wt::WString name
00082 )
00083
00084
00085 {
00086 #ifdef G4DEBUG_VIS_OGL
00087 printf("G4OpenGLWtViewer::CreateMainWindow \n");
00088 #endif
00089
00090 if(fWindow) return;
00091
00092 fWindow = glWidget ;
00093
00094
00095
00096
00097 ResizeWindow(fVP.GetWindowSizeHintX(),fVP.GetWindowSizeHintY());
00098
00099
00100
00101
00102
00103
00104
00105
00106
00107
00108 fGLWindow = fWindow;
00109
00110
00111 if(!fWindow) return;
00112
00113 #ifdef _A_FINIR_FIXME
00114 if (!fContextMenu)
00115 createPopupMenu();
00116 #endif
00117
00118
00119
00120 }
00121
00124
00125
00126
00127
00129 G4OpenGLWtViewer::G4OpenGLWtViewer (
00130 G4OpenGLSceneHandler& scene
00131 )
00132 :G4VViewer (scene, -1)
00133 ,G4OpenGLViewer (scene)
00134 ,fWindow(0)
00135 ,fRecordFrameNumber(0)
00136
00137 ,fMouseAction(STYLE1)
00138 ,fDeltaRotation(1)
00139 ,fDeltaSceneTranslation(0.01)
00140 ,fDeltaDepth(0.01)
00141 ,fDeltaZoom(0.05)
00142 ,fDeltaMove(0.05)
00143 ,fHoldKeyEvent(false)
00144 ,fHoldMoveEvent(false)
00145 ,fHoldRotateEvent(false)
00146 ,fAutoMove(false)
00147 ,fEncoderPath("")
00148 ,fTempFolderPath("")
00149 ,fMovieTempFolderPath("")
00150 ,fSaveFileName("")
00151 ,fParameterFileName("mpeg_encode_parameter_file.par")
00152 ,fMovieParametersDialog(NULL)
00153 ,fRecordingStep(WAIT)
00154 ,fProcess(NULL)
00155 ,fNbMaxFramesPerSec(100)
00156 ,fNbMaxAnglePerSec(360)
00157 ,fLaunchSpinDelay(100)
00158 ,fXRot(0)
00159 ,fYRot(0)
00160 ,fNoKeyPress(true)
00161 ,fAltKeyPress(false)
00162 ,fControlKeyPress(false)
00163 ,fShiftKeyPress(false)
00164 {
00165
00166
00167 G4Wt::getInstance ();
00168
00169 #ifdef G4DEBUG_VIS_OGL
00170 printf("G4OpenGLWtViewer::Create \n");
00171 #endif
00172 fLastPos3 = Wt::WPoint(-1,-1);
00173 fLastPos2 = Wt::WPoint(-1,-1);
00174 fLastPos1 = Wt::WPoint(-1,-1);
00175
00176 #ifdef _A_FINIR_FIXME
00177 initMovieParameters();
00178 #endif
00179
00180 fLastEventTime = new Wt::WTime();
00181
00182 #ifdef G4DEBUG_VIS_OGL
00183 printf("G4OpenGLWtViewer::G4OpenGLWtViewer END\n");
00184 #endif
00185 }
00186
00188 G4OpenGLWtViewer::~G4OpenGLWtViewer (
00189 )
00190
00191
00192 {
00193 #ifdef _A_FINIR_FIXME
00194 G4cout <<removeTempFolder().toUTF8().c_str() <<G4endl;
00195 #endif
00196 }
00197
00198
00199 #ifdef _A_FINIR_FIXME
00200
00203 void G4OpenGLWtViewer::createPopupMenu() {
00204
00205 fContextMenu = new WMenu("All");
00206
00207 WMenu *mMouseAction = fContextMenu->addMenu("&Mouse actions");
00208
00209 fRotateAction = mMouseAction->addAction("Rotate");
00210 fMoveAction = mMouseAction->addAction("Move");
00211 fPickAction = mMouseAction->addAction("Pick");
00212 WAction *shortcutsAction = mMouseAction->addAction("Show shortcuts");
00213
00214 fRotateAction->setCheckable(true);
00215 fMoveAction->setCheckable(false);
00216 fPickAction->setCheckable(false);
00217 shortcutsAction->setCheckable(false);
00218
00219 fRotateAction->setChecked(true);
00220 fMoveAction->setChecked(false);
00221 fPickAction->setChecked(false);
00222 shortcutsAction->setChecked(false);
00223
00224 WObject ::connect(fRotateAction,
00225 SIGNAL(triggered(bool)),
00226 this,
00227 SLOT(actionMouseRotate()));
00228
00229 WObject ::connect(fMoveAction,
00230 SIGNAL(triggered(bool)),
00231 this,
00232 SLOT(actionMouseMove()));
00233
00234 WObject ::connect(fPickAction,
00235 SIGNAL(triggered(bool)),
00236 this,
00237 SLOT(actionMousePick()));
00238
00239 WObject ::connect(shortcutsAction,
00240 SIGNAL(triggered(bool)),
00241 this,
00242 SLOT(showShortcuts()));
00243
00244
00245 WMenu *mStyle = fContextMenu->addMenu("&Style");
00246
00247 WMenu *mRepresentation = mStyle->addMenu("&Representation");
00248 WMenu *mProjection = mStyle->addMenu("&Projection");
00249 WAction *polyhedron = mRepresentation->addAction("Polyhedron");
00250 WAction *nurbs = mRepresentation->addAction("NURBS");
00251
00252 WAction *ortho = mProjection->addAction("Orthographic");
00253 WAction *perspective = mProjection->addAction("Persepective");
00254
00255
00256 G4ViewParameters::RepStyle style;
00257 style = fVP.GetRepStyle();
00258 if (style == G4ViewParameters::polyhedron) {
00259 createRadioAction(polyhedron,nurbs,SLOT(toggleRepresentation(bool)),1);
00260 } else if (style == G4ViewParameters::nurbs) {
00261 createRadioAction(polyhedron,nurbs,SLOT(toggleRepresentation(bool)),2);
00262 } else {
00263 mRepresentation->clear();
00264 }
00265
00266
00267 if (fVP.GetFieldHalfAngle() == 0) {
00268 createRadioAction(ortho, perspective,SLOT(toggleProjection(bool)),1);
00269 } else {
00270 createRadioAction(ortho, perspective,SLOT(toggleProjection(bool)),2);
00271 }
00272
00273
00274 WMenu *mDrawing = mStyle->addMenu("&Drawing");
00275
00276 fDrawingWireframe = mDrawing->addAction("Wireframe");
00277 fDrawingWireframe->setCheckable(true);
00278
00279 fDrawingLineRemoval = mDrawing->addAction("Hidden line removal");
00280 fDrawingLineRemoval->setCheckable(true);
00281
00282 fDrawingSurfaceRemoval = mDrawing->addAction("Hidden Surface removal");
00283 fDrawingSurfaceRemoval->setCheckable(true);
00284
00285 fDrawingLineSurfaceRemoval = mDrawing->addAction("Hidden line and surface removal");
00286 fDrawingLineSurfaceRemoval->setCheckable(true);
00287
00288
00289 G4ViewParameters::DrawingStyle d_style;
00290 d_style = fVP.GetDrawingStyle();
00291
00292 if (d_style == G4ViewParameters::wireframe) {
00293 fDrawingWireframe->setChecked(true);
00294 } else if (d_style == G4ViewParameters::hlr) {
00295 fDrawingLineRemoval->setChecked(true);
00296 } else if (d_style == G4ViewParameters::hsr) {
00297 fDrawingSurfaceRemoval->setChecked(true);
00298 } else if (d_style == G4ViewParameters::hlhsr) {
00299 fDrawingLineSurfaceRemoval->setChecked(true);
00300 } else {
00301 mDrawing->clear();
00302 }
00303 WObject ::connect(fDrawingWireframe,
00304 SIGNAL(triggered(bool)),
00305 this,
00306 SLOT(actionDrawingWireframe()));
00307 WObject ::connect(fDrawingLineRemoval,
00308 SIGNAL(triggered(bool)),
00309 this,
00310 SLOT(actionDrawingLineRemoval()));
00311 WObject ::connect(fDrawingSurfaceRemoval,
00312 SIGNAL(triggered(bool)),
00313 this,
00314 SLOT(actionDrawingSurfaceRemoval()));
00315 WObject ::connect(fDrawingLineSurfaceRemoval,
00316 SIGNAL(triggered(bool)),
00317 this,
00318 SLOT(actionDrawingLineSurfaceRemoval()));
00319
00320
00321
00322 WAction *backgroundColorChooser ;
00323
00324
00325 backgroundColorChooser = mStyle->addAction("Background color");
00326 WObject ::connect(backgroundColorChooser,
00327 SIGNAL(triggered()),
00328 this,
00329 SLOT(actionChangeBackgroundColor()));
00330
00331
00332
00333 WAction *textColorChooser ;
00334
00335 textColorChooser = mStyle->addAction("Text color");
00336 WObject ::connect(textColorChooser,
00337 SIGNAL(triggered()),
00338 this,
00339 SLOT(actionChangeTextColor()));
00340
00341
00342
00343 WAction *defaultColorChooser ;
00344
00345 defaultColorChooser = mStyle->addAction("Default color");
00346 WObject ::connect(defaultColorChooser,
00347 SIGNAL(triggered()),
00348 this,
00349 SLOT(actionChangeDefaultColor()));
00350
00351
00352
00353 WMenu *mActions = fContextMenu->addMenu("&Actions");
00354 WAction *createEPS = mActions->addAction("Save as ...");
00355 WObject ::connect(createEPS,
00356 SIGNAL(triggered()),
00357 this,
00358 SLOT(actionSaveImage()));
00359
00360
00361 WAction *movieParameters = mActions->addAction("Movie parameters...");
00362 WObject ::connect(movieParameters,
00363 SIGNAL(triggered()),
00364 this,
00365 SLOT(actionMovieParameters()));
00366
00367
00368
00369
00370
00371 WMenu *mSpecial = fContextMenu->addMenu("S&pecial");
00372 WMenu *mTransparency = mSpecial->addMenu("Transparency");
00373 WAction *transparencyOn = mTransparency->addAction("On");
00374 WAction *transparencyOff = mTransparency->addAction("Off");
00375
00376 if (transparency_enabled == false) {
00377 createRadioAction(transparencyOn,transparencyOff,SLOT(toggleTransparency(bool)),2);
00378 } else if (transparency_enabled == true) {
00379 createRadioAction(transparencyOn,transparencyOff,SLOT(toggleTransparency(bool)),1);
00380 } else {
00381 mSpecial->clear();
00382 }
00383
00384
00385 WMenu *mAntialiasing = mSpecial->addMenu("Antialiasing");
00386 WAction *antialiasingOn = mAntialiasing->addAction("On");
00387 WAction *antialiasingOff = mAntialiasing->addAction("Off");
00388
00389 if (antialiasing_enabled == false) {
00390 createRadioAction(antialiasingOn,antialiasingOff,SLOT(toggleAntialiasing(bool)),2);
00391 } else if (antialiasing_enabled == true) {
00392 createRadioAction(antialiasingOn,antialiasingOff,SLOT(toggleAntialiasing(bool)),1);
00393 } else {
00394 mAntialiasing->clear();
00395 }
00396
00397 WMenu *mHaloing = mSpecial->addMenu("Haloing");
00398 WAction *haloingOn = mHaloing->addAction("On");
00399 WAction *haloingOff = mHaloing->addAction("Off");
00400
00401 if (haloing_enabled == false) {
00402 createRadioAction(haloingOn,haloingOff,SLOT(toggleHaloing(bool)),2);
00403 } else if (haloing_enabled == true) {
00404 createRadioAction(haloingOn,haloingOff,SLOT(toggleHaloing(bool)),1);
00405 } else {
00406 mHaloing->clear();
00407 }
00408
00409 WMenu *mAux = mSpecial->addMenu("Auxiliary edges");
00410 WAction *auxOn = mAux->addAction("On");
00411 WAction *auxOff = mAux->addAction("Off");
00412 if (!fVP.IsAuxEdgeVisible()) {
00413 createRadioAction(auxOn,auxOff,SLOT(toggleAux(bool)),1);
00414 } else {
00415 createRadioAction(auxOn,auxOff,SLOT(toggleAux(bool)),2);
00416 }
00417
00418
00419
00420 WMenu *mFullScreen = mSpecial->addMenu("&Full screen");
00421 fFullScreenOn = mFullScreen->addAction("On");
00422 fFullScreenOff = mFullScreen->addAction("Off");
00423 createRadioAction(fFullScreenOn,fFullScreenOff,SLOT(toggleFullScreen(bool)),2);
00424
00425 }
00426
00427
00428 void G4OpenGLWtViewer::G4manageContextMenuEvent(WContextMenuEvent *e)
00429 {
00430 if (!fGLWindow) {
00431 G4cerr << "Visualization window not defined, please choose one before" << G4endl;
00432 } else {
00433
00434 if (!fContextMenu)
00435 createPopupMenu();
00436
00437
00438 if ( fContextMenu ) {
00439 fContextMenu->exec( e->globalPos() );
00440
00441 }
00442 }
00443 e->accept();
00444 }
00445
00446
00455 void G4OpenGLWtViewer::createRadioAction(WAction *action1,WAction *action2, const std::string& method,unsigned int nCheck) {
00456
00457 action1->setCheckable(true);
00458 action2->setCheckable(true);
00459
00460 if (nCheck ==1)
00461 action1->setChecked (true);
00462 else
00463 action2->setChecked (true);
00464
00465 WObject ::connect(action1, SIGNAL(triggered(bool)),action2, SLOT(toggle()));
00466 WObject ::connect(action2, SIGNAL(triggered(bool)),action1, SLOT(toggle()));
00467
00468 WObject ::connect(action1, SIGNAL(toggled(bool)),this, method.c_str());
00469
00470 }
00471
00475 void G4OpenGLWtViewer::actionMouseRotate() {
00476 emit toggleMouseAction(STYLE1);
00477 }
00478
00479
00483 void G4OpenGLWtViewer::actionMouseMove() {
00484 emit toggleMouseAction(STYLE2);
00485 }
00486
00487
00491 void G4OpenGLWtViewer::actionMousePick() {
00492 emit toggleMouseAction(STYLE3);
00493 }
00494
00495
00499 void G4OpenGLWtViewer::actionDrawingWireframe() {
00500 emit toggleDrawingAction(1);
00501 }
00502
00506 void G4OpenGLWtViewer::actionDrawingLineRemoval() {
00507 emit toggleDrawingAction(2);
00508 }
00509
00513 void G4OpenGLWtViewer::actionDrawingSurfaceRemoval() {
00514 emit toggleDrawingAction(3);
00515 }
00516
00520 void G4OpenGLWtViewer::actionDrawingLineSurfaceRemoval() {
00521 emit toggleDrawingAction(4);
00522 }
00523
00524
00529 void G4OpenGLWtViewer::toggleMouseAction(mouseActions aAction) {
00530
00531 if ((aAction == STYLE1) ||
00532 (aAction == STYLE2) ||
00533 (aAction == STYLE3)) {
00534 fRotateAction->setChecked (false);
00535 fMoveAction->setChecked (false);
00536 fPickAction->setChecked (false);
00537 fVP.SetPicking(false);
00538 fMouseAction = aAction;
00539 }
00540
00541 if (aAction == STYLE1) {
00542 showShortcuts();
00543 fRotateAction->setChecked (true);
00544 } else if (aAction == STYLE2) {
00545 fMoveAction->setChecked (true);
00546 } else if (aAction == STYLE3) {
00547 fPickAction->setChecked (true);
00548 fVP.SetPicking(true);
00549 }
00550 }
00551
00552 #endif
00553
00556 void G4OpenGLWtViewer::showShortcuts() {
00557 G4cout << "========= Mouse Shortcuts =========" << G4endl;
00558 if (fMouseAction == STYLE1) {
00559 G4cout << "Click and move mouse to rotate volume " << G4endl;
00560 G4cout << "ALT + Click and move mouse to rotate volume (View Direction)" << G4endl;
00561 G4cout << "CTRL + Click and zoom mouse to zoom in/out" << G4endl;
00562 G4cout << "SHIFT + Click and zoommove camera point of view" << G4endl;
00563 } else if (fMouseAction == STYLE2) {
00564 G4cout << "Move camera point of view with mouse" << G4endl;
00565 } else if (fMouseAction == STYLE3) {
00566 G4cout << "Click and pick " << G4endl;
00567 }
00568 G4cout << "========= Move Shortcuts =========" << G4endl;
00569 G4cout << "Press left/right arrows to move volume left/right" << G4endl;
00570 G4cout << "Press up/down arrows to move volume up/down" << G4endl;
00571 G4cout << "Press '+'/'-' to move volume toward/forward" << G4endl;
00572 G4cout << G4endl;
00573 G4cout << "========= Rotation (Theta/Phi) Shortcuts =========" << G4endl;
00574 G4cout << "Press SHIFT + left/right arrows to rotate volume left/right" << G4endl;
00575 G4cout << "Press SHIFT + up/down arrows to rotate volume up/down" << G4endl;
00576 G4cout << G4endl;
00577 G4cout << "========= Rotation (View Direction) Shortcuts =========" << G4endl;
00578 G4cout << "Press ALT + left/right to rotate volume around vertical direction" << G4endl;
00579 G4cout << "Press ALT + up/down to rotate volume around horizontal direction" << G4endl;
00580 G4cout << G4endl;
00581 G4cout << "========= Zoom View =========" << G4endl;
00582 G4cout << "Press CTRL + '+'/'-' to zoom into volume" << G4endl;
00583 G4cout << G4endl;
00584 G4cout << "========= Misc =========" << G4endl;
00585 G4cout << "Press ALT +/- to slow/speed rotation/move" << G4endl;
00586 G4cout << "Press H to reset view" << G4endl;
00587 G4cout << "Press Esc to exit FullScreen" << G4endl;
00588 G4cout << G4endl;
00589 G4cout << "========= Video =========" << G4endl;
00590 G4cout << "In video mode : " << G4endl;
00591 G4cout << " Press SPACE to Start/Pause video recording " << G4endl;
00592 G4cout << " Press RETURN to Stop video recording " << G4endl;
00593 G4cout << G4endl;
00594 }
00595
00596
00597
00598 #ifdef _A_FINIR_FIXME
00599
00610 void G4OpenGLWtViewer::toggleDrawingAction(int aAction) {
00611
00612 G4ViewParameters::DrawingStyle d_style = G4ViewParameters::wireframe;
00613
00614
00615
00616 if ((aAction >0) && (aAction <5)) {
00617 fDrawingWireframe->setChecked (false);
00618 fDrawingLineRemoval->setChecked (false);
00619 fDrawingSurfaceRemoval->setChecked (false);
00620 fDrawingLineSurfaceRemoval->setChecked (false);
00621 }
00622 if (aAction ==1) {
00623 fDrawingWireframe->setChecked (true);
00624
00625 d_style = G4ViewParameters::wireframe;
00626
00627 } else if (aAction ==2) {
00628 fDrawingLineRemoval->setChecked (true);
00629
00630 d_style = G4ViewParameters::hlr;
00631
00632 } else if (aAction ==3) {
00633 fDrawingSurfaceRemoval->setChecked (true);
00634
00635 d_style = G4ViewParameters::hsr;
00636
00637 } else if (aAction ==4) {
00638 fDrawingLineSurfaceRemoval->setChecked (true);
00639 d_style = G4ViewParameters::hlhsr;
00640 }
00641 fVP.SetDrawingStyle(d_style);
00642
00643 updateWWidget();
00644 }
00645
00646
00657 void G4OpenGLWtViewer::toggleRepresentation(bool check) {
00658
00659 G4ViewParameters::RepStyle style;
00660 if (check == 1) {
00661 style = G4ViewParameters::polyhedron;
00662 } else {
00663 style = G4ViewParameters::nurbs;
00664 }
00665 fVP.SetRepStyle (style);
00666
00667 updateWWidget();
00668 }
00669
00680 void G4OpenGLWtViewer::toggleProjection(bool check) {
00681
00682 if (check == 1) {
00683 G4UImanager::GetUIpointer()->ApplyCommand("/vis/viewer/set/projection o");
00684 } else {
00685 G4UImanager::GetUIpointer()->ApplyCommand("/vis/viewer/set/projection p");
00686 }
00687 updateWWidget();
00688 }
00689
00690
00695 void G4OpenGLWtViewer::toggleTransparency(bool check) {
00696
00697 if (check) {
00698 transparency_enabled = false;
00699 } else {
00700 transparency_enabled = true;
00701 }
00702 SetNeedKernelVisit (true);
00703 updateWWidget();
00704 }
00705
00710 void G4OpenGLWtViewer::toggleAntialiasing(bool check) {
00711
00712 if (!check) {
00713 antialiasing_enabled = false;
00714 glDisable (GL_LINE_SMOOTH);
00715 glDisable (GL_POLYGON_SMOOTH);
00716 } else {
00717 antialiasing_enabled = true;
00718 glEnable (GL_LINE_SMOOTH);
00719 glHint (GL_LINE_SMOOTH_HINT, GL_NICEST);
00720 glEnable (GL_POLYGON_SMOOTH);
00721 glHint (GL_POLYGON_SMOOTH_HINT, GL_NICEST);
00722 }
00723
00724 updateWWidget();
00725 }
00726
00731
00732 void G4OpenGLWtViewer::toggleHaloing(bool check) {
00733 if (check) {
00734 haloing_enabled = false;
00735 } else {
00736 haloing_enabled = true;
00737 }
00738
00739 updateWWidget();
00740
00741 }
00742
00747 void G4OpenGLWtViewer::toggleAux(bool check) {
00748 if (check) {
00749 fVP.SetAuxEdgeVisible(true);
00750 } else {
00751 fVP.SetAuxEdgeVisible(false);
00752 }
00753 SetNeedKernelVisit (true);
00754 updateWWidget();
00755 }
00756
00760 void G4OpenGLWtViewer::toggleFullScreen(bool check) {
00761 if (check != fGLWindow->isFullScreen()) {
00762 fGLWindow->setWindowState(fGLWindow->windowState() ^ Wt::WindowFullScreen);
00763 G4cerr << "This version of Wt could not do fullScreen. Resizing the widget is the only solution available." << G4endl;
00764 }
00765 }
00766 #endif
00767
00768 #ifdef _A_FINIR_FIXME
00769 void G4OpenGLWtViewer::savePPMToTemp() {
00770 if (fMovieTempFolderPath == "") {
00771 return;
00772 }
00773 Wt::WString fileName ="Test"+Wt::WString::number(fRecordFrameNumber)+".ppm";
00774 Wt::WString filePath =fMovieTempFolderPath+fileName;
00775
00776 WImage image;
00777 image = fWindow->grabFrameBuffer();
00778 bool res = false;
00779
00780 res = image.save(filePath,0);
00781 if (res == false) {
00782 resetRecording();
00783 setRecordingInfos("Can't save tmp file "+filePath);
00784 return;
00785 }
00786
00787 setRecordingInfos("File "+fileName+" saved");
00788 fRecordFrameNumber++;
00789 }
00790
00791
00792
00793 void G4OpenGLWtViewer::actionSaveImage() {
00794 Wt::WString filters;
00795 WList<WByteArray> formats = WImageWriter::supportedImageFormats ();
00796 for (int i = 0; i < formats.size(); ++i) {
00797 filters +=formats.at(i) + ";;";
00798 }
00799 filters += "eps;;";
00800 filters += "ps;;";
00801 filters += "pdf";
00802 Wt::WString* selectedFormat = new Wt::WString();
00803 std::string name;
00804 name = WFileDialog::getSaveFileName ( fGLWindow,
00805 tr("Save as ..."),
00806 ".",
00807 filters,
00808 selectedFormat ).toUTF8().c_str();
00809
00810 if (name.empty()) {
00811 return;
00812 }
00813 name += "." + selectedFormat->toUTF8();
00814 Wt::WString format = selectedFormat->toLower();
00815 setPrintFilename(name.c_str(),0);
00816 G4OpenGLWtExportDialog* exportDialog= new G4OpenGLWtExportDialog(fGLWindow,format,fWindow->height(),fWindow->width());
00817 if( exportDialog->exec()) {
00818
00819 WImage image;
00820 bool res = false;
00821 if ((exportDialog->getWidth() !=fWindow->width()) ||
00822 (exportDialog->getHeight() !=fWindow->height())) {
00823 setPrintSize(exportDialog->getWidth(),exportDialog->getHeight());
00824 if ((format != Wt::WString("eps")) && (format != Wt::WString("ps"))) {
00825 G4cerr << "Export->Change Size : This function is not implemented, to export in another size, please resize your frame to what you need" << G4endl;
00826
00827
00828
00829
00830
00831
00832
00833
00834
00835
00836
00837
00838
00839
00840
00841
00842 }
00843 } else {
00844 image = fWindow->grabFrameBuffer();
00845 }
00846 if (format == Wt::WString("eps")) {
00847 fVectoredPs = exportDialog->getVectorEPS();
00848 printEPS();
00849 } else if (format == "ps") {
00850 fVectoredPs = true;
00851 printEPS();
00852 } else if (format == "pdf") {
00853
00854 res = printPDF(name,exportDialog->getNbColor(),image);
00855
00856 } else if ((format == "tif") ||
00857 (format == "tiff") ||
00858 (format == "jpg") ||
00859 (format == "jpeg") ||
00860 (format == "png") ||
00861 (format == "pbm") ||
00862 (format == "pgm") ||
00863 (format == "ppm") ||
00864 (format == "bmp") ||
00865 (format == "xbm") ||
00866 (format == "xpm")) {
00867 res = image.save(Wt::WString(name.c_str()),0,exportDialog->getSliderValue());
00868 } else {
00869 G4cerr << "This version of G4UI Could not generate the selected format" << G4endl;
00870 }
00871 if ((format == Wt::WString("eps")) && (format == Wt::WString("ps"))) {
00872 if (res == false) {
00873 G4cerr << "Error while saving file... "<<name.c_str()<< G4endl;
00874 } else {
00875 G4cout << "File "<<name.c_str()<<" has been saved " << G4endl;
00876 }
00877 }
00878
00879 } else {
00880 return;
00881 }
00882
00883 }
00884 #endif
00885
00886
00887 #ifdef _A_FINIR_FIXME
00888 void G4OpenGLWtViewer::actionChangeBackgroundColor() {
00889
00890
00891
00892
00893
00894
00895
00896 WColor color;
00897 color = WColorDialog::getColor(Wt::black, fGLWindow);
00898 if (color.isValid()) {
00899 Wt::WString com = "/vis/viewer/set/background ";
00900 Wt::WString num;
00901 com += num.setNum(((float)color.red())/256)+" ";
00902 com += num.setNum(((float)color.green())/256)+" ";
00903 com += num.setNum(((float)color.blue())/256)+" ";
00904 G4UImanager::GetUIpointer()->ApplyCommand(com.toUTF8().c_str());
00905 updateWWidget();
00906 }
00907 }
00908
00909 void G4OpenGLWtViewer::actionChangeTextColor() {
00910
00911 WColor color;
00912 color = WColorDialog::getColor(Wt::yellow, fGLWindow);
00913 if (color.isValid()) {
00914 Wt::WString com = "/vis/viewer/set/defaultTextColour ";
00915 Wt::WString num;
00916 com += num.setNum(((float)color.red())/256)+" ";
00917 com += num.setNum(((float)color.green())/256)+" ";
00918 com += num.setNum(((float)color.blue())/256)+" ";
00919 G4UImanager::GetUIpointer()->ApplyCommand(com.toUTF8().c_str());
00920 updateWWidget();
00921 }
00922 }
00923
00924 void G4OpenGLWtViewer::actionChangeDefaultColor() {
00925
00926 WColor color;
00927 color = WColorDialog::getColor(Wt::white, fGLWindow);
00928 printf("actionChangeDefaultColor\n");
00929 if (color.isValid()) {
00930 Wt::WString com = "/vis/viewer/set/defaultColour ";
00931 Wt::WString num;
00932 com += num.setNum(((float)color.red())/256)+" ";
00933 com += num.setNum(((float)color.green())/256)+" ";
00934 com += num.setNum(((float)color.blue())/256)+" ";
00935 G4UImanager::GetUIpointer()->ApplyCommand(com.toUTF8().c_str());
00936 updateWWidget();
00937 }
00938 }
00939
00940
00941 void G4OpenGLWtViewer::actionMovieParameters() {
00942 showMovieParametersDialog();
00943 }
00944
00945
00946 void G4OpenGLWtViewer::showMovieParametersDialog() {
00947 if (!fMovieParametersDialog) {
00948 fMovieParametersDialog= new G4OpenGLWtMovieDialog(this,fGLWindow);
00949 displayRecordingStatus();
00950 fMovieParametersDialog->checkEncoderSwParameters();
00951 fMovieParametersDialog->checkSaveFileNameParameters();
00952 fMovieParametersDialog->checkTempFolderParameters();
00953 if (getEncoderPath() == "") {
00954 setRecordingInfos("mpeg_encode is needed to encode in video format. It is available here: http://bmrc.berkeley.edu/frame/research/mpeg/");
00955 }
00956 }
00957 fMovieParametersDialog->show();
00958 }
00959 #endif
00960
00961
00962
00963
00964
00965
00966
00967
00968
00969
00970
00971
00972
00973
00974
00975
00976
00977
00978
00979 void G4OpenGLWtViewer::FinishView()
00980 {
00981 glFlush ();
00982
00983
00984
00985 }
00986
00991 void G4OpenGLWtViewer::G4MousePressEvent(Wt::WMouseEvent *event)
00992 {
00993 if (event->button() & Wt::WMouseEvent::LeftButton) {
00994 #ifdef _A_FINIR_FIXME
00995 fWindow->setMouseTracking(true);
00996 #endif
00997 fAutoMove = false;
00998 fLastPos1 = Wt::WPoint(event->widget().x,event->widget().y);
00999 fLastPos2 = fLastPos1;
01000 fLastPos3 = fLastPos2;
01001 fLastEventTime->start();
01002 if (fMouseAction == STYLE3){
01003 Pick(event->widget().x,event->widget().y);
01004 }
01005 }
01006 }
01007
01010 void G4OpenGLWtViewer::G4MouseReleaseEvent()
01011 {
01012 fSpinningDelay = fLastEventTime->elapsed();
01013 Wt::WPoint delta = Wt::WPoint(fLastPos3.x()-fLastPos1.x(),fLastPos3.y()-fLastPos1.y());
01014 if ((delta.x() == 0) && (delta.y() == 0)) {
01015 return;
01016 }
01017 if (fSpinningDelay < fLaunchSpinDelay ) {
01018 fAutoMove = true;
01019 Wt::WTime lastMoveTime;
01020 lastMoveTime.start();
01021
01022 float correctionFactor = 5;
01023 while (fAutoMove) {
01024 if ( lastMoveTime.elapsed () >= (int)(1000/fNbMaxFramesPerSec)) {
01025 float lTime = 1000/((float)lastMoveTime.elapsed ());
01026 if (((((float)delta.x())/correctionFactor)*lTime > fNbMaxAnglePerSec) ||
01027 ((((float)delta.x())/correctionFactor)*lTime < -fNbMaxAnglePerSec) ) {
01028 correctionFactor = (float)delta.x()*(lTime/fNbMaxAnglePerSec);
01029 if (delta.x() <0 ) {
01030 correctionFactor = -correctionFactor;
01031 }
01032 }
01033 if (((((float)delta.y())/correctionFactor)*lTime > fNbMaxAnglePerSec) ||
01034 ((((float)delta.y())/correctionFactor)*lTime < -fNbMaxAnglePerSec) ) {
01035 correctionFactor = (float)delta.y()*(lTime/fNbMaxAnglePerSec);
01036 if (delta.y() <0 ) {
01037 correctionFactor = -correctionFactor;
01038 }
01039 }
01040
01041
01042
01043
01044
01045
01046
01047
01048 if (fMouseAction == STYLE1) {
01049 if (fNoKeyPress) {
01050 rotateWtScene(((float)delta.x())/correctionFactor,((float)delta.y())/correctionFactor);
01051 } else if (fAltKeyPress) {
01052 rotateWtSceneInViewDirection(((float)delta.x())/correctionFactor,((float)delta.y())/correctionFactor);
01053 }
01054
01055 } else if (fMouseAction == STYLE2) {
01056 moveScene(-((float)delta.x())/correctionFactor,-((float)delta.y())/correctionFactor,0,true);
01057 }
01058 lastMoveTime.start();
01059 }
01060 #ifdef _A_FINIR_FIXME
01061 ((Wt::WApplication*)G4Wt::getInstance ())->processEvents();
01062 #endif
01063 }
01064 }
01065 #ifdef _A_FINIR_FIXME
01066 fWindow->setMouseTracking(false);
01067 #endif
01068 }
01069
01070
01071 void G4OpenGLWtViewer::G4MouseDoubleClickEvent()
01072 {
01073 #ifdef _A_FINIR_FIXME
01074 fWindow->setMouseTracking(true);
01075 #endif
01076 }
01077
01078
01086 void G4OpenGLWtViewer::G4MouseMoveEvent(Wt::WMouseEvent *event)
01087 {
01088
01089 Wt::WMouseEvent::Button mButtons = event->button();
01090
01091 #ifdef _A_FINIR_FIXME
01092 updateKeyModifierState(event->modifiers());
01093 #endif
01094
01095 if (fAutoMove) {
01096 return;
01097 }
01098
01099 fLastPos3 = fLastPos2;
01100 fLastPos2 = fLastPos1;
01101 fLastPos1 = Wt::WPoint(event->widget().x, event->widget().y);
01102
01103 int deltaX = fLastPos2.x()-fLastPos1.x();
01104 int deltaY = fLastPos2.y()-fLastPos1.y();
01105
01106 if (fMouseAction == STYLE1) {
01107 if (mButtons & Wt::WMouseEvent::LeftButton) {
01108 if (fNoKeyPress) {
01109 rotateWtScene(((float)deltaX),((float)deltaY));
01110 } else if (fAltKeyPress) {
01111 rotateWtSceneInViewDirection(((float)deltaX),((float)deltaY));
01112 } else if (fShiftKeyPress) {
01113 unsigned int sizeWin;
01114 sizeWin = getWinWidth();
01115 if (getWinHeight() < getWinWidth()) {
01116 sizeWin = getWinHeight();
01117 }
01118
01119
01120 float factor = ((float)100/(float)sizeWin) ;
01121 moveScene(-(float)deltaX*factor,-(float)deltaY*factor,0,false);
01122 } else if (fControlKeyPress) {
01123 fVP.SetZoomFactor(fVP.GetZoomFactor()*(1+((float)deltaY)));
01124 }
01125 }
01126 } else if (fMouseAction == STYLE2) {
01127 if (mButtons & Wt::WMouseEvent::LeftButton) {
01128 moveScene(-deltaX,-deltaY,0,true);
01129 }
01130 }
01131
01132 fLastEventTime->start();
01133 }
01134
01135
01143 void G4OpenGLWtViewer::moveScene(float dx,float dy, float dz,bool mouseMove)
01144 {
01145 if (fHoldMoveEvent)
01146 return;
01147 fHoldMoveEvent = true;
01148
01149 G4double coefTrans = 0;
01150 GLdouble coefDepth = 0;
01151 if(mouseMove) {
01152 coefTrans = ((G4double)getSceneNearWidth())/((G4double)getWinWidth());
01153 if (getWinHeight() <getWinWidth()) {
01154 coefTrans = ((G4double)getSceneNearWidth())/((G4double)getWinHeight());
01155 }
01156 } else {
01157 coefTrans = getSceneNearWidth()*fDeltaSceneTranslation;
01158 coefDepth = getSceneDepth()*fDeltaDepth;
01159 }
01160 fVP.IncrementPan(-dx*coefTrans,dy*coefTrans,dz*coefDepth);
01161 updateWWidget();
01162 if (fAutoMove)
01163 #ifdef _A_FINIR_FIXME
01164 ((WApplication*)G4Wt::getInstance ())->processEvents();
01165 #endif
01166
01167 fHoldMoveEvent = false;
01168 }
01169
01170
01176 void G4OpenGLWtViewer::rotateWtScene(float dx, float dy)
01177 {
01178 if (fHoldRotateEvent)
01179 return;
01180 fHoldRotateEvent = true;
01181
01182 if( dx != 0) {
01183 rotateScene(dx,0);
01184 }
01185 if( dy != 0) {
01186 rotateScene(0,dy);
01187 }
01188 updateWWidget();
01189
01190 fHoldRotateEvent = false;
01191 }
01192
01198 void G4OpenGLWtViewer::rotateWtSceneInViewDirection(float dx, float dy)
01199 {
01200 if (fHoldRotateEvent)
01201 return;
01202 fHoldRotateEvent = true;
01203
01204 fXRot +=dx;
01205 fYRot +=dy;
01206
01207 rotateSceneInViewDirection(dx/100,dy/100);
01208
01209 updateWWidget();
01210
01211 fHoldRotateEvent = false;
01212 }
01213
01219 void G4OpenGLWtViewer::rotateWtCamera(float dx, float dy)
01220 {
01221 if (fHoldRotateEvent)
01222 return;
01223 fHoldRotateEvent = true;
01224
01225 rotateScene(dx,dy);
01226 updateWWidget();
01227
01228 fHoldRotateEvent = false;
01229 }
01230
01236 void G4OpenGLWtViewer::rotateWtCameraInViewDirection(float dx, float dy)
01237 {
01238 if (fHoldRotateEvent)
01239 return;
01240 fHoldRotateEvent = true;
01241
01242 fVP.SetUpVector(G4Vector3D(0.0, 1.0, 0.0));
01243 fVP.SetViewAndLights (G4Vector3D(0.0, 0.0, 1.0));
01244
01245
01246 fXRot +=dx;
01247 fYRot +=dy;
01248
01249 rotateSceneInViewDirection(fXRot/100,fYRot/100);
01250
01251 updateWWidget();
01252
01253 fHoldRotateEvent = false;
01254 }
01255
01256
01257
01258
01259
01264 void G4OpenGLWtViewer::rescaleImage(
01265 int
01266 ,int
01267 ){
01268
01269
01270
01271
01272
01273
01274
01275
01276
01277
01278
01279 }
01280
01281
01282
01283 #ifdef _A_FINIR_FIXME
01284
01290 bool G4OpenGLWtViewer::printPDF (
01291 const std::string aFilename
01292 ,int aInColor
01293 ,WImage aImage
01294 )
01295 {
01296
01297 WPrinter printer;
01298
01299
01300
01301
01302
01303 if ((!aImage.isGrayscale ()) &&(aInColor ==1 )) {
01304 aImage = aImage.convertToFormat ( aImage.format(), Wt::MonoOnly);
01305 }
01306
01307
01308 if (aFilename.substr(aFilename.size()-3) == ".ps") {
01309 #if WT_VERSION > 0x040200
01310 printer.setOutputFormat(WPrinter::PostScriptFormat);
01311 #endif
01312 } else {
01313 #if WT_VERSION > 0x040100
01314 printer.setOutputFormat(WPrinter::PdfFormat);
01315 #endif
01316 }
01317 #if WT_VERSION > 0x040100
01318 printer.setOutputFileName(Wt::WString(aFilename.c_str()));
01319 #endif
01320
01321 WPainter paint(&printer);
01322 paint.drawImage (0,0,aImage);
01323 paint.end();
01324 return true;
01325 }
01326
01327
01328
01329 void G4OpenGLWtViewer::G4wheelEvent (Wt::WWheelEvent * event)
01330 {
01331 fVP.SetZoomFactor(fVP.GetZoomFactor()+(fVP.GetZoomFactor()*(event->delta())/1200));
01332 updateWWidget();
01333 }
01334 #endif
01335
01336 void G4OpenGLWtViewer::G4keyPressEvent (Wt::WKeyEvent * event)
01337 {
01338 if (fHoldKeyEvent)
01339 return;
01340
01341 fHoldKeyEvent = true;
01342
01343
01344
01345 #ifdef _A_FINIR_FIXME
01346 updateKeyModifierState(event->modifiers());
01347 #endif
01348 if ((fNoKeyPress)) {
01349 if (event->key() == Wt::Key_Down) {
01350 moveScene(0,1,0,false);
01351 }
01352 else if (event->key() == Wt::Key_Up) {
01353 moveScene(0,-1,0,false);
01354 }
01355 if (event->key() == Wt::Key_Left) {
01356 moveScene(-1,0,0,false);
01357 }
01358 else if (event->key() == Wt::Key_Right) {
01359 moveScene(1,0,0,false);
01360 }
01361 if (event->text() == Wt::WString("-") ) {
01362 moveScene(0,0,1,false);
01363 }
01364 else if (event->text() == Wt::WString("+")) {
01365 moveScene(0,0,-1,false);
01366 }
01367
01368
01369 if (event->key() == Wt::Key_Escape) {
01370 #ifdef _A_FINIR_FIXME
01371 toggleFullScreen(false);
01372 #endif
01373 }
01374 }
01375
01376
01377
01378
01379
01380
01381 #ifdef _A_FINIR_FIXME
01382 if ( (event->key() == Wt::Key_Enter)){
01383 stopVideo();
01384 }
01385 if (event->key() == Wt::Key_Space){
01386 startPauseVideo();
01387 }
01388 #endif
01389
01390
01391 if (event->key() == Wt::Key_H){
01392 fDeltaRotation = 1;
01393 fDeltaSceneTranslation = 0.01;
01394 fDeltaDepth = 0.01;
01395 fDeltaZoom = 0.05;
01396 fDeltaMove = 0.05;
01397
01398 fVP.SetZoomFactor(1.);
01399 fVP.SetUpVector(G4Vector3D (0., 1., 0.));
01400 fVP.SetViewAndLights (G4Vector3D (0., 0., 1.));
01401
01402 updateWWidget();
01403 }
01404
01405
01406 if (fShiftKeyPress) {
01407 if (event->key() == Wt::Key_Down) {
01408 rotateWtScene(0,-fDeltaRotation);
01409 }
01410 else if (event->key() == Wt::Key_Up) {
01411 rotateWtScene(0,fDeltaRotation);
01412 }
01413 if (event->key() == Wt::Key_Left) {
01414 rotateWtScene(fDeltaRotation,0);
01415 }
01416 else if (event->key() == Wt::Key_Right) {
01417 rotateWtScene(-fDeltaRotation,0);
01418 }
01419
01420
01421 }
01422 if ((fAltKeyPress)) {
01423 if (event->key() == Wt::Key_Down) {
01424 rotateWtSceneInViewDirection(0,-fDeltaRotation);
01425 }
01426 else if (event->key() == Wt::Key_Up) {
01427 rotateWtSceneInViewDirection(0,fDeltaRotation);
01428 }
01429 if (event->key() == Wt::Key_Left) {
01430 rotateWtSceneInViewDirection(fDeltaRotation,0);
01431 }
01432 else if (event->key() == Wt::Key_Right) {
01433 rotateWtSceneInViewDirection(-fDeltaRotation,0);
01434 }
01435
01436
01437 if (event->text() == Wt::WString("+")) {
01438 fDeltaRotation = fDeltaRotation/0.7;
01439 G4cout << "Auto-rotation set to : " << fDeltaRotation << G4endl;
01440 }
01441 else if (event->text() == Wt::WString("-")) {
01442 fDeltaRotation = fDeltaRotation*0.7;
01443 G4cout << "Auto-rotation set to : " << fDeltaRotation << G4endl;
01444 }
01445
01446
01447 }
01448 if ((fControlKeyPress)) {
01449 if (event->text() == Wt::WString("+")) {
01450 fVP.SetZoomFactor(fVP.GetZoomFactor()*(1+fDeltaZoom));
01451 updateWWidget();
01452 }
01453 else if (event->text() == Wt::WString("-")) {
01454 fVP.SetZoomFactor(fVP.GetZoomFactor()*(1-fDeltaZoom));
01455 updateWWidget();
01456 }
01457 }
01458
01459 fHoldKeyEvent = false;
01460 }
01461
01462
01463 #ifdef _A_FINIR_FIXME
01464 void G4OpenGLWtViewer::updateKeyModifierState(Wt::KeyboardModifiers modifier) {
01465
01466
01467 fNoKeyPress = true;
01468 fAltKeyPress = false;
01469 fShiftKeyPress = false;
01470 fControlKeyPress = false;
01471
01472 if (modifier & Wt::AltModifier ) {
01473 fAltKeyPress = true;
01474 fNoKeyPress = false;
01475 }
01476 if (modifier & Wt::ShiftModifier ) {
01477 fShiftKeyPress = true;
01478 fNoKeyPress = false;
01479 }
01480 if (modifier & Wt::ControlModifier ) {
01481 fControlKeyPress = true;
01482 fNoKeyPress = false;
01483 }
01484 }
01485
01488 void G4OpenGLWtViewer::stopVideo() {
01489
01490
01491 if (!fMovieParametersDialog) {
01492 showMovieParametersDialog();
01493 }
01494 setRecordingStatus(STOP);
01495
01496 if (fRecordFrameNumber >0) {
01497
01498 if (!(fMovieParametersDialog->checkEncoderSwParameters())) {
01499 setRecordingStatus(BAD_ENCODER);
01500 } else if (!(fMovieParametersDialog->checkSaveFileNameParameters())) {
01501 setRecordingStatus(BAD_OUTPUT);
01502 }
01503 } else {
01504 resetRecording();
01505 setRecordingInfos("No frame to encode.");
01506 }
01507 }
01508
01511 void G4OpenGLWtViewer::saveVideo() {
01512
01513
01514 if (!fMovieParametersDialog) {
01515 showMovieParametersDialog();
01516 }
01517
01518 fMovieParametersDialog->checkEncoderSwParameters();
01519 fMovieParametersDialog->checkSaveFileNameParameters();
01520
01521 if (fRecordingStep == STOP) {
01522 setRecordingStatus(SAVE);
01523 generateMpegEncoderParameters();
01524 encodeVideo();
01525 }
01526 }
01527
01528
01531 void G4OpenGLWtViewer::startPauseVideo() {
01532
01533
01534
01535 if (( fRecordingStep == WAIT)) {
01536 if ( fRecordFrameNumber == 0) {
01537 if (getTempFolderPath() == "") {
01538 showMovieParametersDialog();
01539 setRecordingInfos("You should specified the temp folder in order to make movie");
01540 return;
01541 } else {
01542
01543 Wt::WString tmp = removeTempFolder();
01544 if (tmp !="") {
01545 setRecordingInfos(tmp);
01546 return;
01547 }
01548 tmp = createTempFolder();
01549 if (tmp != "") {
01550 setRecordingInfos("Can't create temp folder."+tmp);
01551 return;
01552 }
01553 }
01554 }
01555 }
01556 if ((fRecordingStep == WAIT)) {
01557 setRecordingStatus(START);
01558 } else if (fRecordingStep == START) {
01559 setRecordingStatus(PAUSE);
01560 } else if (fRecordingStep == PAUSE) {
01561 setRecordingStatus(CONTINUE);
01562 } else if (fRecordingStep == CONTINUE) {
01563 setRecordingStatus(PAUSE);
01564 }
01565 }
01566
01567 void G4OpenGLWtViewer::setRecordingStatus(RECORDING_STEP step) {
01568
01569 fRecordingStep = step;
01570 displayRecordingStatus();
01571 }
01572
01573
01574 void G4OpenGLWtViewer::displayRecordingStatus() {
01575
01576 Wt::WString txtStatus = "";
01577 if (fRecordingStep == WAIT) {
01578 txtStatus = "Waiting to start...";
01579 fRecordFrameNumber = 0;
01580 } else if (fRecordingStep == START) {
01581 txtStatus = "Start Recording...";
01582 } else if (fRecordingStep == PAUSE) {
01583 txtStatus = "Pause Recording...";
01584 } else if (fRecordingStep == CONTINUE) {
01585 txtStatus = "Continue Recording...";
01586 } else if (fRecordingStep == STOP) {
01587 txtStatus = "Stop Recording...";
01588 } else if (fRecordingStep == READY_TO_ENCODE) {
01589 txtStatus = "Ready to Encode...";
01590 } else if (fRecordingStep == ENCODING) {
01591 txtStatus = "Encoding...";
01592 } else if (fRecordingStep == FAILED) {
01593 txtStatus = "Failed to encode...";
01594 } else if ((fRecordingStep == BAD_ENCODER)
01595 || (fRecordingStep == BAD_OUTPUT)
01596 || (fRecordingStep == BAD_TMP)) {
01597 txtStatus = "Correct above errors first";
01598 } else if (fRecordingStep == SUCCESS) {
01599 txtStatus = "File encoded successfully";
01600 } else {
01601 }
01602
01603 if (fMovieParametersDialog) {
01604 fMovieParametersDialog->setRecordingStatus(txtStatus);
01605 } else {
01606 G4cout << txtStatus.toUTF8().c_str() << G4endl;
01607 }
01608 setRecordingInfos("");
01609 }
01610
01611
01612 void G4OpenGLWtViewer::setRecordingInfos(Wt::WString txt) {
01613 if (fMovieParametersDialog) {
01614 fMovieParametersDialog->setRecordingInfos(txt);
01615 } else {
01616 G4cout << txt.toUTF8().c_str() << G4endl;
01617 }
01618 }
01619
01622 void G4OpenGLWtViewer::initMovieParameters() {
01623
01624
01625
01626 fProcess = new WProcess();
01627
01628 WObject ::connect(fProcess,SIGNAL(finished ( int)),
01629 this,SLOT(processLookForFinished()));
01630 fProcess->setReadChannelMode(WProcess::MergedChannels);
01631 fProcess->start ("which mpeg_encode");
01632
01633 }
01634
01637 Wt::WString G4OpenGLWtViewer::getEncoderPath() {
01638 return fEncoderPath;
01639 }
01640
01641
01646 Wt::WString G4OpenGLWtViewer::setEncoderPath(Wt::WString path) {
01647 if (path == "") {
01648 return "File does not exist";
01649 }
01650
01651 path = WDir::cleanPath(path);
01652 WFileInfo *f = new WFileInfo(path);
01653 if (!f->exists()) {
01654 return "File does not exist";
01655 } else if (f->isDir()) {
01656 return "This is a directory";
01657 } else if (!f->isExecutable()) {
01658 return "File exist but is not executable";
01659 } else if (!f->isFile()) {
01660 return "This is not a file";
01661 }
01662 fEncoderPath = path;
01663
01664 if ((fRecordingStep == BAD_ENCODER)) {
01665 setRecordingStatus(STOP);
01666 }
01667 return "";
01668 }
01669
01670
01671 bool G4OpenGLWtViewer::isRecording(){
01672 if ((fRecordingStep == START) || (fRecordingStep == CONTINUE)) {
01673 return true;
01674 }
01675 return false;
01676 }
01677
01678 bool G4OpenGLWtViewer::isPaused(){
01679 if (fRecordingStep == PAUSE) {
01680 return true;
01681 }
01682 return false;
01683 }
01684
01685 bool G4OpenGLWtViewer::isEncoding(){
01686 if (fRecordingStep == ENCODING) {
01687 return true;
01688 }
01689 return false;
01690 }
01691
01692 bool G4OpenGLWtViewer::isWaiting(){
01693 if (fRecordingStep == WAIT) {
01694 return true;
01695 }
01696 return false;
01697 }
01698
01699 bool G4OpenGLWtViewer::isStopped(){
01700 if (fRecordingStep == STOP) {
01701 return true;
01702 }
01703 return false;
01704 }
01705
01706 bool G4OpenGLWtViewer::isFailed(){
01707 if (fRecordingStep == FAILED) {
01708 return true;
01709 }
01710 return false;
01711 }
01712
01713 bool G4OpenGLWtViewer::isSuccess(){
01714 if (fRecordingStep == SUCCESS) {
01715 return true;
01716 }
01717 return false;
01718 }
01719
01720 bool G4OpenGLWtViewer::isBadEncoder(){
01721 if (fRecordingStep == BAD_ENCODER) {
01722 return true;
01723 }
01724 return false;
01725 }
01726 bool G4OpenGLWtViewer::isBadTmp(){
01727 if (fRecordingStep == BAD_TMP) {
01728 return true;
01729 }
01730 return false;
01731 }
01732 bool G4OpenGLWtViewer::isBadOutput(){
01733 if (fRecordingStep == BAD_OUTPUT) {
01734 return true;
01735 }
01736 return false;
01737 }
01738
01739 void G4OpenGLWtViewer::setBadEncoder(){
01740 fRecordingStep = BAD_ENCODER;
01741 displayRecordingStatus();
01742 }
01743 void G4OpenGLWtViewer::setBadTmp(){
01744 fRecordingStep = BAD_TMP;
01745 displayRecordingStatus();
01746 }
01747 void G4OpenGLWtViewer::setBadOutput(){
01748 fRecordingStep = BAD_OUTPUT;
01749 displayRecordingStatus();
01750 }
01751
01752 void G4OpenGLWtViewer::setWaiting(){
01753 fRecordingStep = WAIT;
01754 displayRecordingStatus();
01755 }
01756
01757
01758 bool G4OpenGLWtViewer::isReadyToEncode(){
01759 if (fRecordingStep == READY_TO_ENCODE) {
01760 return true;
01761 }
01762 return false;
01763 }
01764
01765 void G4OpenGLWtViewer::resetRecording() {
01766 setRecordingStatus(WAIT);
01767 }
01768
01773 Wt::WString G4OpenGLWtViewer::setTempFolderPath(Wt::WString path) {
01774
01775 if (path == "") {
01776 return "Path does not exist";
01777 }
01778 path = WDir::cleanPath(path);
01779 WFileInfo *d = new WFileInfo(path);
01780 if (!d->exists()) {
01781 return "Path does not exist";
01782 } else if (!d->isDir()) {
01783 return "This is not a directory";
01784 } else if (!d->isReadable()) {
01785 return path +" is read protected";
01786 } else if (!d->isWritable()) {
01787 return path +" is write protected";
01788 }
01789
01790 if ((fRecordingStep == BAD_TMP)) {
01791 setRecordingStatus(WAIT);
01792 }
01793 fTempFolderPath = path;
01794 return "";
01795 }
01796
01799 Wt::WString G4OpenGLWtViewer::getTempFolderPath() {
01800 return fTempFolderPath;
01801 }
01802
01807 Wt::WString G4OpenGLWtViewer::setSaveFileName(Wt::WString path) {
01808
01809 if (path == "") {
01810 return "Path does not exist";
01811 }
01812
01813 WFileInfo *file = new WFileInfo(path);
01814 WDir dir = file->dir();
01815 path = WDir::cleanPath(path);
01816 if (file->exists()) {
01817 return "File already exist, please choose a new one";
01818 } else if (!dir.exists()) {
01819 return "Dir does not exist";
01820 } else if (!dir.isReadable()) {
01821 return path +" is read protected";
01822 }
01823
01824 if ((fRecordingStep == BAD_OUTPUT)) {
01825 setRecordingStatus(STOP);
01826 }
01827 fSaveFileName = path;
01828 return "";
01829 }
01830
01833 Wt::WString G4OpenGLWtViewer::getSaveFileName() {
01834 return fSaveFileName ;
01835 }
01836
01841 Wt::WString G4OpenGLWtViewer::createTempFolder() {
01842 fMovieTempFolderPath = "";
01843
01844 Wt::WString tmp = setTempFolderPath(fTempFolderPath);
01845 if (tmp != "") {
01846 return tmp;
01847 }
01848 Wt::WString sep = Wt::WString(WDir::separator());
01849 Wt::WString path = sep+"WtMovie_"+WDateTime::currentDateTime ().toString("dd-MM-yyyy_hh-mm-ss")+sep;
01850 WDir *d = new WDir(WDir::cleanPath(fTempFolderPath));
01851
01852 if (d->exists(path)) {
01853 return "Folder "+path+" already exists.Please remove it first";
01854 }
01855 if (d->mkdir(fTempFolderPath+path)) {
01856 fMovieTempFolderPath = fTempFolderPath+path;
01857 return "";
01858 } else {
01859 return "Can't create "+fTempFolderPath+path;
01860 }
01861 return "-";
01862 }
01863
01866 Wt::WString G4OpenGLWtViewer::removeTempFolder() {
01867
01868 if (fMovieTempFolderPath == "") {
01869 return "";
01870 }
01871 WDir *d = new WDir(WDir::cleanPath(fMovieTempFolderPath));
01872 if (!d->exists()) {
01873 return "";
01874 }
01875
01876 d->setFilter( WDir::Files );
01877 Wt::WStringList subDirList = d->entryList();
01878 int res = true;
01879 Wt::WString error = "";
01880 for (Wt::WStringList::ConstIterator it = subDirList.begin() ;(it != subDirList.end()) ; it++) {
01881 const Wt::WString currentFile = *it;
01882 if (!d->remove(currentFile)) {
01883 res = false;
01884 Wt::WString file = fMovieTempFolderPath+currentFile;
01885 error +="Removing file failed : "+file;
01886 } else {
01887 }
01888 }
01889 if (res) {
01890 if (d->rmdir(fMovieTempFolderPath)) {
01891 fMovieTempFolderPath = "";
01892 return "";
01893 } else {
01894 return "Dir "+fMovieTempFolderPath+" should be empty, but could not remove it";
01895 }
01896
01897 }
01898 return "Could not remove "+fMovieTempFolderPath+" because of the following errors :"+error;
01899 }
01900
01901
01902
01903 bool G4OpenGLWtViewer::hasPendingEvents () {
01904 #ifdef _A_FINIR_FIXME
01905 return ((WApplication*)G4Wt::getInstance ())->hasPendingEvents ();
01906 #endif
01907 return false;
01908 }
01909
01910 bool G4OpenGLWtViewer::generateMpegEncoderParameters () {
01911
01912
01913 FILE* fp;
01914 fp = fopen (Wt::WString(fMovieTempFolderPath+fParameterFileName).toUTF8().c_str(), "w");
01915
01916 if (fp == NULL) {
01917 setRecordingInfos("Generation of parameter file failed");
01918 return false;
01919 }
01920
01921 fprintf (fp,"# parameter file template with lots of comments to assist you\n");
01922 fprintf (fp,"#\n");
01923 fprintf (fp,"# you can use this as a template, copying it to a separate file then modifying\n");
01924 fprintf (fp,"# the copy\n");
01925 fprintf (fp,"#\n");
01926 fprintf (fp,"#\n");
01927 fprintf (fp,"# any line beginning with '#' is a comment\n");
01928 fprintf (fp,"#\n");
01929 fprintf (fp,"# no line should be longer than 255 characters\n");
01930 fprintf (fp,"#\n");
01931 fprintf (fp,"#\n");
01932 fprintf (fp,"# general format of each line is:\n");
01933 fprintf (fp,"# \n");
01934 fprintf (fp,"#\n");
01935 fprintf (fp,"# lines can generally be in any order\n");
01936 fprintf (fp,"#\n");
01937 fprintf (fp,"# an exception is the option 'INPUT' which must be followed by input\n");
01938 fprintf (fp,"# files in the order in which they must appear, followed by 'END_INPUT'\n");
01939 fprintf (fp,"#\n");
01940 fprintf (fp,"# Also, if you use the `command` method of generating input file names,\n");
01941 fprintf (fp,"# the command will only be executed in the INPUT_DIR if INPUT_DIR preceeds\n");
01942 fprintf (fp,"# the INPUT parameter.\n");
01943 fprintf (fp,"#\n");
01944 fprintf (fp,"# MUST be in UPPER CASE\n");
01945 fprintf (fp,"#\n");
01946 fprintf (fp,"\n");
01947 fprintf (fp,"# Pattern affects speed, quality and compression. See the User's Guide\n");
01948 fprintf (fp,"# for more info.\n");
01949 fprintf (fp,"\n");
01950 fprintf (fp,"PATTERN IBBPBBPBBPBBPBBP\n");
01951 fprintf (fp,"OUTPUT %s\n",getSaveFileName().toUTF8().c_str());
01952 fprintf (fp,"\n");
01953 fprintf (fp,"# mpeg_encode really only accepts 3 different file formats, but using a\n");
01954 fprintf (fp,"# conversion statement it can effectively handle ANY file format\n");
01955 fprintf (fp,"#\n");
01956 fprintf (fp,"# You must specify the type of the input files. The choices are:\n");
01957 fprintf (fp,"# YUV, PPM, JMOVIE, Y, JPEG, PNM\n");
01958 fprintf (fp,"# (must be upper case)\n");
01959 fprintf (fp,"#\n");
01960 fprintf (fp,"BASE_FILE_FORMAT PPM\n");
01961 fprintf (fp,"\n");
01962 fprintf (fp,"#\n");
01963 fprintf (fp,"# if YUV format (or using parallel version), must provide width and height\n");
01964 fprintf (fp,"# YUV_SIZE widthxheight\n");
01965 fprintf (fp,"# this option is ignored if BASE_FILE_FORMAT is not YUV and you're running\n");
01966 fprintf (fp,"# on just one machine\n");
01967 fprintf (fp,"#\n");
01968 fprintf (fp,"YUV_SIZE 352x240\n");
01969 fprintf (fp,"\n");
01970 fprintf (fp,"# If you are using YUV, there are different supported file formats.\n");
01971 fprintf (fp,"# EYUV or UCB are the same as previous versions of this encoder.\n");
01972 fprintf (fp,"# (All the Y's, then U's then V's, in 4:2:0 subsampling.)\n");
01973 fprintf (fp,"# Other formats, such as Abekas, Phillips, or a general format are\n");
01974 fprintf (fp,"# permissible, the general format is a string of Y's, U's, and V's\n");
01975 fprintf (fp,"# to specify the file order.\n");
01976 fprintf (fp,"\n");
01977 fprintf (fp,"INPUT_FORMAT UCB\n");
01978 fprintf (fp,"\n");
01979 fprintf (fp,"# the conversion statement\n");
01980 fprintf (fp,"#\n");
01981 fprintf (fp,"# Each occurrence of '*' will be replaced by the input file\n");
01982 fprintf (fp,"#\n");
01983 fprintf (fp,"# e.g., if you have a bunch of GIF files, then this might be:\n");
01984 fprintf (fp,"# INPUT_CONVERT giftoppm *\n");
01985 fprintf (fp,"#\n");
01986 fprintf (fp,"# e.g., if you have a bunch of files like a.Y a.U a.V, etc., then:\n");
01987 fprintf (fp,"# INPUT_CONVERT cat *.Y *.U *.V\n");
01988 fprintf (fp,"#\n");
01989 fprintf (fp,"# e.g., if you are grabbing from laser disc you might have something like\n");
01990 fprintf (fp,"# INPUT_CONVERT goto frame *; grabppm\n");
01991 fprintf (fp,"# 'INPUT_CONVERT *' means the files are already in the base file format\n");
01992 fprintf (fp,"#\n");
01993 fprintf (fp,"INPUT_CONVERT * \n");
01994 fprintf (fp,"\n");
01995 fprintf (fp,"# number of frames in a GOP.\n");
01996 fprintf (fp,"#\n");
01997 fprintf (fp,"# since each GOP must have at least one I-frame, the encoder will find the\n");
01998 fprintf (fp,"# the first I-frame after GOP_SIZE frames to start the next GOP\n");
01999 fprintf (fp,"#\n");
02000 fprintf (fp,"# later, will add more flexible GOP signalling\n");
02001 fprintf (fp,"#\n");
02002 fprintf (fp,"GOP_SIZE 16\n");
02003 fprintf (fp,"\n");
02004 fprintf (fp,"# number of slices in a frame\n");
02005 fprintf (fp,"#\n");
02006 fprintf (fp,"# 1 is a good number. another possibility is the number of macroblock rows\n");
02007 fprintf (fp,"# (which is the height divided by 16)\n");
02008 fprintf (fp,"#\n");
02009 fprintf (fp,"SLICES_PER_FRAME 1\n");
02010 fprintf (fp,"\n");
02011 fprintf (fp,"# directory to get all input files from (makes this file easier to read)\n");
02012 fprintf (fp,"INPUT_DIR %s\n",fMovieTempFolderPath.toUTF8().c_str());
02013 fprintf (fp,"\n");
02014 fprintf (fp,"# There are a bunch of ways to specify the input files.\n");
02015 fprintf (fp,"# from a simple one-per-line listing, to the following \n");
02016 fprintf (fp,"# way of numbering them. See the manual for more information.\n");
02017 fprintf (fp,"INPUT\n");
02018 fprintf (fp,"# '*' is replaced by the numbers 01, 02, 03, 04\n");
02019 fprintf (fp,"# if I instead do [01-11], it would be 01, 02, ..., 09, 10, 11\n");
02020 fprintf (fp,"# if I instead do [1-11], it would be 1, 2, 3, ..., 9, 10, 11\n");
02021 fprintf (fp,"# if I instead do [1-11+3], it would be 1, 4, 7, 10\n");
02022 fprintf (fp,"# the program assumes none of your input files has a name ending in ']'\n");
02023 fprintf (fp,"# if you do, too bad!!!\n");
02024 fprintf (fp,"#\n");
02025 fprintf (fp,"#\n");
02026 fprintf (fp,"Test*.ppm [0-%d]\n",fRecordFrameNumber-1);
02027 fprintf (fp,"# can have more files here if you want...there is no limit on the number\n");
02028 fprintf (fp,"# of files\n");
02029 fprintf (fp,"END_INPUT\n");
02030 fprintf (fp,"\n");
02031 fprintf (fp,"\n");
02032 fprintf (fp,"\n");
02033 fprintf (fp,"# Many of the remaining options have to do with the motion search and qscale\n");
02034 fprintf (fp,"\n");
02035 fprintf (fp,"# FULL or HALF -- must be upper case\n");
02036 fprintf (fp,"# Should be FULL for computer generated images\n");
02037 fprintf (fp,"PIXEL FULL\n");
02038 fprintf (fp,"\n");
02039 fprintf (fp,"# means +/- this many pixels for both P and B frame searches\n");
02040 fprintf (fp,"# specify two numbers if you wish to serc different ranges in the two.\n");
02041 fprintf (fp,"RANGE 10\n");
02042 fprintf (fp,"\n");
02043 fprintf (fp,"# The two search algorithm parameters below mostly affect speed,\n");
02044 fprintf (fp,"# with some affect on compression and almost none on quality.\n");
02045 fprintf (fp,"\n");
02046 fprintf (fp,"# this must be one of {EXHAUSTIVE, SUBSAMPLE, LOGARITHMIC}\n");
02047 fprintf (fp,"PSEARCH_ALG LOGARITHMIC\n");
02048 fprintf (fp,"\n");
02049 fprintf (fp,"# this must be one of {SIMPLE, CROSS2, EXHAUSTIVE}\n");
02050 fprintf (fp,"#\n");
02051 fprintf (fp,"# note that EXHAUSTIVE is really, really, really slow\n");
02052 fprintf (fp,"#\n");
02053 fprintf (fp,"BSEARCH_ALG SIMPLE\n");
02054 fprintf (fp,"\n");
02055 fprintf (fp,"#\n");
02056 fprintf (fp,"# these specify the q-scale for I, P, and B frames\n");
02057 fprintf (fp,"# (values must be between 1 and 31)\n");
02058 fprintf (fp,"# These are the Wscale values for the entire frame in variable bit-rate\n");
02059 fprintf (fp,"# mode, and starting points (but not important) for constant bit rate\n");
02060 fprintf (fp,"#\n");
02061 fprintf (fp,"\n");
02062 fprintf (fp,"# Wscale (Wuantization scale) affects quality and compression,\n");
02063 fprintf (fp,"# but has very little effect on speed.\n");
02064 fprintf (fp,"\n");
02065 fprintf (fp,"IWSCALE 4\n");
02066 fprintf (fp,"PWSCALE 5\n");
02067 fprintf (fp,"BWSCALE 12\n");
02068 fprintf (fp,"\n");
02069 fprintf (fp,"# this must be ORIGINAL or DECODED\n");
02070 fprintf (fp,"REFERENCE_FRAME ORIGINAL\n");
02071 fprintf (fp,"\n");
02072 fprintf (fp,"# for parallel parameters see parallel.param in the exmaples subdirectory\n");
02073 fprintf (fp,"\n");
02074 fprintf (fp,"# if you want constant bit-rate mode, specify it as follows (number is bits/sec):\n");
02075 fprintf (fp,"#BIT_RATE 1000000\n");
02076 fprintf (fp,"\n");
02077 fprintf (fp,"# To specify the buffer size (327680 is default, measused in bits, for 16bit words)\n");
02078 fprintf (fp,"BUFFER_SIZE 327680\n");
02079 fprintf (fp,"\n");
02080 fprintf (fp,"# The frame rate is the number of frames/second (legal values:\n");
02081 fprintf (fp,"# 23.976, 24, 25, 29.97, 30, 50 ,59.94, 60\n");
02082 fprintf (fp,"FRAME_RATE 30\n");
02083 fprintf (fp,"\n");
02084 fprintf (fp,"# There are many more options, see the users manual for examples....\n");
02085 fprintf (fp,"# ASPECT_RATIO, USER_DATA, GAMMA, IWTABLE, etc.\n");
02086 fprintf (fp,"\n");
02087 fprintf (fp,"\n");
02088 fclose (fp);
02089
02090 setRecordingInfos("Parameter file "+fParameterFileName+" generated in "+fMovieTempFolderPath);
02091 setRecordingStatus(READY_TO_ENCODE);
02092 return true;
02093 }
02094
02095 void G4OpenGLWtViewer::encodeVideo()
02096 {
02097 if ((getEncoderPath() != "") && (getSaveFileName() != "")) {
02098 setRecordingStatus(ENCODING);
02099
02100 fProcess = new WProcess();
02101 WObject ::connect(fProcess,SIGNAL(finished ( int)),
02102 this,SLOT(processEncodeFinished()));
02103 WObject ::connect(fProcess,SIGNAL(readyReadStandardOutput ()),
02104 this,SLOT(processEncodeStdout()));
02105 fProcess->setReadChannelMode(WProcess::MergedChannels);
02106 fProcess->start (fEncoderPath, Wt::WStringList(fMovieTempFolderPath+fParameterFileName));
02107 }
02108 }
02109
02110
02111
02112 void G4OpenGLWtViewer::processEncodeStdout()
02113 {
02114 Wt::WString tmp = fProcess->readStdout ().data();
02115 int start = tmp.findRev("ESTIMATED TIME");
02116 tmp = tmp.mid(start,tmp.find("\n",start)-start);
02117 setRecordingInfos(tmp);
02118 }
02119
02120
02121 void G4OpenGLWtViewer::processEncodeFinished()
02122 {
02123
02124 Wt::WString txt = "";
02125 txt = getProcessErrorMsg();
02126 if (txt == "") {
02127 setRecordingStatus(SUCCESS);
02128 } else {
02129 setRecordingStatus(FAILED);
02130 }
02131
02132 }
02133
02134
02135 void G4OpenGLWtViewer::processLookForFinished()
02136 {
02137
02138 Wt::WString txt = getProcessErrorMsg();
02139 if (txt != "") {
02140 fEncoderPath = "";
02141 } else {
02142 fEncoderPath = Wt::WString(fProcess->readAllStandardOutput ().data()).trimmed();
02143
02144 if (fEncoderPath.contains(" ")) {
02145 fEncoderPath = "";
02146 } else if (!fEncoderPath.contains("mpeg_encode")) {
02147 fEncoderPath = "";
02148 }
02149 setEncoderPath(fEncoderPath);
02150 }
02151
02152 setTempFolderPath(WDir::temp ().absolutePath ());
02153 }
02154
02155
02156 Wt::WString G4OpenGLWtViewer::getProcessErrorMsg()
02157 {
02158 Wt::WString txt = "";
02159 if (fProcess->exitCode() != 0) {
02160 switch (fProcess->error()) {
02161 case WProcess::FailedToStart:
02162 txt = "The process failed to start. Either the invoked program is missing, or you may have insufficient permissions to invoke the program.\n";
02163 break;
02164 case WProcess::Crashed:
02165 txt = "The process crashed some time after starting successfully.\n";
02166 break;
02167 case WProcess::Timedout:
02168 txt = "The last waitFor...() function timed out. The state of WProcess is unchanged, and you can try calling waitFor...() again.\n";
02169 break;
02170 case WProcess::WriteError:
02171 txt = "An error occurred when attempting to write to the process. For example, the process may not be running, or it may have closed its input channel.\n";
02172 break;
02173 case WProcess::ReadError:
02174 txt = "An error occurred when attempting to read from the process. For example, the process may not be running.\n";
02175 break;
02176 case WProcess::UnknownError:
02177 txt = "An unknown error occurred. This is the default return value of error().\n";
02178 break;
02179 }
02180 }
02181 return txt;
02182 }
02183 #endif
02184
02185
02186
02187
02188
02189
02190
02191
02192
02193
02194
02195
02196
02197
02198
02199
02200
02201
02202
02203
02204
02205
02206
02207
02208
02209
02210 #endif