Geant4 Cross Reference |
1 // 1 2 // ******************************************* 3 // * License and Disclaimer 4 // * 5 // * The Geant4 software is copyright of th 6 // * the Geant4 Collaboration. It is provided 7 // * conditions of the Geant4 Software License 8 // * LICENSE and available at http://cern.ch/ 9 // * include a list of copyright holders. 10 // * 11 // * Neither the authors of this software syst 12 // * institutes,nor the agencies providing fin 13 // * work make any representation or warran 14 // * regarding this software system or assum 15 // * use. Please see the license in the file 16 // * for the full disclaimer and the limitatio 17 // * 18 // * This code implementation is the result 19 // * technical work of the GEANT4 collaboratio 20 // * By using, copying, modifying or distri 21 // * any work based on the software) you ag 22 // * use in resulting scientific publicati 23 // * acceptance of all terms of the Geant4 Sof 24 // ******************************************* 25 // 26 // John Allison 17th June 2019 27 28 #include "G4Qt3DViewer.hh" 29 30 #include "G4Qt3DSceneHandler.hh" 31 #include "G4Qt3DUtils.hh" 32 33 #include "G4Scene.hh" 34 #include "G4UImanager.hh" 35 #include "G4UIQt.hh" 36 #include "G4SystemOfUnits.hh" 37 38 #define G4warn G4cout 39 40 G4Qt3DViewer::G4Qt3DViewer 41 (G4Qt3DSceneHandler& sceneHandler, const G4Str 42 : G4VViewer(sceneHandler, sceneHandler.Increme 43 , fQt3DSceneHandler(sceneHandler) 44 , fKeyPressed(false) 45 , fMousePressed(false) 46 , fMousePressedX(0.) 47 , fMousePressedY(0.) 48 {} 49 50 void G4Qt3DViewer::Initialise() 51 { 52 setObjectName(fName.c_str()); 53 54 fVP.SetAutoRefresh(true); 55 fDefaultVP.SetAutoRefresh(true); 56 57 auto UI = G4UImanager::GetUIpointer(); 58 auto uiQt = dynamic_cast<G4UIQt*>(UI->GetG4U 59 if (!uiQt) { 60 fViewId = -1; // This flags an error. 61 G4warn << "G4Qt3DViewer::G4Qt3DViewer requ 62 << G4endl; 63 return; 64 } 65 fUIWidget = QWidget::createWindowContainer(t 66 uiQt->AddTabWidget(fUIWidget,QString(fName)) 67 68 setRootEntity(fQt3DSceneHandler.fpQt3DScene) 69 } 70 71 G4Qt3DViewer::~G4Qt3DViewer() 72 { 73 setRootEntity(nullptr); 74 } 75 76 void G4Qt3DViewer::resizeEvent(QResizeEvent*) 77 SetView(); 78 } 79 80 void G4Qt3DViewer::SetView() 81 { 82 // Background colour 83 defaultFrameGraph()->setClearColor(G4Qt3DUti 84 85 // Get radius of scene, etc. 86 // Note that this procedure properly takes i 87 const G4Point3D targetPoint 88 = fSceneHandler.GetScene()->GetStandardTar 89 + fVP.GetCurrentTargetPoint (); 90 G4double radius = fSceneHandler.GetScene()-> 91 if(radius<=0.) radius = 1.; 92 const G4double cameraDistance = fVP.GetCamer 93 const G4Point3D cameraPosition = 94 targetPoint + cameraDistance * fVP.GetView 95 const GLdouble pnear = fVP.GetNearDistance 96 const GLdouble pfar = fVP.GetFarDistance 97 const GLdouble right = fVP.GetFrontHalfHeig 98 const GLdouble left = -right; 99 const GLdouble top = fVP.GetFrontHalfHeig 100 const GLdouble bottom = -top; 101 102 camera()->setObjectName((fName + " camera"). 103 camera()->setViewCenter(G4Qt3DUtils::Convert 104 camera()->setPosition(G4Qt3DUtils::ConvertTo 105 camera()->setUpVector(G4Qt3DUtils::ConvertTo 106 107 // auto lightEntity = new Qt3DCore::QEntity(f 108 // auto directionalLight = new Qt3DRender::QD 109 //// directionalLight->setColor("white"); 110 //// directionalLight->setIntensity(1.); 111 // directionalLight->setWorldDirection(G4Qt3D 112 // lightEntity->addComponent(directionalLight 113 114 const auto& size = fUIWidget->size(); 115 G4double w = size.width(); 116 G4double h = size.height(); 117 #ifdef G4QT3DDEBUG 118 // Curiously w,h are wrong first time - 640, 119 G4cout << "W,H: " << w << ',' << h << G4endl 120 #endif 121 const G4double aspectRatio = w/h; 122 if (fVP.GetFieldHalfAngle() == 0.) { 123 camera()->lens()->setOrthographicProjectio 124 (left*aspectRatio,right*aspectRatio,bottom 125 } else { 126 camera()->lens()->setPerspectiveProjection 127 (2.*fVP.GetFieldHalfAngle()/deg,aspectRati 128 } 129 } 130 131 void G4Qt3DViewer::ClearView() 132 {} 133 134 void G4Qt3DViewer::DrawView() 135 { 136 // First, a view should decide when to re-vi 137 // Sometimes it might not be necessary, e.g. 138 // in a graphical database (e.g., OpenGL's d 139 // the viewing angle has changed. But graph 140 // graphical database will always need to vi 141 142 // The fNeedKernelVisit flag might have been 143 // /vis/viewer/rebuild, but if not, make dec 144 // if necessary... 145 if (!fNeedKernelVisit) KernelVisitDecision() 146 G4bool kernelVisitWasNeeded = fNeedKernelVis 147 fLastVP = fVP; 148 149 ProcessView (); // Clears store and process 150 151 if (kernelVisitWasNeeded) { 152 // We might need to do something if the ke 153 } else { 154 } 155 156 // ...before finally... 157 FinishView (); // Flush streams and/or 158 } 159 160 void G4Qt3DViewer::ShowView() 161 { 162 #if QT_VERSION < 0x060000 163 // show() may only be called from master thr 164 if (G4Threading::IsMasterThread()) { 165 show(); 166 } 167 // The way Qt seems to work, we don't seem t 168 // we'll leave it in - it seems not to have 169 #endif 170 } 171 172 void G4Qt3DViewer::FinishView() 173 { 174 #if QT_VERSION < 0x060000 175 if (G4Threading::IsMasterThread()) { 176 show(); 177 } 178 #endif 179 } 180 181 // Note: the order of calling of MovingToVisSu 182 // is undefined. The order of calling is 183 // DoneWithMasterThread 184 // MovingToVisSubThread ) or ( SwitchToVisSu 185 // SwitchToVisSubThread ) ( MovingToVisSu 186 // DoneWithVisSubThread 187 // MovingToMasterThread 188 // SwitchToMasterThread 189 // So regarding the move/switch to the vis sub 190 // If the viewer wishes to accept drawing from 191 // But at this point we are still on the maste 192 // not known. So it has to wait - a conditiona 193 // and the provision of a pointer to the QThre 194 // sub-thread has to wait until the QObjects h 195 196 namespace { 197 QThread* masterQThread = nullptr; 198 QThread* visSubThreadQThread = nullptr; 199 200 G4Mutex visSubThreadMutex = G4MUTEX_INITIALI 201 G4Condition waitForVisSubThreadInitialized = 202 G4bool visSubThreadEstablished = false; 203 G4bool qObjectsSwitched = false; 204 } 205 206 void G4Qt3DViewer::MovingToVisSubThread() 207 // Still on master thread but vis thread has b 208 { 209 // Make note of master QThread 210 masterQThread = QThread::currentThread(); 211 212 // Wait until SwitchToVisSubThread has found 213 { 214 G4AutoLock lock(&visSubThreadMutex); 215 G4CONDITIONWAITLAMBDA(&waitForVisSubThreadIn 216 } 217 218 // Move relevant stuff to vis sub-thread QTh 219 auto p1 = fQt3DSceneHandler.fpQt3DScene->par 220 if(p1) { 221 auto p2 = p1->parent(); 222 if(p2) { 223 p2->moveToThread(visSubThreadQThread); 224 } else { 225 p1->moveToThread(visSubThreadQThread); 226 } 227 } 228 229 // Inform sub-thread 230 G4AutoLock lock(&visSubThreadMutex); 231 qObjectsSwitched = true; 232 lock.unlock(); 233 G4CONDITIONBROADCAST(&waitForVisSubThreadIni 234 } 235 236 void G4Qt3DViewer::SwitchToVisSubThread() 237 // On vis sub-thread before any drawing 238 { 239 // Make note of vis-subthread QThread for Mo 240 visSubThreadQThread = QThread::currentThread 241 242 // Let MovingToVisSubThread know we have the 243 { 244 G4AutoLock lock(&visSubThreadMutex); 245 visSubThreadEstablished = true; 246 G4CONDITIONBROADCAST(&waitForVisSubThreadIni 247 } 248 249 // Wait until MovingToVisSubThread has moved 250 { 251 G4AutoLock lock(&visSubThreadMutex); 252 G4CONDITIONWAITLAMBDA(&waitForVisSubThreadIn 253 } 254 } 255 256 void G4Qt3DViewer::MovingToMasterThread() 257 // On vis sub-thread just before exit 258 { 259 // Move relevant stuff to master QThread. 260 auto p1 = fQt3DSceneHandler.fpQt3DScene->par 261 if(p1) { 262 auto p2 = p1->parent(); 263 if(p2) { 264 p2->moveToThread(masterQThread); 265 } else { 266 p1->moveToThread(masterQThread); 267 } 268 } 269 270 // Reset 271 visSubThreadQThread = nullptr; 272 qObjectsSwitched = false; 273 } 274 275 void G4Qt3DViewer::SwitchToMasterThread() 276 // On master thread after vis sub-thread has t 277 { 278 visSubThreadEstablished = false; 279 } 280 281 void G4Qt3DViewer::KernelVisitDecision () { 282 283 // If there's a significant difference with 284 // of either the scene handler or this viewe 285 286 if (CompareForKernelVisit(fLastVP)) { 287 NeedKernelVisit (); // Sets fNeedKernelVi 288 } 289 } 290 291 G4bool G4Qt3DViewer::CompareForKernelVisit(G4V 292 { 293 // Typical comparison. Taken from OpenInven 294 if ( 295 (vp.GetDrawingStyle () != fVP.GetDrawi 296 (vp.GetNumberOfCloudPoints() != fVP.GetN 297 (vp.IsAuxEdgeVisible () != fVP.IsAuxEdg 298 (vp.IsCulling () != fVP.IsCullin 299 (vp.IsCullingInvisible () != fVP.IsCullin 300 (vp.IsDensityCulling () != fVP.IsDensit 301 (vp.IsCullingCovered () != fVP.IsCullin 302 (vp.GetCBDAlgorithmNumber() != 303 fVP.GetCBDAlgorithmNumber()) 304 (vp.IsSection () != fVP.IsSectio 305 (vp.IsCutaway () != fVP.IsCutawa 306 // This assumes use of generic clipping ( 307 // DCUT, cutaway). If a decision is made 308 // this will need changing. See G4OpenGL 309 // G4OpenGLStoredViewer.cc::CompareForKer 310 // G4OpenGLStoredSceneHander::CreateSecti 311 (vp.IsExplode () != fVP.IsExplod 312 (vp.GetNoOfSides () != fVP.GetNoOfS 313 (vp.GetGlobalMarkerScale() != fVP.GetG 314 (vp.GetGlobalLineWidthScale() != fVP.GetG 315 (vp.IsMarkerNotHidden () != fVP.IsMarker 316 (vp.GetDefaultVisAttributes()->GetColour( 317 fVP.GetDefaultVisAttributes()->GetColour 318 (vp.GetDefaultTextVisAttributes()->GetCol 319 fVP.GetDefaultTextVisAttributes()->GetCo 320 (vp.GetBackgroundColour ()!= fVP.GetBackg 321 (vp.IsPicking () != fVP.IsPickin 322 // Scaling for Open Inventor is done by t 323 // needs a kernel visit. (In this respec 324 // OpenGL drivers, where it's done in Set 325 (vp.GetScaleFactor () != fVP.GetScale 326 (vp.GetVisAttributesModifiers() != 327 fVP.GetVisAttributesModifiers()) 328 (vp.IsSpecialMeshRendering() != 329 fVP.IsSpecialMeshRendering()) 330 (vp.GetSpecialMeshRenderingOption() != 331 fVP.GetSpecialMeshRenderingOption()) 332 ) 333 return true; 334 335 if (vp.IsDensityCulling () && 336 (vp.GetVisibleDensity () != fVP.GetVisib 337 return true; 338 339 if (vp.GetCBDAlgorithmNumber() > 0) { 340 if (vp.GetCBDParameters().size() != fVP.Ge 341 else if (vp.GetCBDParameters() != fVP.GetC 342 } 343 344 if (vp.IsSection () && 345 (vp.GetSectionPlane () != fVP.GetSection 346 return true; 347 348 if (vp.IsCutaway ()) { 349 if (vp.GetCutawayMode() != fVP.GetCutawayM 350 if (vp.GetCutawayPlanes ().size () != 351 fVP.GetCutawayPlanes ().size ()) retur 352 for (size_t i = 0; i < vp.GetCutawayPlanes 353 if (vp.GetCutawayPlanes()[i] != fVP.GetCut 354 return true; 355 } 356 357 if (vp.IsExplode () && 358 (vp.GetExplodeFactor () != fVP.GetExplod 359 return true; 360 361 if (vp.IsSpecialMeshRendering() && 362 (vp.GetSpecialMeshVolumes() != fVP.GetSp 363 return true; 364 365 return false; 366 } 367 368 void G4Qt3DViewer::keyPressEvent(QKeyEvent* ev 369 { 370 fKeyPressed = true; 371 fKey = ev->key(); 372 } 373 374 void G4Qt3DViewer::keyReleaseEvent(QKeyEvent* 375 { 376 fKeyPressed = false; 377 } 378 379 void G4Qt3DViewer::mouseDoubleClickEvent(QMous 380 381 void G4Qt3DViewer::mouseMoveEvent(QMouseEvent* 382 { 383 // I think we only want these if a mouse but 384 // But they come even when not pressed (on m 385 // Documentation says: 386 /* Mouse move events will occur only when a 387 unless mouse tracking has been enabled with 388 // But this is a window not a widget. 389 // As a workaround we maintain a flag change 390 #if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) 391 G4double x = ev->x(); 392 G4double y = ev->y(); 393 #else 394 G4double x = ev->position().x(); 395 G4double y = ev->position().y(); 396 #endif 397 G4double dx = x-fMousePressedX; 398 G4double dy = y-fMousePressedY; 399 fMousePressedX = x; 400 fMousePressedY = y; 401 402 if (fMousePressed) { 403 404 if (fKeyPressed && fKey == Qt::Key_Shift) 405 406 const G4double sceneRadius = fQt3DSceneH 407 const G4double scale = 300; // Roughly 408 const G4double dxScene = dx*sceneRadius/ 409 const G4double dyScene = dy*sceneRadius/ 410 fVP.IncrementPan(-dxScene,dyScene); 411 412 } else { // Rotation 413 414 // Simple ad-hoc algorithms 415 const G4Vector3D& x_prime = fVP.GetViewp 416 const G4Vector3D& y_prime = x_prime.cros 417 const G4double scale = 200; // Roughly 418 G4Vector3D newViewpointDirection = fVP.G 419 newViewpointDirection += dx*x_prime/scal 420 newViewpointDirection += dy*y_prime/scal 421 fVP.SetViewpointDirection(newViewpointDi 422 423 if (fVP.GetRotationStyle() == G4ViewPara 424 G4Vector3D newUpVector = fVP.GetUpVect 425 newUpVector += dx*x_prime/scale; 426 newUpVector += dy*y_prime/scale; 427 fVP.SetUpVector(newUpVector.unit()); 428 } 429 } 430 } 431 432 SetView(); 433 DrawView(); 434 } 435 436 void G4Qt3DViewer::mousePressEvent(QMouseEvent 437 { 438 fMousePressed = true; 439 #if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0)) 440 fMousePressedX = ev->x(); 441 fMousePressedY = ev->y(); 442 #else 443 fMousePressedX = ev->position().x(); 444 fMousePressedY = ev->position().y(); 445 #endif 446 } 447 448 void G4Qt3DViewer::mouseReleaseEvent(QMouseEve 449 { 450 fMousePressed = false; 451 } 452 453 void G4Qt3DViewer::wheelEvent(QWheelEvent* ev) 454 { 455 // Take note of up-down motion only 456 const G4double angleY = ev->angleDelta().y() 457 458 if (fVP.GetFieldHalfAngle() == 0.) { // Ort 459 const G4double scale = 500; // Empiricall 460 fVP.MultiplyZoomFactor(1.+angleY/scale); 461 } else { // Per 462 const G4double delta = fSceneHandler.GetEx 463 fVP.SetDolly(fVP.GetDolly()+angleY*delta); 464 } 465 466 SetView(); 467 DrawView(); 468 } 469