1

I'm currently using Irrlicht Engine to implement the QuickHull algorithm in 3D. The user will be able to step through the algorithm at a keypress, moving through each individual step.

I'd like to do this independent of the main render loop so as to not bring the entire simulation to a halt while waiting for user input; the user should be able to pan the camera around and see what's going on.

I currently have a QuickHull container class with all of the points and line segments and a method for the algorithm. How do I get this method to pause on demand and wait for user input, without stopping the render loop or accidentally breaking the recursion as the render loop moves on?

Dan Fairaizl
  • 2,172
  • 2
  • 28
  • 31
Nerve
  • 317
  • 2
  • 8

1 Answers1

1

The following is not the best solution memory-wise, but could work:

When the user runs QuickHull, actually run through the entire solution. HOWEVER, save intermediate states as you go. In the worst case, QHull needs O(n) memory if all the points lie on the convex hull (i.e. a convex polygon) and runs in O(n2) time, so if you're generating intermediate states, you'll need to store vectors of size 1, 2, 3, ..., n to represent the hull at each stage, which could result in total extra memory of O(n3).

As you generate the hull incrementally, create a new set of points for each iteration that represent what the hull looks like at that moment in time. To represent the convex hull that is generated, add sphere scene nodes for each point on the hull as you go.

You can add a sphere to the scene with the irr::scene::ISceneManager::addSphereSceneNode(radius) function, which returns an IMeshSceneNode* that you can push into a vector representing the convex hull at that state.

To show edges between those points, you need to draw lines between your spheres. So, for each point in the current hull, draw a line between them (also a line between last and first)

You can draw a line with the draw3DLine command in irr::video::IVideoDriver like such:

irr::video::SMaterial lineMaterial;
lineMaterial.Lighting = false;
lineMaterial.Thickness = 2.0f;
lineMaterial.FrontfaceCulling = false;
lineMaterial.BackfaceCulling = false;
lineMaterial.MaterialType = irr::video::EMT_SOLID;
_driver->setMaterial(lineMaterial);
_driver->setTransform(irr::video::ETS_WORLD,
                irr::core::IdentityMatrix);

//draw a line
irr::core::vector3df lineStart,lineEnd; //set these equal to 2 adjacent vertices on your hull
_driver->draw3DLine(lineStart,lineEnd,irr::video::SColor(0.0,0.0,0.0,205)); //blue line

Initially, all the IMeshSceneNode* that you have added to the scene should be invisible. Use the command void irr::scene::ISceneNode::setVisible(bool isVisible) on each sphere in your vectors as you create them.

Now, after you've run the QHull algorithm, you can create an index into your vector of states (where each state represents the hull at a timestep, which itself is a vector).

When you increment the index, you can set all the spheres in the current state (before incrementing) to invisible, and then make all the spheres in the next state visible.

Your main render loop should also be calling some function that renders the edges in the current ConvexHull state.

The nice thing about this model is that the user can now go forwards AND backwards to see how the algorithm progressed. If you're only ever going forward, then instead of making the previous nodes representing the hull invisible, you may remove them from the scene entirely.

Also with this model, we don't need to try suspending the QHull algorithm in the middle of execution using some weird threaded model, and we don't need to modify the algorithm itself a great deal such that it can pause and resume. You simply render the current Hull state each frame, allowing you to move the camera around and look at it as you will, and then just update the hull with a button press using your own EventReceiver class.

AndyG
  • 39,700
  • 8
  • 109
  • 143
  • Fantastic explanation. I thought about storing a scene state for each step for reverse stepping, but I thought that would add far too much to the complexity of the project; however, if I can easily define exactly how many data structures I'll have at any given point, I can just save them all in a vector of structs containing them. I also did momentarily terrify myself thinking that threading might be necessary, so thanks for addressing that. I'll let you know how implementation goes! – Nerve Nov 05 '13 at 20:45