Though still not perfect (there are more problems to solve) I got rid of the vibrations be dividing the updating into two steps as was suggested. I'd agree with that the vibrations were due to multiple re-updates.
So I first simply project the 3D to screen coordinates:
void IconRep::update()
{
// Note that we have to invert the Z-axis of the source Ship's
// look vector since it uses left-handed coordinates while
// OGRE uses right-handed.
//
Ogre::Vector3 pos = *reinterpret_cast<ogre::Vector3 const *>( &rShip.world.pos() );
float x, y;
ogre::GetScreenCoordinates( pos, x, y );
x = -HALF_SIZE + ogre::pWindow->getWidth()/2 - x;
y = -(ICON_SIZE+HALF_SIZE) + ogre::pWindow->getHeight()/2 - y;
x = std::min<float>( x, ogre::pWindow->getWidth()-ICON_SIZE ); x = std::max<float>( 0, x );
y = std::min<float>( y, ogre::pWindow->getHeight()-ICON_SIZE ); y = std::max<float>( 0, y );
pIcon->setPosition( CEGUI::UVector2(CEGUI::UDim(0, x), CEGUI::UDim(0, y)) );
}
I then moved the unbunching into its own method. DO NOT COPY THIS, it is WIP and experimental, and very buggy!!!
void IconRep::alignIcons()
{
using namespace Ogre;
float x = pIcon->getXPosition().d_offset,
y = pIcon->getYPosition().d_offset;
float penetr = 0.f;
Vector2 dir = Vector2::ZERO;
unsigned start = 0;
for (ICONREP_VEC::iterator i = rOtherIcons.begin(); i != rOtherIcons.end(); ++i, ++start)
if (i->get() == this)
break;
struct Shift
{
CEGUI::Window *pIcon;
float p;
Vector2 dir;
};
std::vector<Shift> adjustments;
for (ICONREP_VEC::iterator i = rOtherIcons.begin() + start+1; i < rOtherIcons.end(); ++i)
{
CEGUI::UVector2 gv;
Vector2 ov = Vector2::ZERO;
CEGUI::UVector2 p1 = pIcon->getPosition(),
p2 = (**i).pIcon->getPosition();
if (p1.d_x == p2.d_x) p1.d_x.d_offset += 5;
if (p1.d_y == p2.d_y) p1.d_y.d_offset += 5;
gv = (p1 - p2);
ov.x = gv.d_x.d_offset, ov.y = gv.d_y.d_offset;
float len = ov.length();
if (len < ICON_SIZE && len > 0)
{
penetr = std::max<float>(penetr, ICON_SIZE-len),
dir += ov;
Shift s;
s.pIcon = (**i).pIcon;
s.dir = ov.normalisedCopy();
s.p = len;
adjustments.push_back( s );
}
}
dir.normalise();
dir *= penetr;
x += dir.x;
y += dir.y;
pIcon->setPosition( CEGUI::UVector2(CEGUI::UDim(0, x), CEGUI::UDim(0, y)) );
for (std::vector<Shift>::iterator i = adjustments.begin(); i != adjustments.end(); ++i)
{
Vector2 nv( i->pIcon->getXPosition().d_offset, y = i->pIcon->getYPosition().d_offset );
nv -= i->dir * i->p;
i->pIcon->setPosition( CEGUI::UVector2(CEGUI::UDim(0, nv.x), CEGUI::UDim(0, nv.y)) );
}
}
Finally, everything is updated outside of these methods:
void Session::updateRepresentations()
{
for (SHIPREP_VEC::iterator i = ship_reps.begin(); i != ship_reps.end(); ++i)
(*i)->update();
for (ICONREP_VEC::iterator i = icons_reps.begin(); i != icons_reps.end(); ++i)
if ((*i)->active)
(*i)->update();
for (ICONREP_VEC::iterator i = icons_reps.begin(); i != icons_reps.end(); ++i)
if ((*i)->active)
(*i)->alignIcons();
}
This is much more efficient and solves the vibrations. Still, icons can shove each other out of the screen, and the mutual shoving I'm implementing (it looks much better than one icon being completely solid and the other doing all the dodging) is extremely rudimentary and the icons do not align nicely alongside sides and in corners when all in a bunch. Anyway, it is a good start, I hope...