Geant4 Cross Reference

Cross-Referencing   Geant4
Geant4/visualization/management/src/G4VSceneHandler.cc

Version: [ ReleaseNotes ] [ 1.0 ] [ 1.1 ] [ 2.0 ] [ 3.0 ] [ 3.1 ] [ 3.2 ] [ 4.0 ] [ 4.0.p1 ] [ 4.0.p2 ] [ 4.1 ] [ 4.1.p1 ] [ 5.0 ] [ 5.0.p1 ] [ 5.1 ] [ 5.1.p1 ] [ 5.2 ] [ 5.2.p1 ] [ 5.2.p2 ] [ 6.0 ] [ 6.0.p1 ] [ 6.1 ] [ 6.2 ] [ 6.2.p1 ] [ 6.2.p2 ] [ 7.0 ] [ 7.0.p1 ] [ 7.1 ] [ 7.1.p1 ] [ 8.0 ] [ 8.0.p1 ] [ 8.1 ] [ 8.1.p1 ] [ 8.1.p2 ] [ 8.2 ] [ 8.2.p1 ] [ 8.3 ] [ 8.3.p1 ] [ 8.3.p2 ] [ 9.0 ] [ 9.0.p1 ] [ 9.0.p2 ] [ 9.1 ] [ 9.1.p1 ] [ 9.1.p2 ] [ 9.1.p3 ] [ 9.2 ] [ 9.2.p1 ] [ 9.2.p2 ] [ 9.2.p3 ] [ 9.2.p4 ] [ 9.3 ] [ 9.3.p1 ] [ 9.3.p2 ] [ 9.4 ] [ 9.4.p1 ] [ 9.4.p2 ] [ 9.4.p3 ] [ 9.4.p4 ] [ 9.5 ] [ 9.5.p1 ] [ 9.5.p2 ] [ 9.6 ] [ 9.6.p1 ] [ 9.6.p2 ] [ 9.6.p3 ] [ 9.6.p4 ] [ 10.0 ] [ 10.0.p1 ] [ 10.0.p2 ] [ 10.0.p3 ] [ 10.0.p4 ] [ 10.1 ] [ 10.1.p1 ] [ 10.1.p2 ] [ 10.1.p3 ] [ 10.2 ] [ 10.2.p1 ] [ 10.2.p2 ] [ 10.2.p3 ] [ 10.3 ] [ 10.3.p1 ] [ 10.3.p2 ] [ 10.3.p3 ] [ 10.4 ] [ 10.4.p1 ] [ 10.4.p2 ] [ 10.4.p3 ] [ 10.5 ] [ 10.5.p1 ] [ 10.6 ] [ 10.6.p1 ] [ 10.6.p2 ] [ 10.6.p3 ] [ 10.7 ] [ 10.7.p1 ] [ 10.7.p2 ] [ 10.7.p3 ] [ 10.7.p4 ] [ 11.0 ] [ 11.0.p1 ] [ 11.0.p2 ] [ 11.0.p3, ] [ 11.0.p4 ] [ 11.1 ] [ 11.1.1 ] [ 11.1.2 ] [ 11.1.3 ] [ 11.2 ] [ 11.2.1 ] [ 11.2.2 ] [ 11.3.0 ]

  1 //
  2 // ********************************************************************
  3 // * License and Disclaimer                                           *
  4 // *                                                                  *
  5 // * The  Geant4 software  is  copyright of the Copyright Holders  of *
  6 // * the Geant4 Collaboration.  It is provided  under  the terms  and *
  7 // * conditions of the Geant4 Software License,  included in the file *
  8 // * LICENSE and available at  http://cern.ch/geant4/license .  These *
  9 // * include a list of copyright holders.                             *
 10 // *                                                                  *
 11 // * Neither the authors of this software system, nor their employing *
 12 // * institutes,nor the agencies providing financial support for this *
 13 // * work  make  any representation or  warranty, express or implied, *
 14 // * regarding  this  software system or assume any liability for its *
 15 // * use.  Please see the license in the file  LICENSE  and URL above *
 16 // * for the full disclaimer and the limitation of liability.         *
 17 // *                                                                  *
 18 // * This  code  implementation is the result of  the  scientific and *
 19 // * technical work of the GEANT4 collaboration.                      *
 20 // * By using,  copying,  modifying or  distributing the software (or *
 21 // * any work based  on the software)  you  agree  to acknowledge its *
 22 // * use  in  resulting  scientific  publications,  and indicate your *
 23 // * acceptance of all terms of the Geant4 Software license.          *
 24 // ********************************************************************
 25 //
 26 //
 27 //
 28 // 
 29 // John Allison  19th July 1996
 30 // Abstract interface class for graphics scenes.
 31 
 32 #include "G4VSceneHandler.hh"
 33 
 34 #include "G4ios.hh"
 35 #include <sstream>
 36 
 37 #include "G4VisManager.hh"
 38 #include "G4VGraphicsSystem.hh"
 39 #include "G4VViewer.hh"
 40 #include "G4VSolid.hh"
 41 #include "G4RotationMatrix.hh"
 42 #include "G4ThreeVector.hh"
 43 #include "G4VPhysicalVolume.hh"
 44 #include "G4Material.hh"
 45 #include "G4Polyline.hh"
 46 #include "G4Text.hh"
 47 #include "G4Circle.hh"
 48 #include "G4Square.hh"
 49 #include "G4Polymarker.hh"
 50 #include "G4Polyhedron.hh"
 51 #include "G4Visible.hh"
 52 #include "G4VisAttributes.hh"
 53 #include "G4VModel.hh"
 54 #include "G4TrajectoriesModel.hh"
 55 #include "G4Box.hh"
 56 #include "G4Cons.hh"
 57 #include "G4Orb.hh"
 58 #include "G4Para.hh"
 59 #include "G4Sphere.hh"
 60 #include "G4Torus.hh"
 61 #include "G4Trap.hh"
 62 #include "G4Trd.hh"
 63 #include "G4Tubs.hh"
 64 #include "G4Ellipsoid.hh"
 65 #include "G4Polycone.hh"
 66 #include "G4Polyhedra.hh"
 67 #include "G4Tet.hh"
 68 #include "G4DisplacedSolid.hh"
 69 #include "G4UnionSolid.hh"
 70 #include "G4IntersectionSolid.hh"
 71 #include "G4SubtractionSolid.hh"
 72 #include "G4LogicalVolume.hh"
 73 #include "G4PhysicalVolumeModel.hh"
 74 #include "G4ModelingParameters.hh"
 75 #include "G4VTrajectory.hh"
 76 #include "G4VTrajectoryPoint.hh"
 77 #include "G4HitsModel.hh"
 78 #include "G4VHit.hh"
 79 #include "G4VDigi.hh"
 80 #include "G4ScoringManager.hh"
 81 #include "G4VScoringMesh.hh"
 82 #include "G4Mesh.hh"
 83 #include "G4DefaultLinearColorMap.hh"
 84 #include "G4QuickRand.hh"
 85 #include "G4StateManager.hh"
 86 #include "G4RunManager.hh"
 87 #include "G4RunManagerFactory.hh"
 88 #include "G4Run.hh"
 89 #include "G4Transform3D.hh"
 90 #include "G4AttHolder.hh"
 91 #include "G4AttDef.hh"
 92 #include "G4SceneTreeItem.hh"
 93 #include "G4VVisCommand.hh"
 94 #include "G4PhysicalConstants.hh"
 95 #include "G4SystemOfUnits.hh"
 96 
 97 #define G4warn G4cout
 98 
 99 G4VSceneHandler::G4VSceneHandler (G4VGraphicsSystem& system, G4int id, const G4String& name):
100   fSystem                (system),
101   fSceneHandlerId        (id),
102   fViewCount             (0),
103   fpViewer               (0),
104   fpScene                (0),
105   fMarkForClearingTransientStore (true),  // Ready for first
106             // ClearTransientStoreIfMarked(),
107             // e.g., at end of run (see
108             // G4VisManager.cc).
109   fReadyForTransients    (true),  // Only false while processing scene.
110   fProcessingSolid       (false),
111   fProcessing2D          (false),
112   fpModel                (0),
113   fNestingDepth          (0),
114   fpVisAttribs           (0)
115 {
116   G4VisManager* pVMan = G4VisManager::GetInstance ();
117   fpScene = pVMan -> GetCurrentScene ();
118   if (name == "") {
119     std::ostringstream ost;
120     ost << fSystem.GetName () << '-' << fSceneHandlerId;
121     fName = ost.str();
122   }
123   else {
124     fName = name;
125   }
126   fTransientsDrawnThisEvent = pVMan->GetTransientsDrawnThisEvent();
127   fTransientsDrawnThisRun = pVMan->GetTransientsDrawnThisRun();
128 }
129 
130 G4VSceneHandler::~G4VSceneHandler () {
131   G4VViewer* last;
132   while( ! fViewerList.empty() ) {
133     last = fViewerList.back();
134     fViewerList.pop_back();
135     delete last;
136   }
137 }
138 
139 const G4VisExtent& G4VSceneHandler::GetExtent() const
140 {
141   if (fpScene) {
142     return fpScene->GetExtent();
143   } else {
144     static const G4VisExtent defaultExtent = G4VisExtent();
145     return defaultExtent;
146   }
147 }
148 
149 void G4VSceneHandler::PreAddSolid (const G4Transform3D& objectTransformation,
150            const G4VisAttributes& visAttribs) {
151   fObjectTransformation = objectTransformation;
152   fpVisAttribs = &visAttribs;
153   fProcessingSolid = true;
154 }
155 
156 void G4VSceneHandler::PostAddSolid () {
157   fpVisAttribs = 0;
158   fProcessingSolid = false;
159   if (fReadyForTransients) {
160     fTransientsDrawnThisEvent = true;
161     fTransientsDrawnThisRun = true;
162   }
163 }
164 
165 void G4VSceneHandler::BeginPrimitives
166 (const G4Transform3D& objectTransformation) {
167   //static G4int count = 0;
168   //G4cout << "G4VSceneHandler::BeginPrimitives: " << count++ << G4endl;
169   fNestingDepth++;
170   if (fNestingDepth > 1)
171     G4Exception
172       ("G4VSceneHandler::BeginPrimitives",
173        "visman0101", FatalException,
174        "Nesting detected. It is illegal to nest Begin/EndPrimitives.");
175   fObjectTransformation = objectTransformation;
176 }
177 
178 void G4VSceneHandler::EndPrimitives () {
179   if (fNestingDepth <= 0)
180     G4Exception("G4VSceneHandler::EndPrimitives",
181     "visman0102", FatalException, "Nesting error.");
182   fNestingDepth--;
183   if (fReadyForTransients) {
184     fTransientsDrawnThisEvent = true;
185     fTransientsDrawnThisRun = true;
186   }
187 }
188 
189 void G4VSceneHandler::BeginPrimitives2D
190 (const G4Transform3D& objectTransformation) {
191   fNestingDepth++;
192   if (fNestingDepth > 1)
193     G4Exception
194       ("G4VSceneHandler::BeginPrimitives2D",
195        "visman0103", FatalException,
196        "Nesting detected. It is illegal to nest Begin/EndPrimitives.");
197   fObjectTransformation = objectTransformation;
198   fProcessing2D = true;
199 }
200 
201 void G4VSceneHandler::EndPrimitives2D () {
202   if (fNestingDepth <= 0)
203     G4Exception("G4VSceneHandler::EndPrimitives2D",
204     "visman0104", FatalException, "Nesting error.");
205   fNestingDepth--;
206   if (fReadyForTransients) {
207     fTransientsDrawnThisEvent = true;
208     fTransientsDrawnThisRun = true;
209   }
210   fProcessing2D = false;
211 }
212 
213 void G4VSceneHandler::BeginModeling () {
214 }
215 
216 void G4VSceneHandler::EndModeling ()
217 {
218   fpModel = 0;
219 }
220 
221 void G4VSceneHandler::ClearStore () {}
222 
223 void G4VSceneHandler::ClearTransientStore () {}
224 
225 template <class T> void G4VSceneHandler::AddSolidT
226 (const T& solid)
227 {
228   // Get and check applicable vis attributes.
229   fpVisAttribs = fpViewer->GetApplicableVisAttributes(fpVisAttribs);
230   RequestPrimitives (solid);
231 }
232 
233 template <class T> void G4VSceneHandler::AddSolidWithAuxiliaryEdges
234 (const T& solid)
235 {
236   // Get and check applicable vis attributes.
237   fpVisAttribs = fpViewer->GetApplicableVisAttributes(fpVisAttribs);
238   // Draw with auxiliary edges unless otherwise specified.
239   if (!fpVisAttribs->IsForceAuxEdgeVisible()) {
240     // Create a vis atts object for the modified vis atts.
241     // It is static so that we may return a reliable pointer to it.
242     static G4VisAttributes visAttsWithAuxEdges;
243     // Initialise it with the current vis atts and reset the pointer.
244     visAttsWithAuxEdges = *fpVisAttribs;
245     // Force auxiliary edges visible.
246     visAttsWithAuxEdges.SetForceAuxEdgeVisible();
247     fpVisAttribs = &visAttsWithAuxEdges;
248   }
249   RequestPrimitives (solid);
250 }
251 
252 void G4VSceneHandler::AddSolid (const G4Box& box) {
253   AddSolidT (box);
254   // If your graphics system is sophisticated enough to handle a
255   //  particular solid shape as a primitive, in your derived class write a
256   //  function to override this.
257   // Your function might look like this...
258   // void G4MySceneHandler::AddSolid (const G4Box& box) {
259   // Get and check applicable vis attributes.
260   //   fpVisAttribs = fpViewer->GetApplicableVisAttributes(fpVisAttribs);
261   // Do not draw if not visible.
262   //   if (fpVisAttribs->IsVisible()) {
263   //   Get parameters of appropriate object, e.g.:
264   //     G4double dx = box.GetXHalfLength ();
265   //     G4double dy = box.GetYHalfLength ();
266   //     G4double dz = box.GetZHalfLength ();
267   //     ...
268   //     and Draw or Store in your display List.
269 }
270 
271 void G4VSceneHandler::AddSolid (const G4Cons& cons) {
272   AddSolidT (cons);
273 }
274 
275 void G4VSceneHandler::AddSolid (const G4Orb& orb) {
276   AddSolidWithAuxiliaryEdges (orb);
277 }
278 
279 void G4VSceneHandler::AddSolid (const G4Para& para) {
280   AddSolidT (para);
281 }
282 
283 void G4VSceneHandler::AddSolid (const G4Sphere& sphere) {
284   AddSolidWithAuxiliaryEdges (sphere);
285 }
286 
287 void G4VSceneHandler::AddSolid (const G4Torus& torus) {
288   AddSolidWithAuxiliaryEdges (torus);
289 }
290 
291 void G4VSceneHandler::AddSolid (const G4Trap& trap) {
292   AddSolidT (trap);
293 }
294 
295 void G4VSceneHandler::AddSolid (const G4Trd& trd) {
296   AddSolidT (trd);
297 }
298 
299 void G4VSceneHandler::AddSolid (const G4Tubs& tubs) {
300   AddSolidT (tubs);
301 }
302 
303 void G4VSceneHandler::AddSolid (const G4Ellipsoid& ellipsoid) {
304   AddSolidWithAuxiliaryEdges (ellipsoid);
305 }
306 
307 void G4VSceneHandler::AddSolid (const G4Polycone& polycone) {
308   AddSolidT (polycone);
309 }
310 
311 void G4VSceneHandler::AddSolid (const G4Polyhedra& polyhedra) {
312   AddSolidT (polyhedra);
313 }
314 
315 void G4VSceneHandler::AddSolid (const G4TessellatedSolid& tess) {
316   AddSolidT (tess);
317 }
318 
319 void G4VSceneHandler::AddSolid (const G4VSolid& solid) {
320   AddSolidT (solid);
321 }
322 
323 void G4VSceneHandler::AddCompound (const G4VTrajectory& traj) {
324   G4TrajectoriesModel* trajectoriesModel =
325     dynamic_cast<G4TrajectoriesModel*>(fpModel);
326   if (trajectoriesModel)
327     traj.DrawTrajectory();
328   else {
329     G4Exception
330     ("G4VSceneHandler::AddCompound(const G4VTrajectory&)",
331      "visman0105", FatalException, "Not a G4TrajectoriesModel.");
332   }
333 }
334 
335 void G4VSceneHandler::AddCompound (const G4VHit& hit) {
336   // Cast away const because Draw is non-const!!!!
337   const_cast<G4VHit&>(hit).Draw();
338 }
339 
340 void G4VSceneHandler::AddCompound (const G4VDigi& digi) {
341   // Cast away const because Draw is non-const!!!!
342   const_cast<G4VDigi&>(digi).Draw();
343 }
344 
345 void G4VSceneHandler::AddCompound (const G4THitsMap<G4double>& hits) {
346   using MeshScoreMap = G4VScoringMesh::MeshScoreMap;
347   //G4cout << "AddCompound: hits: " << &hits << G4endl;
348   G4bool scoreMapHits = false;
349   G4ScoringManager* scoringManager = G4ScoringManager::GetScoringManagerIfExist();
350   if (scoringManager) {
351     std::size_t nMeshes = scoringManager->GetNumberOfMesh();
352     for (std::size_t iMesh = 0; iMesh < nMeshes; ++iMesh) {
353       G4VScoringMesh* mesh = scoringManager->GetMesh((G4int)iMesh);
354       if (mesh && mesh->IsActive()) {
355   MeshScoreMap scoreMap = mesh->GetScoreMap();
356         const G4String& mapNam = const_cast<G4THitsMap<G4double>&>(hits).GetName();
357   for(MeshScoreMap::const_iterator i = scoreMap.cbegin();
358       i != scoreMap.cend(); ++i) {
359     const G4String& scoreMapName = i->first;
360     if (scoreMapName == mapNam) {
361       G4DefaultLinearColorMap colorMap("G4VSceneHandlerColorMap");
362       scoreMapHits = true;
363       mesh->DrawMesh(scoreMapName, &colorMap);
364     }
365   }
366       }
367     }
368   }
369   if (scoreMapHits) {
370     static G4bool first = true;
371     if (first) {
372       first = false;
373       G4cout <<
374   "Scoring map drawn with default parameters."
375   "\n  To get gMocren file for gMocren browser:"
376   "\n    /vis/open gMocrenFile"
377   "\n    /vis/viewer/flush"
378   "\n  Many other options available with /score/draw... commands."
379   "\n  You might want to \"/vis/viewer/set/autoRefresh false\"."
380        << G4endl;
381     }
382   } else {  // Not score map hits.  Just call DrawAllHits.
383     // Cast away const because DrawAllHits is non-const!!!!
384     const_cast<G4THitsMap<G4double>&>(hits).DrawAllHits();
385   }
386 }
387 
388 void G4VSceneHandler::AddCompound (const G4THitsMap<G4StatDouble>& hits) {
389   using MeshScoreMap = G4VScoringMesh::MeshScoreMap;
390   //G4cout << "AddCompound: hits: " << &hits << G4endl;
391   G4bool scoreMapHits = false;
392   G4ScoringManager* scoringManager = G4ScoringManager::GetScoringManagerIfExist();
393   if (scoringManager) {
394     std::size_t nMeshes = scoringManager->GetNumberOfMesh();
395     for (std::size_t iMesh = 0; iMesh < nMeshes; ++iMesh) {
396       G4VScoringMesh* mesh = scoringManager->GetMesh((G4int)iMesh);
397       if (mesh && mesh->IsActive()) {
398   MeshScoreMap scoreMap = mesh->GetScoreMap();
399   for(MeshScoreMap::const_iterator i = scoreMap.cbegin();
400       i != scoreMap.cend(); ++i) {
401     const G4String& scoreMapName = i->first;
402     const G4THitsMap<G4StatDouble>* foundHits = i->second;
403     if (foundHits == &hits) {
404       G4DefaultLinearColorMap colorMap("G4VSceneHandlerColorMap");
405       scoreMapHits = true;
406       mesh->DrawMesh(scoreMapName, &colorMap);
407     }
408   }
409       }
410     }
411   }
412   if (scoreMapHits) {
413     static G4bool first = true;
414     if (first) {
415       first = false;
416       G4cout <<
417   "Scoring map drawn with default parameters."
418   "\n  To get gMocren file for gMocren browser:"
419   "\n    /vis/open gMocrenFile"
420   "\n    /vis/viewer/flush"
421   "\n  Many other options available with /score/draw... commands."
422   "\n  You might want to \"/vis/viewer/set/autoRefresh false\"."
423        << G4endl;
424     }
425   } else {  // Not score map hits.  Just call DrawAllHits.
426     // Cast away const because DrawAllHits is non-const!!!!
427     const_cast<G4THitsMap<G4StatDouble>&>(hits).DrawAllHits();
428   }
429 }
430 
431 void G4VSceneHandler::AddCompound(const G4Mesh& mesh)
432 {
433   G4warn <<
434   "There has been an attempt to draw a mesh with option \""
435   << fpViewer->GetViewParameters().GetSpecialMeshRenderingOption()
436   << "\":\n" << mesh
437   << "but it is not of a recognised type or is not implemented"
438   "\nby the current graphics driver. Instead we draw its"
439   "\ncontainer \"" << mesh.GetContainerVolume()->GetName() << "\"."
440   << G4endl;
441   const auto& pv = mesh.GetContainerVolume();
442   const auto& lv = pv->GetLogicalVolume();
443   const auto& solid = lv->GetSolid();
444   const auto& transform = mesh.GetTransform();
445   // Make sure container is visible
446   G4VisAttributes tmpVisAtts;  // Visible, white, not forced.
447   const auto& saveVisAtts = lv->GetVisAttributes();
448   if (saveVisAtts) {
449     tmpVisAtts = *saveVisAtts;
450     tmpVisAtts.SetVisibility(true);
451     auto colour = saveVisAtts->GetColour();
452     colour.SetAlpha(1.);
453     tmpVisAtts.SetColour(colour);
454   }
455   // Draw container
456   PreAddSolid(transform,tmpVisAtts);
457   solid->DescribeYourselfTo(*this);
458   PostAddSolid();
459   // Restore vis attributes
460   lv->SetVisAttributes(saveVisAtts);
461 }
462 
463 void G4VSceneHandler::AddViewerToList (G4VViewer* pViewer) {
464   fViewerList.push_back (pViewer);
465 }
466 
467 void G4VSceneHandler::AddPrimitive (const G4Polymarker& polymarker) {
468   switch (polymarker.GetMarkerType()) {
469     default:
470     case G4Polymarker::dots:
471     {
472       G4Circle dot (polymarker);
473       dot.SetWorldSize  (0.);
474       dot.SetScreenSize (0.1);  // Very small circle.
475       for (std::size_t iPoint = 0; iPoint < polymarker.size (); ++iPoint) {
476         dot.SetPosition (polymarker[iPoint]);
477         AddPrimitive (dot);
478       }
479     }
480       break;
481     case G4Polymarker::circles:
482     {
483       G4Circle circle (polymarker);  // Default circle
484       for (std::size_t iPoint = 0; iPoint < polymarker.size (); ++iPoint) {
485         circle.SetPosition (polymarker[iPoint]);
486         AddPrimitive (circle);
487       }
488     }
489       break;
490     case G4Polymarker::squares:
491     {
492       G4Square square (polymarker);  // Default square
493       for (std::size_t iPoint = 0; iPoint < polymarker.size (); ++iPoint) {
494         square.SetPosition (polymarker[iPoint]);
495         AddPrimitive (square);
496       }
497     }
498       break;
499   }
500 }
501 
502 void G4VSceneHandler::RemoveViewerFromList (G4VViewer* pViewer) {
503   fViewerList.remove(pViewer);  // Does nothing if already removed
504   // And reset current viewer
505   auto visManager = G4VisManager::GetInstance();
506   visManager->SetCurrentViewer(nullptr);
507 }
508 
509 
510 void G4VSceneHandler::AddPrimitive (const G4Plotter&) {
511   G4warn << "WARNING: Plotter not implemented for " << fSystem.GetName() << G4endl;
512   G4warn << "  Open a plotter-aware graphics system or remove plotter with" << G4endl;
513   G4warn << "  /vis/scene/removeModel Plotter" << G4endl;
514 }
515 
516 void G4VSceneHandler::SetScene (G4Scene* pScene) {
517   fpScene = pScene;
518   // Notify all viewers that a kernel visit is required.
519   G4ViewerListIterator i;
520   for (i = fViewerList.begin(); i != fViewerList.end(); i++) {
521     (*i) -> SetNeedKernelVisit (true);
522   }
523 }
524 
525 void G4VSceneHandler::RequestPrimitives (const G4VSolid& solid)
526 {
527   // Sometimes solids that have no substance get requested. They may
528   // be part of the geometry tree but have been "spirited away", for
529   // example by a Boolean subtraction in which the original volume
530   // is entirely inside the subtractor or an intersection in which
531   // the original volume is entirely outside the intersector.
532   // The problem is that the Boolean Processor still returns a
533   // polyhedron in these cases (IMHO it should not), so the
534   // workaround is to return before the damage is done.
535   // Algorithm by Evgueni Tcherniaev
536   auto pSolid = &solid;
537   auto pBooleanSolid = dynamic_cast<const G4BooleanSolid*>(pSolid);
538   if (pBooleanSolid) {
539     G4ThreeVector bmin, bmax;
540     pBooleanSolid->BoundingLimits(bmin, bmax);
541     G4bool isGood = false;
542     if (dynamic_cast<const G4SubtractionSolid*>(pBooleanSolid)) {
543       auto ptrB = pBooleanSolid->GetConstituentSolid(1);
544       for (G4int i=0; i<10; ++i) {
545         G4double x = bmin.x() + (bmax.x() - bmin.x())*G4QuickRand();
546         G4double y = bmin.y() + (bmax.y() - bmin.y())*G4QuickRand();
547         G4double z = bmin.z() + (bmax.z() - bmin.z())*G4QuickRand();
548         if (ptrB->Inside(G4ThreeVector(x,y,bmin.z())) != kInside) { isGood = true; break; }
549         if (ptrB->Inside(G4ThreeVector(x,y,bmax.z())) != kInside) { isGood = true; break; }
550         if (ptrB->Inside(G4ThreeVector(x,bmin.y(),z)) != kInside) { isGood = true; break; }
551         if (ptrB->Inside(G4ThreeVector(x,bmax.y(),z)) != kInside) { isGood = true; break; }
552         if (ptrB->Inside(G4ThreeVector(bmin.x(),y,z)) != kInside) { isGood = true; break; }
553         if (ptrB->Inside(G4ThreeVector(bmax.x(),y,z)) != kInside) { isGood = true; break; }
554       }
555     } else if (dynamic_cast<const G4IntersectionSolid*>(pBooleanSolid)) {
556       auto ptrB = pBooleanSolid->GetConstituentSolid(1);
557       for (G4int i=0; i<10; ++i) {
558         G4double x = bmin.x() + (bmax.x() - bmin.x())*G4QuickRand();
559         G4double y = bmin.y() + (bmax.y() - bmin.y())*G4QuickRand();
560         G4double z = bmin.z() + (bmax.z() - bmin.z())*G4QuickRand();
561         if (ptrB->Inside(G4ThreeVector(x,y,bmin.z())) == kInside) { isGood = true; break; }
562         if (ptrB->Inside(G4ThreeVector(x,y,bmax.z())) == kInside) { isGood = true; break; }
563         if (ptrB->Inside(G4ThreeVector(x,bmin.y(),z)) == kInside) { isGood = true; break; }
564         if (ptrB->Inside(G4ThreeVector(x,bmax.y(),z)) == kInside) { isGood = true; break; }
565         if (ptrB->Inside(G4ThreeVector(bmin.x(),y,z)) == kInside) { isGood = true; break; }
566         if (ptrB->Inside(G4ThreeVector(bmax.x(),y,z)) == kInside) { isGood = true; break; }
567       }
568     }
569     if (!isGood)
570     {
571       for (G4int i=0; i<10000; ++i) {
572         G4double x = bmin.x() + (bmax.x() - bmin.x())*G4QuickRand();
573         G4double y = bmin.y() + (bmax.y() - bmin.y())*G4QuickRand();
574         G4double z = bmin.z() + (bmax.z() - bmin.z())*G4QuickRand();
575         if (pBooleanSolid->Inside(G4ThreeVector(x,y,z)) == kInside) { isGood = true; break; }
576       }
577     }
578     if (!isGood) return;
579   }
580 
581   const G4ViewParameters::DrawingStyle style = GetDrawingStyle(fpVisAttribs);
582   const G4ViewParameters& vp = fpViewer->GetViewParameters();
583 
584   switch (style) {
585     default:
586     case G4ViewParameters::wireframe:
587     case G4ViewParameters::hlr:
588     case G4ViewParameters::hsr:
589     case G4ViewParameters::hlhsr:
590     {
591       // Use polyhedral representation
592       G4Polyhedron::SetNumberOfRotationSteps (GetNoOfSides (fpVisAttribs));
593       G4Polyhedron* pPolyhedron = solid.GetPolyhedron ();
594       G4Polyhedron::ResetNumberOfRotationSteps ();
595       if (pPolyhedron) {
596         pPolyhedron -> SetVisAttributes (fpVisAttribs);
597         BeginPrimitives (fObjectTransformation);
598         AddPrimitive (*pPolyhedron);
599         EndPrimitives ();
600         break;
601       } else {  // Print warnings and drop through to cloud
602         G4VisManager::Verbosity verbosity = G4VisManager::GetVerbosity();
603         auto pPVModel = dynamic_cast<G4PhysicalVolumeModel*>(fpModel);
604         if (pPVModel) {
605           auto problematicVolume = pPVModel->GetCurrentPV();
606           if (fProblematicVolumes.find(problematicVolume) == fProblematicVolumes.end()) {
607             fProblematicVolumes[problematicVolume] = "Null polyhedron";
608             if (verbosity >= G4VisManager::errors) {
609               G4warn <<
610               "ERROR: G4VSceneHandler::RequestPrimitives"
611               "\n  Polyhedron not available for " << solid.GetName ();
612               G4warn << "\n  Touchable path: " << pPVModel->GetFullPVPath();
613               static G4bool explanation = false;
614               if (!explanation) {
615                 explanation = true;
616                 G4warn <<
617                 "\n  This means it cannot be visualized in the usual way on most systems."
618                 "\n  1) The solid may not have implemented the CreatePolyhedron method."
619                 "\n  2) For Boolean solids, the BooleanProcessor, which attempts to create"
620                 "\n     the resultant polyhedron, may have failed."
621                 "\n  Try RayTracer. It uses Geant4's tracking algorithms instead.";
622               }
623             }
624             G4warn << "\n  Drawing solid with cloud of points.";
625             G4warn << G4endl;
626           }
627         }
628       }
629     }
630       [[fallthrough]];
631 
632     case G4ViewParameters::cloud:
633     {
634       // Form solid out of cloud of dots on surface of solid
635       G4Polymarker dots;
636       // Note: OpenGL has a fast implementation of polymarker so it's better
637       // to build a polymarker rather than add a succession of circles.
638       // And anyway, in Qt, in the latter case each circle would be a scene-tree
639       // entry, something we would want to avoid.
640       dots.SetVisAttributes(fpVisAttribs);
641       dots.SetMarkerType(G4Polymarker::dots);
642       dots.SetSize(G4VMarker::screen,1.);
643       G4int numberOfCloudPoints = GetNumberOfCloudPoints(fpVisAttribs);
644       if (numberOfCloudPoints <= 0) numberOfCloudPoints = vp.GetNumberOfCloudPoints();
645       for (G4int i = 0; i < numberOfCloudPoints; ++i) {
646   G4ThreeVector p = solid.GetPointOnSurface();
647   dots.push_back(p);
648       }
649       BeginPrimitives (fObjectTransformation);
650       AddPrimitive(dots);
651       EndPrimitives ();
652       break;
653     }
654   }
655 }
656 
657 //namespace {
658 //  void DrawExtent(const G4VModel* pModel)
659 //  {
660 //    // Show extent boxes - debug only, OGLSX only (OGLSQt problem?)
661 //    if (pModel->GetExtent() != G4VisExtent::GetNullExtent()) {
662 //      const auto& extent = pModel->GetExtent();
663 //      const auto& centre = extent.GetExtentCenter();
664 //      const auto& position = G4Translate3D(centre);
665 //      const auto& dx = (extent.GetXmax()-extent.GetXmin())/2.;
666 //      const auto& dy = (extent.GetYmax()-extent.GetYmin())/2.;
667 //      const auto& dz = (extent.GetZmax()-extent.GetZmin())/2.;
668 //      auto visAtts = G4VisAttributes();
669 //      visAtts.SetForceWireframe();
670 //      G4Box extentBox("Extent",dx,dy,dz);
671 //      G4VisManager::GetInstance()->Draw(extentBox,visAtts,position);
672 //    }
673 //  }
674 //}
675 
676 void G4VSceneHandler::ProcessScene()
677 {
678   // Assumes graphics database store has already been cleared if
679   // relevant for the particular scene handler.
680 
681   if(!fpScene)
682     return;
683 
684   if(fpScene->GetExtent() == G4VisExtent::GetNullExtent())
685   {
686     G4Exception("G4VSceneHandler::ProcessScene", "visman0106", JustWarning,
687                 "The scene has no extent.");
688   }
689 
690   G4VisManager* visManager = G4VisManager::GetInstance();
691 
692   if(!visManager->GetConcreteInstance())
693     return;
694 
695   G4VisManager::Verbosity verbosity = visManager->GetVerbosity();
696 
697   fReadyForTransients = false;
698 
699   // Reset fMarkForClearingTransientStore. (Leaving
700   // fMarkForClearingTransientStore true causes problems with
701   // recomputing transients below.)  Restore it again at end...
702   G4bool tmpMarkForClearingTransientStore = fMarkForClearingTransientStore;
703   fMarkForClearingTransientStore          = false;
704 
705   // Traverse geometry tree and send drawing primitives to window(s).
706 
707   const std::vector<G4Scene::Model>& runDurationModelList =
708     fpScene->GetRunDurationModelList();
709 
710   if(runDurationModelList.size()) {
711     if(verbosity >= G4VisManager::confirmations) {
712       G4cout << "Traversing scene data..." << G4endl;
713       static G4int first = true;
714       if (first) {
715         first = false;
716         G4cout <<
717         "(This could happen more than once - in fact, up to three times"
718         "\nper rebuild, for opaque, transparent and non-hidden markers.)"
719         << G4endl;
720       }
721     }
722 
723     // Reset visibility of all objects to false - visible objects will then set to true
724     fpViewer->AccessSceneTree().ResetVisibility();
725 
726     BeginModeling();
727 
728     // Create modeling parameters from view parameters...
729     G4ModelingParameters* pMP = CreateModelingParameters();
730 
731     for(std::size_t i = 0; i < runDurationModelList.size(); ++i) {
732       if(runDurationModelList[i].fActive) {
733         fpModel = runDurationModelList[i].fpModel;
734         fpModel->SetModelingParameters(pMP);
735 
736         // Describe to the current scene handler
737         fpModel->DescribeYourselfTo(*this);
738 
739         // To see the extents of each model represented as wireframe boxes,
740         // uncomment the next line and DrawExtent in namespace above.
741         // DrawExtent(fpModel);
742 
743         // Enter models in the scene tree. Then for PV models, describe
744         // the model to the scene tree, i.e., enter all the touchables.
745         fpViewer->InsertModelInSceneTree(fpModel);
746         auto pPVModel = dynamic_cast<G4PhysicalVolumeModel*>(fpModel);
747         if (pPVModel) {
748           G4VViewer::SceneTreeScene sceneTreeScene(fpViewer, pPVModel);
749           fpModel->DescribeYourselfTo(sceneTreeScene);
750         }
751 
752         // Reset modeling parameters pointer
753         fpModel->SetModelingParameters(0);
754       }
755     }
756 
757     fpModel = 0;
758     delete pMP;
759 
760     EndModeling();
761   }
762 
763   // Some printing
764   if(verbosity >= G4VisManager::confirmations) {
765     for (const auto& model: runDurationModelList) {
766       if (model.fActive) {
767         auto pvModel = dynamic_cast<G4PhysicalVolumeModel*>(model.fpModel);
768         if (pvModel) {
769           G4int nTouchables = 0;
770           G4cout << "Numbers of touchables by depth in model \""
771           << pvModel->GetGlobalDescription() << "\":";
772           for (const auto& dn : pvModel->GetNumberOfTouchables()) {
773             G4cout << "\n  Depth " << dn.first << ": " << dn.second;
774             nTouchables += dn.second;
775           }
776           G4cout << "\n  Total number of touchables: " << nTouchables << G4endl;
777         }
778       }
779     }
780 
781     if (fProblematicVolumes.size() > 0) {
782       G4cout << "Problematic volumes:";
783       for (const auto& prob: fProblematicVolumes) {
784         G4cout << "\n  " << prob.first->GetName() << " (" << prob.second << ')';
785       }
786       G4cout << G4endl;
787     }
788   }
789 
790   fReadyForTransients = true;
791 
792   // Refresh event from end-of-event model list.
793   // Allow only in Idle or GeomClosed state...
794   G4StateManager* stateManager = G4StateManager::GetStateManager();
795   G4ApplicationState state     = stateManager->GetCurrentState();
796   if(state == G4State_Idle || state == G4State_GeomClosed)
797   {
798     visManager->SetEventRefreshing(true);
799 
800     if(visManager->GetRequestedEvent())
801     {
802       DrawEvent(visManager->GetRequestedEvent());
803     }
804     else
805     {
806       G4RunManager* runManager = G4RunManagerFactory::GetMasterRunManager();
807       if(runManager)
808       {
809         const G4Run* run = runManager->GetCurrentRun();
810         // Draw a null event in order to pick up models for the scene tree even before a run
811         if (run == nullptr) DrawEvent(0);
812         const std::vector<const G4Event*>* events =
813           run ? run->GetEventVector() : 0;
814         std::size_t nKeptEvents = 0;
815         if(events)
816           nKeptEvents = events->size();
817         if(nKeptEvents)
818         {
819           if(fpScene->GetRefreshAtEndOfEvent())
820           {
821             if(verbosity >= G4VisManager::confirmations)
822             {
823               G4cout << "Refreshing event..." << G4endl;
824             }
825             const G4Event* event = 0;
826             if(events && events->size())
827               event = events->back();
828             if(event)
829               DrawEvent(event);
830           }
831           else
832           {  // Accumulating events.
833 
834             if(verbosity >= G4VisManager::confirmations)
835             {
836               G4cout << "Refreshing events in run..." << G4endl;
837             }
838             for(const auto& event : *events)
839             {
840               if(event)
841                 DrawEvent(event);
842             }
843 
844             if(!fpScene->GetRefreshAtEndOfRun())
845             {
846               if(verbosity >= G4VisManager::warnings)
847               {
848                 G4warn << "WARNING: Cannot refresh events accumulated over more"
849                           "\n  than one runs.  Refreshed just the last run."
850                        << G4endl;
851               }
852             }
853           }
854         }
855       }
856     }
857     visManager->SetEventRefreshing(false);
858   }
859 
860   // Refresh end-of-run model list.
861   // Allow only in Idle or GeomClosed state...
862   if(state == G4State_Idle || state == G4State_GeomClosed)
863   {
864     DrawEndOfRunModels();
865   }
866 
867   fMarkForClearingTransientStore = tmpMarkForClearingTransientStore;
868 }
869 
870 void G4VSceneHandler::DrawEvent(const G4Event* event)
871 {
872   if(!fpViewer->ReadyToDraw()) return;
873   const std::vector<G4Scene::Model>& EOEModelList =
874     fpScene -> GetEndOfEventModelList ();
875   std::size_t nModels = EOEModelList.size();
876   if (nModels) {
877     G4ModelingParameters* pMP = CreateModelingParameters();
878     pMP->SetEvent(event);
879     for (std::size_t i = 0; i < nModels; ++i) {
880       if (EOEModelList[i].fActive) {
881         fpModel = EOEModelList[i].fpModel;
882         fpModel -> SetModelingParameters(pMP);
883 
884         // Describe to the current scene handler
885         fpModel -> DescribeYourselfTo (*this);
886 
887         // Enter models in the scene tree
888         fpViewer->InsertModelInSceneTree(fpModel);
889 
890         // Reset modeling parameters pointer
891         fpModel -> SetModelingParameters(0);
892       }
893     }
894     fpModel = 0;
895     delete pMP;
896   }
897 }
898 
899 void G4VSceneHandler::DrawEndOfRunModels()
900 {
901   if(!fpViewer->ReadyToDraw()) return;
902   const std::vector<G4Scene::Model>& EORModelList =
903     fpScene -> GetEndOfRunModelList ();
904   std::size_t nModels = EORModelList.size();
905   if (nModels) {
906     G4ModelingParameters* pMP = CreateModelingParameters();
907     pMP->SetEvent(0);
908     for (std::size_t i = 0; i < nModels; ++i) {
909       if (EORModelList[i].fActive) {
910         fpModel = EORModelList[i].fpModel;
911         fpModel -> SetModelingParameters(pMP);
912 
913         // Describe to the current scene handler
914         fpModel -> DescribeYourselfTo (*this);
915 
916         // Enter models in the scene tree
917         fpViewer->InsertModelInSceneTree(fpModel);
918 
919         // Reset modeling parameters pointer
920         fpModel -> SetModelingParameters(0);
921       }
922     }
923     fpModel = 0;
924     delete pMP;
925   }
926 }
927 
928 G4ModelingParameters* G4VSceneHandler::CreateModelingParameters ()
929 {
930   // Create modeling parameters from View Parameters...
931   if (!fpViewer) return NULL;
932 
933   const G4ViewParameters& vp = fpViewer -> GetViewParameters ();
934 
935   // Convert drawing styles...
936   G4ModelingParameters::DrawingStyle modelDrawingStyle =
937   G4ModelingParameters::wf;
938   switch (vp.GetDrawingStyle ()) {
939     default:
940     case G4ViewParameters::wireframe:
941       modelDrawingStyle = G4ModelingParameters::wf;
942       break;
943     case G4ViewParameters::hlr:
944       modelDrawingStyle = G4ModelingParameters::hlr;
945       break;
946     case G4ViewParameters::hsr:
947       modelDrawingStyle = G4ModelingParameters::hsr;
948       break;
949     case G4ViewParameters::hlhsr:
950       modelDrawingStyle = G4ModelingParameters::hlhsr;
951       break;
952     case G4ViewParameters::cloud:
953       modelDrawingStyle = G4ModelingParameters::cloud;
954       break;
955   }
956 
957   // Decide if covered daughters are really to be culled...
958   G4bool reallyCullCovered =
959     vp.IsCullingCovered()   // Culling daughters depends also on...
960     && !vp.IsSection ()     // Sections (DCUT) not requested.
961     && !vp.IsCutaway ()     // Cutaways not requested.
962     ;
963 
964   G4ModelingParameters* pModelingParams = new G4ModelingParameters
965     (vp.GetDefaultVisAttributes (),
966      modelDrawingStyle,
967      vp.IsCulling (),
968      vp.IsCullingInvisible (),
969      vp.IsDensityCulling (),
970      vp.GetVisibleDensity (),
971      reallyCullCovered,
972      vp.GetNoOfSides ()
973      );
974 
975   pModelingParams->SetNumberOfCloudPoints(vp.GetNumberOfCloudPoints());
976   pModelingParams->SetWarning
977     (G4VisManager::GetVerbosity() >= G4VisManager::warnings);
978 
979   pModelingParams->SetCBDAlgorithmNumber(vp.GetCBDAlgorithmNumber());
980   pModelingParams->SetCBDParameters(vp.GetCBDParameters());
981 
982   pModelingParams->SetExplodeFactor(vp.GetExplodeFactor());
983   pModelingParams->SetExplodeCentre(vp.GetExplodeCentre());
984 
985   pModelingParams->SetSectionSolid(CreateSectionSolid());
986 
987   if (vp.GetCutawayMode() == G4ViewParameters::cutawayUnion) {
988     pModelingParams->SetCutawayMode(G4ModelingParameters::cutawayUnion);
989   } else if (vp.GetCutawayMode() == G4ViewParameters::cutawayIntersection) {
990     pModelingParams->SetCutawayMode(G4ModelingParameters::cutawayIntersection);
991   }
992 
993   pModelingParams->SetCutawaySolid(CreateCutawaySolid());
994   // The polyhedron objects are deleted in the modeling parameters destructor.
995   
996   pModelingParams->SetVisAttributesModifiers(vp.GetVisAttributesModifiers());
997 
998   pModelingParams->SetSpecialMeshRendering(vp.IsSpecialMeshRendering());
999   pModelingParams->SetSpecialMeshVolumes(vp.GetSpecialMeshVolumes());
1000 
1001   return pModelingParams;
1002 }
1003 
1004 G4DisplacedSolid* G4VSceneHandler::CreateSectionSolid()
1005 {
1006   G4DisplacedSolid* sectioner = 0;
1007 
1008   const G4ViewParameters& vp = fpViewer->GetViewParameters();
1009   if (vp.IsSection () ) {
1010 
1011     G4double radius = fpScene->GetExtent().GetExtentRadius();
1012     G4double safe = radius + fpScene->GetExtent().GetExtentCentre().mag();
1013     G4VSolid* sectionBox =
1014       new G4Box("_sectioner", safe, safe, 1.e-5 * radius);  // Thin in z-plane...
1015 
1016     const G4Plane3D& sp = vp.GetSectionPlane ();
1017     G4ThreeVector normal = sp.normal();
1018     G4Transform3D requiredTransform = G4Translate3D(normal*(-sp.d())) *
1019     G4Rotate3D(G4ThreeVector(0,0,1), G4ThreeVector(0,1,0), normal, normal.orthogonal());
1020 
1021     sectioner = new G4DisplacedSolid
1022     ("_displaced_sectioning_box", sectionBox, requiredTransform);
1023   }
1024   
1025   return sectioner;
1026 }
1027 
1028 G4DisplacedSolid* G4VSceneHandler::CreateCutawaySolid()
1029 {
1030   const auto& vp = fpViewer->GetViewParameters();
1031   const auto& nPlanes = vp.GetCutawayPlanes().size();
1032 
1033   if (nPlanes == 0) return nullptr;
1034 
1035   std::vector<G4DisplacedSolid*> cutaway_solids;
1036 
1037   G4double radius = fpScene->GetExtent().GetExtentRadius();
1038   G4double safe = radius + fpScene->GetExtent().GetExtentCentre().mag();
1039   auto cutawayBox = new G4Box("_cutaway_box", safe, safe, safe);
1040 
1041   // if (vp.GetCutawayMode() == G4ViewParameters::cutawayUnion) we need a subtractor that is
1042   // the intersection of displaced cutaway boxes, displaced so that a subtraction keeps the
1043   // positive values a*x+b*y+c*z+d>0, so we have to invert the normal. This may appear
1044   // "back to front". The parameter "cutawayUnion" means "the union of volumes
1045   // that remain *after* cutaway", because we base the concept on OpenGL cutaway planes and make
1046   // a "union" of what remains by superimposing up to 3 passes - see G4OpenGLViewer::SetView
1047   // and G4OpenGLImmediate/StoredViewer::ProcessView. So we have to create a subtractor
1048   // that is the intersection of inverted cutaway planes.
1049 
1050   // Conversely, if (vp.GetCutawayMode() == G4ViewParameters::cutawayIntersection) we have to
1051   // create an intersector that is the intersector of intersected non-inverted cutaway planes.
1052 
1053   for (size_t plane_no = 0; plane_no < nPlanes; plane_no++)
1054   {
1055     const G4Plane3D& sp = vp.GetCutawayPlanes()[plane_no];
1056     G4Transform3D requiredTransform;
1057     G4ThreeVector normal;
1058     switch (vp.GetCutawayMode()) {
1059       case G4ViewParameters::cutawayUnion:
1060         normal = -sp.normal();  // Invert normal - we want a subtractor
1061         requiredTransform = G4Translate3D(normal*(safe + sp.d())) *
1062         G4Rotate3D(G4ThreeVector(0,0,1), G4ThreeVector(0,1,0), normal, normal.orthogonal());
1063         break;
1064       case G4ViewParameters::cutawayIntersection:
1065         normal = sp.normal();
1066         requiredTransform = G4Translate3D(normal*(safe - sp.d())) *
1067         G4Rotate3D(G4ThreeVector(0,0,1), G4ThreeVector(0,1,0), normal, normal.orthogonal());
1068         break;
1069     }
1070     cutaway_solids.push_back
1071     (new G4DisplacedSolid("_displaced_cutaway_box", cutawayBox, requiredTransform));
1072   }
1073 
1074   if (nPlanes == 1) return (G4DisplacedSolid*) cutaway_solids[0];
1075 
1076   G4IntersectionSolid *union2 = nullptr, *union3 = nullptr;
1077   G4IntersectionSolid *intersection2 = nullptr, *intersection3 = nullptr;
1078   switch (vp.GetCutawayMode()) {
1079 
1080     case G4ViewParameters::cutawayUnion:
1081       // Here we make a subtractor of intersections of inverted cutaway planes.
1082       union2 = new G4IntersectionSolid("_union_2", cutaway_solids[0], cutaway_solids[1]);
1083       if (nPlanes == 2) return (G4DisplacedSolid*)union2;
1084       else if (nPlanes == 3) {
1085         union3 = new G4IntersectionSolid("_union_3", union2, cutaway_solids[2]);
1086         return (G4DisplacedSolid*)union3;
1087       }
1088       break;
1089 
1090     case G4ViewParameters::cutawayIntersection:
1091       // And here we make an intersector of intersections of non-inverted cutaway planes.
1092       intersection2
1093       = new G4IntersectionSolid("_intersection_2", cutaway_solids[0], cutaway_solids[1]);
1094       if (nPlanes == 2) return (G4DisplacedSolid*)intersection2;
1095       else if (nPlanes == 3) {
1096         intersection3
1097         = new G4IntersectionSolid("_intersection_3", intersection2, cutaway_solids[2]);
1098         return (G4DisplacedSolid*)intersection3;
1099       }
1100       break;
1101   }
1102 
1103   G4Exception("G4VSceneHandler::CreateCutawaySolid", "visman0107", JustWarning,
1104               "Not programmed for more than 3 cutaway planes");
1105   return nullptr;
1106 }
1107 
1108 void G4VSceneHandler::LoadAtts(const G4Visible& visible, G4AttHolder* holder)
1109 {
1110   // Load G4Atts from G4VisAttributes, if any...
1111   const G4VisAttributes* va = visible.GetVisAttributes();
1112   if (va) {
1113     const std::map<G4String,G4AttDef>* vaDefs =
1114       va->GetAttDefs();
1115     if (vaDefs) {
1116       holder->AddAtts(visible.GetVisAttributes()->CreateAttValues(), vaDefs);
1117     }
1118   }
1119 
1120   G4PhysicalVolumeModel* pPVModel =
1121     dynamic_cast<G4PhysicalVolumeModel*>(fpModel);
1122   if (pPVModel) {
1123     // Load G4Atts from G4PhysicalVolumeModel...
1124     const std::map<G4String,G4AttDef>* pvDefs = pPVModel->GetAttDefs();
1125     if (pvDefs) {
1126       holder->AddAtts(pPVModel->CreateCurrentAttValues(), pvDefs);
1127     }
1128   }
1129 
1130   G4TrajectoriesModel* trajModel = dynamic_cast<G4TrajectoriesModel*>(fpModel);
1131   if (trajModel) {
1132     // Load G4Atts from trajectory model...
1133     const std::map<G4String,G4AttDef>* trajModelDefs = trajModel->GetAttDefs();
1134     if (trajModelDefs) {
1135       holder->AddAtts(trajModel->CreateCurrentAttValues(), trajModelDefs);
1136     }
1137     // Load G4Atts from trajectory...
1138     const G4VTrajectory* traj = trajModel->GetCurrentTrajectory();
1139     if (traj) {
1140       const std::map<G4String,G4AttDef>* trajDefs = traj->GetAttDefs();
1141       if (trajDefs) {
1142         holder->AddAtts(traj->CreateAttValues(), trajDefs);
1143       }
1144       G4int nPoints = traj->GetPointEntries();
1145       for (G4int i = 0; i < nPoints; ++i) {
1146         G4VTrajectoryPoint* trajPoint = traj->GetPoint(i);
1147         if (trajPoint) {
1148           const std::map<G4String,G4AttDef>* pointDefs = trajPoint->GetAttDefs();
1149           if (pointDefs) {
1150             holder->AddAtts(trajPoint->CreateAttValues(), pointDefs);
1151           }
1152         }
1153       }
1154     }
1155   }
1156 
1157   G4HitsModel* hitsModel = dynamic_cast<G4HitsModel*>(fpModel);
1158   if (hitsModel) {
1159     // Load G4Atts from hit...
1160     const G4VHit* hit = hitsModel->GetCurrentHit();
1161     const std::map<G4String,G4AttDef>* hitsDefs = hit->GetAttDefs();
1162     if (hitsDefs) {
1163       holder->AddAtts(hit->CreateAttValues(), hitsDefs);
1164     }
1165   }
1166 }
1167 
1168 const G4Colour& G4VSceneHandler::GetColour () {
1169   fpVisAttribs = fpViewer->GetApplicableVisAttributes(fpVisAttribs);
1170   const G4Colour& colour = fpVisAttribs -> GetColour ();
1171   return colour;
1172 }
1173 
1174 const G4Colour& G4VSceneHandler::GetColour (const G4Visible& visible) {
1175   auto pVA = visible.GetVisAttributes();
1176   if (!pVA) pVA = fpViewer->GetViewParameters().GetDefaultVisAttributes();
1177   return pVA->GetColour();
1178 }
1179 
1180 const G4Colour& G4VSceneHandler::GetTextColour (const G4Text& text) {
1181   auto pVA = text.GetVisAttributes();
1182   if (!pVA) pVA = fpViewer->GetViewParameters().GetDefaultTextVisAttributes();
1183   return pVA->GetColour();
1184 }
1185 
1186 G4double G4VSceneHandler::GetLineWidth(const G4VisAttributes* pVisAttribs)
1187 {
1188   G4double lineWidth = pVisAttribs->GetLineWidth();
1189   if (lineWidth < 1.) lineWidth = 1.;
1190   lineWidth *= fpViewer -> GetViewParameters().GetGlobalLineWidthScale();
1191   if (lineWidth < 1.) lineWidth = 1.;
1192   return lineWidth;
1193 }
1194 
1195 G4ViewParameters::DrawingStyle G4VSceneHandler::GetDrawingStyle
1196 (const G4VisAttributes* pVisAttribs) {
1197   // Drawing style is normally determined by the view parameters, but
1198   // it can be overriddden by the ForceDrawingStyle flag in the vis
1199   // attributes.
1200   const G4ViewParameters& vp = fpViewer->GetViewParameters();
1201   const G4ViewParameters::DrawingStyle viewerStyle = vp.GetDrawingStyle();
1202   G4ViewParameters::DrawingStyle resultantStyle = viewerStyle;
1203   if (pVisAttribs -> IsForceDrawingStyle ()) {
1204     G4VisAttributes::ForcedDrawingStyle forcedStyle =
1205     pVisAttribs -> GetForcedDrawingStyle ();
1206     // This is complicated because if hidden line and surface removal
1207     // has been requested we wish to preserve this sometimes.
1208     switch (forcedStyle) {
1209       case (G4VisAttributes::solid):
1210         switch (viewerStyle) {
1211           case (G4ViewParameters::hlr):
1212             resultantStyle = G4ViewParameters::hlhsr;
1213             break;
1214           case (G4ViewParameters::wireframe):
1215             resultantStyle = G4ViewParameters::hsr;
1216             break;
1217           case (G4ViewParameters::cloud):
1218             resultantStyle = G4ViewParameters::hsr;
1219             break;
1220           case (G4ViewParameters::hlhsr):
1221           case (G4ViewParameters::hsr):
1222             break;
1223         }
1224         break;
1225       case (G4VisAttributes::cloud):
1226         resultantStyle = G4ViewParameters::cloud;
1227         break;
1228       case (G4VisAttributes::wireframe):
1229       default:
1230         // But if forced style is wireframe, do it, because one of its
1231         // main uses is in displaying the consituent solids of a Boolean
1232         // solid and their surfaces overlap with the resulting Booean
1233         // solid, making a mess if hlr is specified.
1234         resultantStyle = G4ViewParameters::wireframe;
1235         break;
1236     }
1237   }
1238   return resultantStyle;
1239 }
1240 
1241 G4int G4VSceneHandler::GetNumberOfCloudPoints
1242 (const G4VisAttributes* pVisAttribs) const {
1243   // Returns no of cloud points from current view parameters, unless the user
1244   // has forced through the vis attributes, thereby over-riding the
1245   // current view parameter.
1246   G4int numberOfCloudPoints = fpViewer->GetViewParameters().GetNumberOfCloudPoints();
1247   if (pVisAttribs -> IsForceDrawingStyle() &&
1248       pVisAttribs -> GetForcedDrawingStyle() == G4VisAttributes::cloud &&
1249       pVisAttribs -> GetForcedNumberOfCloudPoints() > 0) {
1250     numberOfCloudPoints = pVisAttribs -> GetForcedNumberOfCloudPoints();
1251   }
1252   return numberOfCloudPoints;
1253 }
1254 
1255 G4bool G4VSceneHandler::GetAuxEdgeVisible (const G4VisAttributes* pVisAttribs) {
1256   G4bool isAuxEdgeVisible = fpViewer->GetViewParameters().IsAuxEdgeVisible ();
1257   if (pVisAttribs -> IsForceAuxEdgeVisible()) {
1258     isAuxEdgeVisible = pVisAttribs->IsForcedAuxEdgeVisible();
1259   }
1260   return isAuxEdgeVisible;
1261 }
1262 
1263 G4double G4VSceneHandler::GetMarkerSize
1264 (const G4VMarker& marker, 
1265  G4VSceneHandler::MarkerSizeType& markerSizeType)
1266 {
1267   G4bool userSpecified = marker.GetWorldSize() || marker.GetScreenSize();
1268   const G4VMarker& defaultMarker =
1269     fpViewer -> GetViewParameters().GetDefaultMarker();
1270   G4double size = userSpecified ?
1271     marker.GetWorldSize() : defaultMarker.GetWorldSize();
1272   if (size) {
1273     // Draw in world coordinates.
1274     markerSizeType = world;
1275   }
1276   else {
1277     size = userSpecified ?
1278       marker.GetScreenSize() : defaultMarker.GetScreenSize();
1279     // Draw in screen coordinates.
1280     markerSizeType = screen;
1281   }
1282   size *= fpViewer -> GetViewParameters().GetGlobalMarkerScale();
1283   if (markerSizeType == screen && size < 1.) size = 1.;
1284   return size;
1285 }
1286 
1287 G4int G4VSceneHandler::GetNoOfSides(const G4VisAttributes* pVisAttribs)
1288 {
1289   // No. of sides (lines segments per circle) is normally determined
1290   // by the view parameters, but it can be overriddden by the
1291   // ForceLineSegmentsPerCircle in the vis attributes.
1292   G4int lineSegmentsPerCircle = fpViewer->GetViewParameters().GetNoOfSides();
1293   if (pVisAttribs) {
1294     if (pVisAttribs->IsForceLineSegmentsPerCircle())
1295       lineSegmentsPerCircle = pVisAttribs->GetForcedLineSegmentsPerCircle();
1296     if (lineSegmentsPerCircle < pVisAttribs->GetMinLineSegmentsPerCircle()) {
1297       lineSegmentsPerCircle = pVisAttribs->GetMinLineSegmentsPerCircle();
1298       G4warn <<
1299   "G4VSceneHandler::GetNoOfSides: attempt to set the"
1300   "\nnumber of line segments per circle < " << lineSegmentsPerCircle
1301        << "; forced to " << pVisAttribs->GetMinLineSegmentsPerCircle() << G4endl;
1302     }
1303   }
1304   return lineSegmentsPerCircle;
1305 }
1306 
1307 std::ostream& operator << (std::ostream& os, const G4VSceneHandler& sh) {
1308 
1309   os << "Scene handler " << sh.fName << " has "
1310      << sh.fViewerList.size () << " viewer(s):";
1311   for (std::size_t i = 0; i < sh.fViewerList.size (); ++i) {
1312     os << "\n  " << *(sh.fViewerList [i]);
1313   }
1314 
1315   if (sh.fpScene) {
1316     os << "\n  " << *sh.fpScene;
1317   }
1318   else {
1319     os << "\n  This scene handler currently has no scene.";
1320   }
1321 
1322   return os;
1323 }
1324 
1325 void G4VSceneHandler::PseudoSceneFor3DRectMeshPositions::AddSolid(const G4Box&) {
1326   if (fpPVModel->GetCurrentDepth() == fpMesh->GetMeshDepth()) {  // Leaf-level cells only
1327     const auto& material = fpPVModel->GetCurrentLV()->GetMaterial();
1328     const auto& name = material? material->GetName(): fpMesh->GetContainerVolume()->GetName();
1329     const auto& pVisAtts = fpPVModel->GetCurrentLV()->GetVisAttributes();
1330     // Get position in world coordinates
1331     // As a parameterisation the box is transformed by the current transformation
1332     // and its centre, originally by definition at (0,0,0), is now translated.
1333     const G4ThreeVector& position = fpCurrentObjectTransformation->getTranslation();
1334     fPositionByMaterial.insert(std::make_pair(material,position));
1335     if (fNameAndVisAttsByMaterial.find(material) == fNameAndVisAttsByMaterial.end())
1336       // Store name and vis attributes of first encounter with this material
1337       fNameAndVisAttsByMaterial[material] = NameAndVisAtts(name,*pVisAtts);
1338   }
1339 }
1340 
1341 void G4VSceneHandler::PseudoSceneForTetVertices::AddSolid(const G4VSolid& solid) {
1342   if (fpPVModel->GetCurrentDepth() == fpMesh->GetMeshDepth()) {  // Leaf-level cells only
1343     // Need to know it's a tet !!!! or implement G4VSceneHandler::AddSolid (const G4Tet&) !!!!
1344     try {
1345       const auto& tet = dynamic_cast<const G4Tet&>(solid);
1346       const auto& material = fpPVModel->GetCurrentLV()->GetMaterial();
1347       const auto& name = material? material->GetName(): fpMesh->GetContainerVolume()->GetName();
1348       const auto& pVisAtts = fpPVModel->GetCurrentLV()->GetVisAttributes();
1349       // Transform into world coordinates if necessary
1350       if (fpCurrentObjectTransformation->xx() == 1. &&
1351           fpCurrentObjectTransformation->yy() == 1. &&
1352           fpCurrentObjectTransformation->zz() == 1.) { // No transformation necessary
1353         const auto& vertices = tet.GetVertices();
1354         fVerticesByMaterial.insert(std::make_pair(material,vertices));
1355       } else {
1356         auto vertices = tet.GetVertices();
1357         for (auto&& vertex: vertices) {
1358           vertex = G4Point3D(vertex).transform(*fpCurrentObjectTransformation);
1359         }
1360         fVerticesByMaterial.insert(std::make_pair(material,vertices));
1361       }
1362       if (fNameAndVisAttsByMaterial.find(material) == fNameAndVisAttsByMaterial.end())
1363         // Store name and vis attributes of first encounter with this material
1364         fNameAndVisAttsByMaterial[material] = NameAndVisAtts(name,*pVisAtts);
1365     }
1366     catch (const std::bad_cast&) {
1367       G4ExceptionDescription ed;
1368       ed << "Called for a mesh that is not a tetrahedron mesh: " << solid.GetName();
1369       G4Exception("PseudoSceneForTetVertices","visman0108",JustWarning,ed);
1370     }
1371   }
1372 }
1373 
1374 void G4VSceneHandler::StandardSpecialMeshRendering(const G4Mesh& mesh)
1375 // Standard way of special mesh rendering.
1376 // MySceneHandler::AddCompound(const G4Mesh& mesh) may use this if
1377 // appropriate or implement its own special mesh rendereing.
1378 {
1379   G4bool implemented = false;
1380   switch (mesh.GetMeshType()) {
1381     case G4Mesh::rectangle: [[fallthrough]];
1382     case G4Mesh::nested3DRectangular:
1383       switch (fpViewer->GetViewParameters().GetSpecialMeshRenderingOption()) {
1384         case G4ViewParameters::meshAsDefault:
1385           [[fallthrough]];
1386         case G4ViewParameters::meshAsDots:
1387           Draw3DRectMeshAsDots(mesh);  // Rectangular 3-deep mesh as dots
1388           implemented = true;
1389           break;
1390         case G4ViewParameters::meshAsSurfaces:
1391           Draw3DRectMeshAsSurfaces(mesh);  // Rectangular 3-deep mesh as surfaces
1392           implemented = true;
1393           break;
1394       }
1395       break;
1396     case G4Mesh::tetrahedron:
1397       switch (fpViewer->GetViewParameters().GetSpecialMeshRenderingOption()) {
1398         case G4ViewParameters::meshAsDefault:
1399           [[fallthrough]];
1400         case G4ViewParameters::meshAsDots:
1401           DrawTetMeshAsDots(mesh);  // Tetrahedron mesh as dots
1402           implemented = true;
1403           break;
1404         case G4ViewParameters::meshAsSurfaces:
1405           DrawTetMeshAsSurfaces(mesh);  // Tetrahedron mesh as surfaces
1406           implemented = true;
1407           break;
1408       }
1409       break;
1410     case G4Mesh::cylinder: [[fallthrough]];
1411     case G4Mesh::sphere: [[fallthrough]];
1412     case G4Mesh::invalid: break;
1413   }
1414   if (implemented) {
1415     // Draw container if not marked invisible...
1416     auto container = mesh.GetContainerVolume();
1417     auto containerLogical = container->GetLogicalVolume();
1418     auto containerVisAtts = containerLogical->GetVisAttributes();
1419     if (containerVisAtts == nullptr || containerVisAtts->IsVisible()) {
1420       auto solid = containerLogical->GetSolid();
1421       auto polyhedron = solid->GetPolyhedron();
1422       // Always draw as wireframe
1423       G4VisAttributes tmpVisAtts;
1424       if (containerVisAtts != nullptr) tmpVisAtts = *containerVisAtts;
1425       tmpVisAtts.SetForceWireframe();
1426       polyhedron->SetVisAttributes(tmpVisAtts);
1427       BeginPrimitives(mesh.GetTransform());
1428       AddPrimitive(*polyhedron);
1429       EndPrimitives();
1430     }
1431   } else {
1432     // Invoke base class function
1433     G4VSceneHandler::AddCompound(mesh);
1434   }
1435   return;
1436 }
1437 
1438 void G4VSceneHandler::Draw3DRectMeshAsDots(const G4Mesh& mesh)
1439 // For a rectangular 3-D mesh, draw as coloured dots by colour and material,
1440 // one dot randomly placed in each visible mesh cell.
1441 {
1442   // Check
1443   if (mesh.GetMeshType() != G4Mesh::rectangle &&
1444       mesh.GetMeshType() != G4Mesh::nested3DRectangular) {
1445     G4ExceptionDescription ed;
1446     ed << "Called with a mesh that is not rectangular:" << mesh;
1447     G4Exception("G4VSceneHandler::Draw3DRectMeshAsDots","visman0108",JustWarning,ed);
1448     return;
1449   }
1450 
1451   static G4bool firstPrint = true;
1452   const auto& verbosity = G4VisManager::GetVerbosity();
1453   G4bool print = firstPrint && verbosity >= G4VisManager::errors;
1454   if (print) {
1455     G4cout
1456     << "Special case drawing of 3D rectangular G4VNestedParameterisation as dots:"
1457     << '\n' << mesh
1458     << G4endl;
1459   }
1460 
1461   const auto& container = mesh.GetContainerVolume();
1462 
1463   // This map is static so that once filled it stays filled.
1464   static std::map<G4String,std::map<const G4Material*,G4Polymarker>> dotsByMaterialAndMesh;
1465   auto& dotsByMaterial = dotsByMaterialAndMesh[mesh.GetContainerVolume()->GetName()];
1466 
1467   // Fill map if not already filled
1468   if (dotsByMaterial.empty()) {
1469 
1470     // Get positions and material one cell at a time (using PseudoSceneFor3DRectMeshPositions).
1471     // The pseudo scene allows a "private" descent into the parameterisation.
1472     // Instantiate a temporary G4PhysicalVolumeModel
1473     G4ModelingParameters tmpMP;
1474     tmpMP.SetCulling(true);  // This avoids drawing transparent...
1475     tmpMP.SetCullingInvisible(true);  // ... or invisble volumes.
1476     const G4bool useFullExtent = true;  // To avoid calculating the extent
1477     G4PhysicalVolumeModel tmpPVModel
1478     (container,
1479      G4PhysicalVolumeModel::UNLIMITED,
1480      G4Transform3D(),  // so that positions are in local coordinates
1481      &tmpMP,
1482      useFullExtent);
1483     // Accumulate information in temporary maps by material
1484     std::multimap<const G4Material*,const G4ThreeVector> positionByMaterial;
1485     std::map<const G4Material*,G4VSceneHandler::NameAndVisAtts> nameAndVisAttsByMaterial;
1486     // Instantiate the pseudo scene
1487     PseudoSceneFor3DRectMeshPositions pseudoScene
1488     (&tmpPVModel,&mesh,positionByMaterial,nameAndVisAttsByMaterial);
1489     // Make private descent into the parameterisation
1490     tmpPVModel.DescribeYourselfTo(pseudoScene);
1491     // Now we have a map of positions by material.
1492     // Also a map of name and colour by material.
1493 
1494     const auto& prms = mesh.GetThreeDRectParameters();
1495     const auto& halfX = prms.fHalfX;
1496     const auto& halfY = prms.fHalfY;
1497     const auto& halfZ = prms.fHalfZ;
1498 
1499     // Fill the permanent (static) map of dots by material
1500     G4int nDotsTotal = 0;
1501     for (const auto& entry: nameAndVisAttsByMaterial) {
1502       G4int nDots = 0;
1503       const auto& material = entry.first;
1504       const auto& nameAndVisAtts = nameAndVisAttsByMaterial[material];
1505       const auto& name = nameAndVisAtts.fName;
1506       const auto& visAtts = nameAndVisAtts.fVisAtts;
1507       G4Polymarker dots;
1508       dots.SetInfo(name);
1509       dots.SetVisAttributes(visAtts);
1510       dots.SetMarkerType(G4Polymarker::dots);
1511       dots.SetSize(G4VMarker::screen,1.);
1512       // Enter empty polymarker into the map
1513       dotsByMaterial[material] = dots;
1514       // Now fill it in situ
1515       auto& dotsInMap = dotsByMaterial[material];
1516       const auto& range = positionByMaterial.equal_range(material);
1517       for (auto posByMat = range.first; posByMat != range.second; ++posByMat) {
1518         dotsInMap.push_back(GetPointInBox(posByMat->second, halfX, halfY, halfZ));
1519         ++nDots;
1520       }
1521 
1522       if (print) {
1523         G4cout
1524         << std::setw(30) << std::left << name.substr(0,30) << std::right
1525         << ": " << std::setw(7) << nDots << " dots"
1526         << ": colour " << std::fixed << std::setprecision(2)
1527         << visAtts.GetColour() << std::defaultfloat
1528         << G4endl;
1529       }
1530 
1531       nDotsTotal += nDots;
1532     }
1533 
1534     if (print) {
1535       G4cout << "Total number of dots: " << nDotsTotal << G4endl;
1536     }
1537   }
1538 
1539   // Some subsequent expressions apply only to G4PhysicalVolumeModel
1540   auto pPVModel = dynamic_cast<G4PhysicalVolumeModel*>(fpModel);
1541 
1542   G4String parameterisationName;
1543   if (pPVModel) {
1544     parameterisationName = pPVModel->GetFullPVPath().back().GetPhysicalVolume()->GetName();
1545   }
1546 
1547   // Draw the dots by material
1548   // Ensure they are "hidden", i.e., use the z-buffer as non-marker primitives do
1549   auto keepVP = fpViewer->GetViewParameters();
1550   auto vp = fpViewer->GetViewParameters();
1551   vp.SetMarkerHidden();
1552   fpViewer->SetViewParameters(vp);
1553   // Now we transform to world coordinates
1554   BeginPrimitives (mesh.GetTransform());
1555   for (const auto& entry: dotsByMaterial) {
1556     const auto& dots = entry.second;
1557     // The current "leaf" node in the PVPath is the parameterisation. Here it has
1558     // been converted into polymarkers by material. So...temporarily...change
1559     // its name to that of the material (whose name has been stored in Info)
1560     // so that its appearance in the scene tree of, e.g., G4OpenGLQtViewer, has
1561     // an appropriate name and its visibility and colour may be changed.
1562     if (pPVModel) {
1563       const auto& fullPVPath = pPVModel->GetFullPVPath();
1564       auto leafPV = fullPVPath.back().GetPhysicalVolume();
1565       leafPV->SetName(dots.GetInfo());
1566     }
1567     // Add dots to the scene
1568     AddPrimitive(dots);
1569   }
1570   EndPrimitives ();
1571   // Restore view parameters
1572   fpViewer->SetViewParameters(keepVP);
1573   // Restore parameterisation name
1574   if (pPVModel) {
1575     pPVModel->GetFullPVPath().back().GetPhysicalVolume()->SetName(parameterisationName);
1576   }
1577 
1578   firstPrint = false;
1579   return;
1580 }
1581 
1582 void G4VSceneHandler::Draw3DRectMeshAsSurfaces(const G4Mesh& mesh)
1583 // For a rectangular 3-D mesh, draw as surfaces by colour and material
1584 // with inner shared faces removed.
1585 {
1586   // Check
1587   if (mesh.GetMeshType() != G4Mesh::rectangle &&
1588       mesh.GetMeshType() != G4Mesh::nested3DRectangular) {
1589     G4ExceptionDescription ed;
1590     ed << "Called with a mesh that is not rectangular:" << mesh;
1591     G4Exception("G4VSceneHandler::Draw3DRectMeshAsSurfaces","visman0108",JustWarning,ed);
1592     return;
1593   }
1594 
1595   static G4bool firstPrint = true;
1596   const auto& verbosity = G4VisManager::GetVerbosity();
1597   G4bool print = firstPrint && verbosity >= G4VisManager::errors;
1598   if (print) {
1599     G4cout
1600     << "Special case drawing of 3D rectangular G4VNestedParameterisation as surfaces:"
1601     << '\n' << mesh
1602     << G4endl;
1603   }
1604 
1605   const auto& container = mesh.GetContainerVolume();
1606 
1607   // This map is static so that once filled it stays filled.
1608   static std::map<G4String,std::map<const G4Material*,G4Polyhedron>> boxesByMaterialAndMesh;
1609   auto& boxesByMaterial = boxesByMaterialAndMesh[mesh.GetContainerVolume()->GetName()];
1610 
1611   // Fill map if not already filled
1612   if (boxesByMaterial.empty()) {
1613 
1614     // Get positions and material one cell at a time (using PseudoSceneFor3DRectMeshPositions).
1615     // The pseudo scene allows a "private" descent into the parameterisation.
1616     // Instantiate a temporary G4PhysicalVolumeModel
1617     G4ModelingParameters tmpMP;
1618     tmpMP.SetCulling(true);  // This avoids drawing transparent...
1619     tmpMP.SetCullingInvisible(true);  // ... or invisble volumes.
1620     const G4bool useFullExtent = true;  // To avoid calculating the extent
1621     G4PhysicalVolumeModel tmpPVModel
1622     (container,
1623      G4PhysicalVolumeModel::UNLIMITED,
1624      G4Transform3D(),  // so that positions are in local coordinates
1625      &tmpMP,
1626      useFullExtent);
1627     // Accumulate information in temporary maps by material
1628     std::multimap<const G4Material*,const G4ThreeVector> positionByMaterial;
1629     std::map<const G4Material*,G4VSceneHandler::NameAndVisAtts> nameAndVisAttsByMaterial;
1630     // Instantiate the pseudo scene
1631     PseudoSceneFor3DRectMeshPositions pseudoScene
1632     (&tmpPVModel,&mesh,positionByMaterial,nameAndVisAttsByMaterial);
1633     // Make private descent into the parameterisation
1634     tmpPVModel.DescribeYourselfTo(pseudoScene);
1635     // Now we have a map of positions by material.
1636     // Also a map of name and colour by material.
1637 
1638     const auto& prms = mesh.GetThreeDRectParameters();
1639     const auto& sizeX = 2.*prms.fHalfX;
1640     const auto& sizeY = 2.*prms.fHalfY;
1641     const auto& sizeZ = 2.*prms.fHalfZ;
1642 
1643     // Fill the permanent (static) map of boxes by material
1644     G4int nBoxesTotal = 0, nFacetsTotal = 0;
1645     for (const auto& entry: nameAndVisAttsByMaterial) {
1646       G4int nBoxes = 0;
1647       const auto& material = entry.first;
1648       const auto& nameAndVisAtts = nameAndVisAttsByMaterial[material];
1649       const auto& name = nameAndVisAtts.fName;
1650       const auto& visAtts = nameAndVisAtts.fVisAtts;
1651       // Transfer positions into a vector ready for creating polyhedral surface
1652       std::vector<G4ThreeVector> positionsForPolyhedron;
1653       const auto& range = positionByMaterial.equal_range(material);
1654       for (auto posByMat = range.first; posByMat != range.second; ++posByMat) {
1655         const auto& position = posByMat->second;
1656         positionsForPolyhedron.push_back(position);
1657         ++nBoxes;
1658       }
1659       // The polyhedron will be in local coordinates
1660       // Add an empty place-holder to the map and get a reference to it
1661       auto& polyhedron = boxesByMaterial[material];
1662       // Replace with the desired polyhedron (uses efficient "move assignment")
1663       polyhedron = G4PolyhedronBoxMesh(sizeX,sizeY,sizeZ,positionsForPolyhedron);
1664       polyhedron.SetVisAttributes(visAtts);
1665       polyhedron.SetInfo(name);
1666 
1667       if (print) {
1668         G4cout
1669         << std::setw(30) << std::left << name.substr(0,30) << std::right
1670         << ": " << std::setw(7) << nBoxes << " boxes"
1671         << " (" << std::setw(7) << 6*nBoxes << " faces)"
1672         << ": reduced to " << std::setw(7) << polyhedron.GetNoFacets() << " facets ("
1673         << std::setw(2) << std::fixed << std::setprecision(2) << 100*polyhedron.GetNoFacets()/(6*nBoxes)
1674         << "%): colour " << std::fixed << std::setprecision(2)
1675         << visAtts.GetColour() << std::defaultfloat
1676         << G4endl;
1677       }
1678 
1679       nBoxesTotal += nBoxes;
1680       nFacetsTotal += polyhedron.GetNoFacets();
1681     }
1682 
1683     if (print) {
1684       G4cout << "Total number of boxes: " << nBoxesTotal << " (" << 6*nBoxesTotal << " faces)"
1685       << ": reduced to " << nFacetsTotal << " facets ("
1686       << std::setw(2) << std::fixed << std::setprecision(2) << 100*nFacetsTotal/(6*nBoxesTotal) << "%)"
1687       << G4endl;
1688     }
1689   }
1690 
1691   // Some subsequent expressions apply only to G4PhysicalVolumeModel
1692   auto pPVModel = dynamic_cast<G4PhysicalVolumeModel*>(fpModel);
1693 
1694   G4String parameterisationName;
1695   if (pPVModel) {
1696     parameterisationName = pPVModel->GetFullPVPath().back().GetPhysicalVolume()->GetName();
1697   }
1698 
1699   // Draw the boxes by material
1700   // Now we transform to world coordinates
1701   BeginPrimitives (mesh.GetTransform());
1702   for (const auto& entry: boxesByMaterial) {
1703     const auto& poly = entry.second;
1704     // The current "leaf" node in the PVPath is the parameterisation. Here it has
1705     // been converted into polyhedra by material. So...temporarily...change
1706     // its name to that of the material (whose name has been stored in Info)
1707     // so that its appearance in the scene tree of, e.g., G4OpenGLQtViewer, has
1708     // an appropriate name and its visibility and colour may be changed.
1709     if (pPVModel) {
1710       const auto& fullPVPath = pPVModel->GetFullPVPath();
1711       auto leafPV = fullPVPath.back().GetPhysicalVolume();
1712       leafPV->SetName(poly.GetInfo());
1713     }
1714     AddPrimitive(poly);
1715   }
1716   EndPrimitives ();
1717   // Restore parameterisation name
1718   if (pPVModel) {
1719     pPVModel->GetFullPVPath().back().GetPhysicalVolume()->SetName(parameterisationName);
1720   }
1721 
1722   firstPrint = false;
1723   return;
1724 }
1725 
1726 void G4VSceneHandler::DrawTetMeshAsDots(const G4Mesh& mesh)
1727 // For a tetrahedron mesh, draw as coloured dots by colour and material,
1728 // one dot randomly placed in each visible mesh cell.
1729 {
1730   // Check
1731   if (mesh.GetMeshType() != G4Mesh::tetrahedron) {
1732     G4ExceptionDescription ed;
1733     ed << "Called with mesh that is not a tetrahedron mesh:" << mesh;
1734     G4Exception("G4VSceneHandler::DrawTetMeshAsDots","visman0108",JustWarning,ed);
1735     return;
1736   }
1737 
1738   static G4bool firstPrint = true;
1739   const auto& verbosity = G4VisManager::GetVerbosity();
1740   G4bool print = firstPrint && verbosity >= G4VisManager::errors;
1741 
1742   if (print) {
1743     G4cout
1744     << "Special case drawing of tetrahedron mesh as dots"
1745     << '\n' << mesh
1746     << G4endl;
1747   }
1748 
1749   const auto& container = mesh.GetContainerVolume();
1750 
1751   // This map is static so that once filled it stays filled.
1752   static std::map<G4String,std::map<const G4Material*,G4Polymarker>> dotsByMaterialAndMesh;
1753   auto& dotsByMaterial = dotsByMaterialAndMesh[mesh.GetContainerVolume()->GetName()];
1754 
1755   // Fill map if not already filled
1756   if (dotsByMaterial.empty()) {
1757 
1758     // Get vertices and colour one cell at a time (using PseudoSceneForTetVertices).
1759     // The pseudo scene allows a "private" descent into the parameterisation.
1760     // Instantiate a temporary G4PhysicalVolumeModel
1761     G4ModelingParameters tmpMP;
1762     tmpMP.SetCulling(true);  // This avoids drawing transparent...
1763     tmpMP.SetCullingInvisible(true);  // ... or invisble volumes.
1764     const G4bool useFullExtent = true;  // To avoid calculating the extent
1765     G4PhysicalVolumeModel tmpPVModel
1766     (container,
1767      G4PhysicalVolumeModel::UNLIMITED,
1768      G4Transform3D(),  // so that positions are in local coordinates
1769      &tmpMP,
1770      useFullExtent);
1771     // Accumulate information in temporary maps by material
1772     std::multimap<const G4Material*,std::vector<G4ThreeVector>> verticesByMaterial;
1773     std::map<const G4Material*,G4VSceneHandler::NameAndVisAtts> nameAndVisAttsByMaterial;
1774     // Instantiate a pseudo scene
1775     PseudoSceneForTetVertices pseudoScene
1776     (&tmpPVModel,&mesh,verticesByMaterial,nameAndVisAttsByMaterial);
1777     // Make private descent into the parameterisation
1778     tmpPVModel.DescribeYourselfTo(pseudoScene);
1779     // Now we have a map of vertices by material.
1780     // Also a map of name and colour by material.
1781 
1782     // Fill the permanent (static) map of dots by material
1783     G4int nDotsTotal = 0;
1784     for (const auto& entry: nameAndVisAttsByMaterial) {
1785       G4int nDots = 0;
1786       const auto& material = entry.first;
1787       const auto& nameAndVisAtts = nameAndVisAttsByMaterial[material];
1788       const auto& name = nameAndVisAtts.fName;
1789       const auto& visAtts = nameAndVisAtts.fVisAtts;
1790       G4Polymarker dots;
1791       dots.SetVisAttributes(visAtts);
1792       dots.SetMarkerType(G4Polymarker::dots);
1793       dots.SetSize(G4VMarker::screen,1.);
1794       dots.SetInfo(name);
1795       // Enter empty polymarker into the map
1796       dotsByMaterial[material] = dots;
1797       // Now fill it in situ
1798       auto& dotsInMap = dotsByMaterial[material];
1799       const auto& range = verticesByMaterial.equal_range(material);
1800       for (auto vByMat = range.first; vByMat != range.second; ++vByMat) {
1801         dotsInMap.push_back(GetPointInTet(vByMat->second));
1802         ++nDots;
1803       }
1804 
1805       if (print) {
1806         G4cout
1807         << std::setw(30) << std::left << name.substr(0,30) << std::right
1808         << ": " << std::setw(7) << nDots << " dots"
1809         << ": colour " << std::fixed << std::setprecision(2)
1810         << visAtts.GetColour() << std::defaultfloat
1811         << G4endl;
1812       }
1813 
1814       nDotsTotal += nDots;
1815     }
1816 
1817     if (print) {
1818       G4cout << "Total number of dots: " << nDotsTotal << G4endl;
1819     }
1820   }
1821 
1822   // Some subsequent expressions apply only to G4PhysicalVolumeModel
1823   auto pPVModel = dynamic_cast<G4PhysicalVolumeModel*>(fpModel);
1824 
1825   G4String parameterisationName;
1826   if (pPVModel) {
1827     parameterisationName = pPVModel->GetFullPVPath().back().GetPhysicalVolume()->GetName();
1828   }
1829 
1830   // Draw the dots by material
1831   // Ensure they are "hidden", i.e., use the z-buffer as non-marker primitives do
1832   auto keepVP = fpViewer->GetViewParameters();
1833   auto vp = fpViewer->GetViewParameters();
1834   vp.SetMarkerHidden();
1835   fpViewer->SetViewParameters(vp);
1836 
1837   // Now we transform to world coordinates
1838   BeginPrimitives (mesh.GetTransform());
1839   for (const auto& entry: dotsByMaterial) {
1840     const auto& dots = entry.second;
1841     // The current "leaf" node in the PVPath is the parameterisation. Here it has
1842     // been converted into polymarkers by material. So...temporarily...change
1843     // its name to that of the material (whose name has been stored in Info)
1844     // so that its appearance in the scene tree of, e.g., G4OpenGLQtViewer, has
1845     // an appropriate name and its visibility and colour may be changed.
1846     if (pPVModel) {
1847       const auto& fullPVPath = pPVModel->GetFullPVPath();
1848       auto leafPV = fullPVPath.back().GetPhysicalVolume();
1849       leafPV->SetName(dots.GetInfo());
1850     }
1851     AddPrimitive(dots);
1852   }
1853   EndPrimitives ();
1854 
1855   // Restore view parameters
1856   fpViewer->SetViewParameters(keepVP);
1857   // Restore parameterisation name
1858   if (pPVModel) {
1859     pPVModel->GetFullPVPath().back().GetPhysicalVolume()->SetName(parameterisationName);
1860   }
1861 
1862   firstPrint = false;
1863   return;
1864 }
1865 
1866 void G4VSceneHandler::DrawTetMeshAsSurfaces(const G4Mesh& mesh)
1867 // For a tetrahedron mesh, draw as surfaces by colour and material
1868 // with inner shared faces removed.
1869 {
1870   // Check
1871   if (mesh.GetMeshType() != G4Mesh::tetrahedron) {
1872     G4ExceptionDescription ed;
1873     ed << "Called with mesh that is not a tetrahedron mesh:" << mesh;
1874     G4Exception("G4VSceneHandler::DrawTetMeshAsSurfaces","visman0108",JustWarning,ed);
1875     return;
1876   }
1877 
1878   static G4bool firstPrint = true;
1879   const auto& verbosity = G4VisManager::GetVerbosity();
1880   G4bool print = firstPrint && verbosity >= G4VisManager::errors;
1881 
1882   if (print) {
1883     G4cout
1884     << "Special case drawing of tetrahedron mesh as surfaces"
1885     << '\n' << mesh
1886     << G4endl;
1887   }
1888 
1889   // This map is static so that once filled it stays filled.
1890   static std::map<G4String,std::map<const G4Material*,G4Polyhedron>> surfacesByMaterialAndMesh;
1891   auto& surfacesByMaterial = surfacesByMaterialAndMesh[mesh.GetContainerVolume()->GetName()];
1892 
1893   // Fill map if not already filled
1894   if (surfacesByMaterial.empty()) {
1895 
1896     // Get vertices and colour one cell at a time (using PseudoSceneForTetVertices).
1897     // The pseudo scene allows a "private" descent into the parameterisation.
1898     // Instantiate a temporary G4PhysicalVolumeModel
1899     G4ModelingParameters tmpMP;
1900     tmpMP.SetCulling(true);  // This avoids drawing transparent...
1901     tmpMP.SetCullingInvisible(true);  // ... or invisble volumes.
1902     const G4bool useFullExtent = true;  // To avoid calculating the extent
1903     G4PhysicalVolumeModel tmpPVModel
1904     (mesh.GetContainerVolume(),
1905      G4PhysicalVolumeModel::UNLIMITED,
1906      G4Transform3D(),  // so that positions are in local coordinates
1907      &tmpMP,
1908      useFullExtent);
1909     // Accumulate information in temporary maps by material
1910     std::multimap<const G4Material*,std::vector<G4ThreeVector>> verticesByMaterial;
1911     std::map<const G4Material*,G4VSceneHandler::NameAndVisAtts> nameAndVisAttsByMaterial;
1912     // Instantiate a pseudo scene
1913     PseudoSceneForTetVertices pseudoScene
1914     (&tmpPVModel,&mesh,verticesByMaterial,nameAndVisAttsByMaterial);
1915     // Make private descent into the parameterisation
1916     tmpPVModel.DescribeYourselfTo(pseudoScene);
1917     // Now we have a map of vertices by material.
1918     // Also a map of name and colour by material.
1919 
1920     // Fill the permanent (static) map of surfaces by material
1921     G4int nTetsTotal = 0, nFacetsTotal = 0;
1922     for (const auto& entry: nameAndVisAttsByMaterial) {
1923       G4int nTets = 0;
1924       const auto& material = entry.first;
1925       const auto& nameAndVisAtts = nameAndVisAttsByMaterial[material];
1926       const auto& name = nameAndVisAtts.fName;
1927       const auto& visAtts = nameAndVisAtts.fVisAtts;
1928       // Transfer vertices into a vector ready for creating polyhedral surface
1929       std::vector<G4ThreeVector> verticesForPolyhedron;
1930       const auto& range = verticesByMaterial.equal_range(material);
1931       for (auto vByMat = range.first; vByMat != range.second; ++vByMat) {
1932         const std::vector<G4ThreeVector>& vertices = vByMat->second;
1933         for (const auto& vertex: vertices)
1934           verticesForPolyhedron.push_back(vertex);
1935         ++nTets;
1936       }
1937       // The polyhedron will be in local coordinates
1938       // Add an empty place-holder to the map and get a reference to it
1939       auto& polyhedron = surfacesByMaterial[material];
1940       // Replace with the desired polyhedron (uses efficient "move assignment")
1941       polyhedron = G4PolyhedronTetMesh(verticesForPolyhedron);
1942       polyhedron.SetVisAttributes(visAtts);
1943       polyhedron.SetInfo(name);
1944 
1945       if (print) {
1946         G4cout
1947         << std::setw(30) << std::left << name.substr(0,30) << std::right
1948         << ": " << std::setw(7) << nTets << " tetrahedra"
1949         << " (" << std::setw(7) << 4*nTets << " faces)"
1950         << ": reduced to " << std::setw(7) << polyhedron.GetNoFacets() << " facets ("
1951         << std::setw(2) << std::fixed << std::setprecision(2) << 100*polyhedron.GetNoFacets()/(4*nTets)
1952         << "%): colour " << std::fixed << std::setprecision(2)
1953         << visAtts.GetColour() << std::defaultfloat
1954         << G4endl;
1955      }
1956 
1957       nTetsTotal += nTets;
1958       nFacetsTotal += polyhedron.GetNoFacets();
1959     }
1960 
1961     if (print) {
1962       G4cout << "Total number of tetrahedra: " << nTetsTotal << " (" << 4*nTetsTotal << " faces)"
1963       << ": reduced to " << nFacetsTotal << " facets ("
1964       << std::setw(2) << std::fixed << std::setprecision(2) << 100*nFacetsTotal/(4*nTetsTotal) << "%)"
1965       << G4endl;
1966     }
1967   }
1968 
1969   // Some subsequent expressions apply only to G4PhysicalVolumeModel
1970   auto pPVModel = dynamic_cast<G4PhysicalVolumeModel*>(fpModel);
1971 
1972   G4String parameterisationName;
1973   if (pPVModel) {
1974     parameterisationName = pPVModel->GetFullPVPath().back().GetPhysicalVolume()->GetName();
1975   }
1976 
1977   // Draw the surfaces by material
1978   // Now we transform to world coordinates
1979   BeginPrimitives (mesh.GetTransform());
1980   for (const auto& entry: surfacesByMaterial) {
1981     const auto& poly = entry.second;
1982     // The current "leaf" node in the PVPath is the parameterisation. Here it has
1983     // been converted into polyhedra by material. So...temporarily...change
1984     // its name to that of the material (whose name has been stored in Info)
1985     // so that its appearance in the scene tree of, e.g., G4OpenGLQtViewer, has
1986     // an appropriate name and its visibility and colour may be changed.
1987     if (pPVModel) {
1988       const auto& fullPVPath = pPVModel->GetFullPVPath();
1989       auto leafPV = fullPVPath.back().GetPhysicalVolume();
1990       leafPV->SetName(poly.GetInfo());
1991     }
1992     AddPrimitive(poly);
1993   }
1994   EndPrimitives ();
1995 
1996   // Restore parameterisation name
1997   if (pPVModel) {
1998     pPVModel->GetFullPVPath().back().GetPhysicalVolume()->SetName(parameterisationName);
1999   }
2000 
2001   firstPrint = false;
2002   return;
2003 }
2004 
2005 G4ThreeVector
2006 G4VSceneHandler::GetPointInBox(const G4ThreeVector& pos,
2007                                G4double halfX,
2008                                G4double halfY,
2009                                G4double halfZ) const
2010 {
2011   G4double x = pos.getX() + (2.*G4QuickRand() - 1.)*halfX;
2012   G4double y = pos.getY() + (2.*G4QuickRand() - 1.)*halfY;
2013   G4double z = pos.getZ() + (2.*G4QuickRand() - 1.)*halfZ;
2014   return G4ThreeVector(x, y, z);
2015 }
2016 
2017 G4ThreeVector
2018 G4VSceneHandler::GetPointInTet(const std::vector<G4ThreeVector>& vertices) const
2019 {
2020   G4double p = G4QuickRand();
2021   G4double q = G4QuickRand();
2022   G4double r = G4QuickRand();
2023   if (p + q > 1.)
2024   {
2025     p = 1. - p;
2026     q = 1. - q;
2027   }
2028   if (q + r > 1.)
2029   {
2030     G4double tmp = r;
2031     r = 1. - p - q;
2032     q = 1. - tmp;
2033   }
2034   else if (p + q + r > 1.)
2035   {
2036     G4double tmp = r;
2037     r = p + q + r - 1.;
2038     p = 1. - q - tmp;
2039   }
2040   G4double a = 1. - p - q - r;
2041   return vertices[0]*a + vertices[1]*p + vertices[2]*q + vertices[3]*r;
2042 }
2043