Revision: 62650
Initial Code
Initial URL
Initial Description
Initial Title
Initial Tags
Initial Language
at March 5, 2013 07:02 by xterminhate
Initial Code
// Xterm-in'Hate
//#ifdef _IRR_COMPILE_WITH_GUI_
#include "CGUIDynamicGrid.h"
#include "IVideoDriver.h"
namespace irr
{
namespace gui
{
//! constructor
CGUIDynamicGrid::CGUIDynamicGrid(IGUIEnvironment* environment, IGUIElement* parent)
: IGUIDynamicGrid( environment, parent ), isJointVisible( false ), Behavior(EGDGB_RESET)
{
#ifdef _DEBUG
setDebugName("CGUIDynamicGrid");
#endif
}
//! destructor
CGUIDynamicGrid::~CGUIDynamicGrid()
{
reset();
}
void CGUIDynamicGrid::setJointVisible(bool visible)
{
isJointVisible = visible;
}
void CGUIDynamicGrid::setBehavior(EGUI_DYNAMIC_GRID_BEHAVIOR behavior)
{
switch(Behavior)
{
case EGDGB_RESET:
{
if( behavior != EGDGB_RESET )
{
// init bones and jonts based on child GUI elements
init();
// then change behavior
Behavior = behavior;
}
}
break;
case EGDGB_ATTACH_RUN_ONCE:
{
if( behavior == EGDGB_RESET )
reset();
// change behavior
tryToAttachAll();
Behavior = behavior;
}
break;
case EGDGB_ATTACH_FOCUSED:
{
if( behavior == EGDGB_RESET )
reset();
// change behavior
Behavior = behavior;
}
break;
case EGDGB_RUN:
{
if( behavior == EGDGB_RESET )
reset();
// change behavior
Behavior = behavior;
}
break;
case EGDGB_DETACH_FOCUSED:
{
if( behavior == EGDGB_RESET )
reset();
// change behavior
Behavior = behavior;
}
break;
case EGDGB_DETACH_ALL:
{
if( behavior == EGDGB_RESET )
reset();
// change behavior
Behavior = behavior;
}
break;
}
}
EGUI_DYNAMIC_GRID_BEHAVIOR CGUIDynamicGrid::getBehavior() const
{
return Behavior;
}
void CGUIDynamicGrid::draw()
{
// update the size of the grid element (all screen)
const core::rect<s32>& rect = Parent->getAbsolutePosition();
DesiredRect = core::rect<s32>(0,0,rect.getWidth(),rect.getHeight());
recalculateAbsolutePosition(true);
// apply dynamic grid algorithm
snap(); // find a better place not to decrease FPS
if (!IsVisible)
return;
if (isJointVisible)
{
video::IVideoDriver * driver = Environment->getVideoDriver();
IGUISkin * skin = Environment->getSkin();
core::list<bone*>::ConstIterator itb = Bones.begin();
for (; itb != Bones.end(); ++itb)
{
for (size_t index = 0; index != joint::EJT_MAX; ++index)
{
const video::SColor color(255,0,255,0);
const core::rect<s32> drawrect( rect.UpperLeftCorner + (*itb)->getJoint(index)->getPosition() - core::position2di(4,4),
rect.UpperLeftCorner + (*itb)->getJoint(index)->getPosition() + core::position2di(4,4) );
if ( (*itb)->getJoint(index)->isAttached() )
{
driver->draw2DRectangle(color, drawrect);
}
else
{
driver->draw2DRectangleOutline(drawrect,skin->getColor(EGDC_3D_SHADOW));
}
}
}
}
IGUIElement::draw();
}
void CGUIDynamicGrid::init()
{
reset();
// create a bone per child element
core::list<IGUIElement*>::ConstIterator ite = Children.begin();
for (; ite != Children.end(); ++ite)
{
Bones.push_back( new bone( (*ite) ) );
}
}
void CGUIDynamicGrid::reset()
{
// delete all bones
core::list<bone*>::Iterator itb = Bones.begin();
for (; itb != Bones.end(); ++itb)
{
delete (*itb);
}
Bones.clear();
}
void CGUIDynamicGrid::snap()
{
static u32 counter = 1; ///< 1 : because every bones and joints are initialzed with a null counter.
switch(Behavior)
{
case EGDGB_RESET:
{
}
break;
case EGDGB_ATTACH_RUN_ONCE:
{
tryToAttachAll();
Behavior = EGDGB_RUN;
}
break;
case EGDGB_ATTACH_FOCUSED:
{
IGUIElement* focused = Environment->getFocus();
if ( focused )
{
bone* b;
if ( hasBone(focused,b) )
{
b->update(counter);
tryToAttach( b );
++counter;
}
}
}
break;
case EGDGB_RUN:
{
IGUIElement* focused = Environment->getFocus();
if ( focused )
{
bone* b;
if ( hasBone(focused,b) )
{
b->update(counter);
++counter;
}
}
}
break;
case EGDGB_DETACH_FOCUSED:
{
IGUIElement* focused = Environment->getFocus();
if ( focused )
{
bone* b;
if ( hasBone(focused,b) )
{
b->update(counter);
b->dettach();
++counter;
}
}
}
break;
case EGDGB_DETACH_ALL:
{
dettachAll();
Behavior = EGDGB_RUN;
}
break;
}
}
void CGUIDynamicGrid::tryToAttach(bone* b)
{
core::list<bone*>::ConstIterator itb = Bones.begin();
for (; itb != Bones.end(); ++itb)
{
// don't attach to itself
if ( (*itb) == b || (*itb)->isAttachedToBone(b) )
continue;
// a pair of joints...
size_t j1, j2;
if (b->canAttachToBone((*itb),j1,j2 ))
{
if( ! b->isRectCollidedBody( (*itb)))
{
b->attachToBone((*itb),j1,j2);
b->consolidate();
}
}
}
}
void CGUIDynamicGrid::tryToAttachAll()
{
core::list<bone*>::ConstIterator itb = Bones.begin();
for (; itb != Bones.end(); ++itb)
{
tryToAttach( (*itb) );
}
}
void CGUIDynamicGrid::dettachAll()
{
core::list<bone*>::ConstIterator itb = Bones.begin();
for (; itb != Bones.end(); ++itb)
{
(*itb)->dettach();
}
}
CGUIDynamicGrid::bone* CGUIDynamicGrid::getBone(IGUIElement* element)
{
core::list<bone*>::ConstIterator itb = Bones.begin();
for (; itb != Bones.end(); ++itb)
{
if ( (*itb)->own(element) )
return (*itb);
}
return 0;
}
bool CGUIDynamicGrid::hasBone(const IGUIElement* element, bone*& b) const
{
core::list<bone*>::ConstIterator itb = Bones.begin();
for (; itb != Bones.end(); ++itb)
{
if ( (*itb)->own(element) )
{
b = (*itb);
return true;
}
}
return false;
}
/// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CGUIDynamicGrid::bone::bone(IGUIElement* element) :
Element(element), Counter(0)
{
// create 8 joints per element (two at each corner)
const core::rect<s32>& rect = Element->getRelativePosition();
for (size_t index = 0; index != joint::EJT_MAX; ++index)
{
Joints[index] = new joint( (joint::EJOINT_TYPE)index, this, rect );
}
}
CGUIDynamicGrid::bone::~bone()
{
// delete all joints
for (size_t index = 0; index != joint::EJT_MAX; ++index)
{
delete Joints[index];
}
}
CGUIDynamicGrid::joint* CGUIDynamicGrid::bone::getJoint(size_t index) const
{
return Joints[index];
}
bool CGUIDynamicGrid::bone::own(const IGUIElement* element) const
{
return Element == element;
}
bool CGUIDynamicGrid::bone::canAttachToBone(const bone* b, size_t& j1, size_t& j2) const
{
for (size_t index1=0; index1 != joint::EJT_MAX; ++index1)
{
if ( ! Joints[index1]->isAttached() )
{
for (size_t index2 = 0; index2 != joint::EJT_MAX; ++index2)
{
if (Joints[index1]->canAttachToJoint(b->getJoint(index2)))
{
j1 = index1;
j2 = index2;
return true;
}
}
}
};
return false;
}
void CGUIDynamicGrid::bone::attachToBone(bone* b, size_t j1, size_t j2)
{
Joints[j1]->attachToJoint(b->getJoint(j2));
}
void CGUIDynamicGrid::bone::dettach()
{
for (size_t index = 0; index != joint::EJT_MAX; ++index)
{
Joints[index]->dettach();
}
}
bool CGUIDynamicGrid::bone::isAttachedToBone(const bone* b) const
{
for (size_t index = 0; index != joint::EJT_MAX; ++index)
{
if (Joints[index]->isAttachedToBone(b))
return true;
}
return false;
}
// I have to do it recursively
bool CGUIDynamicGrid::bone::isRectCollidedBodyRecursive(const CGUIDynamicGrid::bone* b, const core::rect<s32>& rect, core::list<const bone*>& body_bones_processed)
{
// check current bone
if ( b->getElement()->getRelativePosition().isRectCollided( rect ) || b->getElement()->getRelativePosition() == rect )
return true;
// dont process twice this bone
body_bones_processed.push_back(b);
// process every attached bone, except those already processed
for (size_t index = 0; index != joint::EJT_MAX; ++index)
{
if( b->getJoint(index)->isAttached() )
{
const bone* next = b->getJoint(index)->getAttachedBone();
if( core::find( body_bones_processed.begin(), body_bones_processed.end(), next) == body_bones_processed.end() )
{
if ( isRectCollidedBodyRecursive( next, rect, body_bones_processed ) )
{
return true;
}
}
}
}
return false;
}
void CGUIDynamicGrid::bone::listBonesRecursive(const bone* b,core::list<const bone*>& body_bones)
{
if( core::find( body_bones.begin(), body_bones.end(), b) == body_bones.end() )
{
body_bones.push_back(b);
for (size_t index = 0; index != joint::EJT_MAX; ++index)
{
if( b->getJoint(index)->isAttached() )
{
const bone* next = b->getJoint(index)->getAttachedBone();
listBonesRecursive(next,body_bones);
}
}
}
}
void CGUIDynamicGrid::bone::listBonesRecursive(bone* b,core::list<bone*>& body_bones)
{
if( core::find( body_bones.begin(), body_bones.end(), b) == body_bones.end() )
{
body_bones.push_back(b);
for (size_t index = 0; index != joint::EJT_MAX; ++index)
{
if( b->getJoint(index)->isAttached() )
{
bone* next = b->getJoint(index)->getAttachedBone();
listBonesRecursive(next,body_bones);
}
}
}
}
bool CGUIDynamicGrid::bone::isRectCollidedBody(const bone* b) const
{
core::list<const bone*> body_bones;
listBonesRecursive(this,body_bones);
core::list<const bone*>::ConstIterator itb = body_bones.begin();
for (; itb != body_bones.end(); ++itb)
{
core::rect<s32> rect = (*itb)->getElement()->getRelativePosition();
core::list<const bone*> body_bones_processed;
if( (*itb)->isRectCollidedBodyRecursive(b,rect,body_bones_processed) )
return true; // exit at once if collision detected
}
return false; // no collision
}
///! Attach together every bones of the body identified by THIS bone, by as many joints as possible
void CGUIDynamicGrid::bone::consolidate()
{
/// \todo try to attach together each bone of one body, starting from this bone
core::list<bone*> body_bones;
listBonesRecursive(this,body_bones);
core::list<bone*>::Iterator itb1 = body_bones.begin();
for (; itb1 != body_bones.end(); ++itb1)
{
core::list<bone*>::Iterator itb2 = itb1+1;
for (; itb2 != body_bones.end(); ++itb2)
{
// don't attach to itself
if ( (*itb1) == (*itb2) )
continue;
// a pair of joints...
(*itb2)->attachToBoneByAllPossibleJoints((*itb1));
}
}
}
///! Attach bones THIS and B with as many joint as possible
void CGUIDynamicGrid::bone::attachToBoneByAllPossibleJoints(bone* b)
{
for (size_t index1 = 0; index1 != joint::EJT_MAX; ++index1)
{
if ( ! Joints[index1]->isAttached() )
{
for (size_t index2 = 0; index2 != joint::EJT_MAX; ++index2)
{
if( Joints[index1]->canAttachToJoint(b->getJoint(index2) ) )
{
Joints[index1]->attachToJoint(b->getJoint(index2));
}
}
}
}
}
const IGUIElement* CGUIDynamicGrid::bone::getElement() const
{
return Element;
}
void CGUIDynamicGrid::bone::update(u32 counter, const core::position2di& dist)
{
if (Counter != counter)
{
Counter = counter;
Element->move( dist );
const core::rect<s32>& rect = Element->getRelativePosition();
for (size_t index = 0; index != joint::EJT_MAX; ++index )
{
Joints[index]->update(counter,rect);
};
}
}
/// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
CGUIDynamicGrid::joint::joint(EJOINT_TYPE type, bone* parent_bone, const core::rect<s32>& rectangle) :
Type( type ), ParentBone(parent_bone), AttachedJoint(0), Counter(0)
{
switch(Type)
{
case EJT_UPPER_RIGHT_TO_TOP:
case EJT_UPPER_RIGHT_TO_RIGHT:
Position = core::position2di( rectangle.LowerRightCorner.X, rectangle.UpperLeftCorner.Y );
break;
case EJT_LOWER_RIGHT_TO_RIGHT:
case EJT_LOWER_RIGHT_TO_BOTTOM:
Position = rectangle.LowerRightCorner;
break;
case EJT_LOWER_LEFT_TO_BOTTOM:
case EJT_LOWER_LEFT_TO_LEFT:
Position = core::position2di( rectangle.UpperLeftCorner.X, rectangle.LowerRightCorner.Y );
break;
case EJT_UPPER_LEFT_TO_LEFT:
case EJT_UPPER_LEFT_TO_TOP:
Position = rectangle.UpperLeftCorner;
break;
}
}
CGUIDynamicGrid::joint::~joint()
{
// detach joint
if ( AttachedJoint != 0 )
AttachedJoint->AttachedJoint = 0;
}
const core::position2di & CGUIDynamicGrid::joint::getPosition() const
{
return Position;
}
CGUIDynamicGrid::joint::EJOINT_TYPE CGUIDynamicGrid::joint::getType() const
{
return Type;
}
bool CGUIDynamicGrid::joint::isAttached() const
{
return AttachedJoint != 0;
}
const bool joint_compatibility[8][8] =
{
{ false, false, false, true, false, false, false, false }, // EJT_UPPER_RIGHT_TO_TOP
{ false, false, false, false, false, false, true, false }, // EJT_UPPER_RIGHT_TO_RIGHT
{ false, false, false, false, false, true, false, false }, // EJT_LOWER_RIGHT_TO_RIGHT
{ true, false, false, false, false, false, false, false }, // EJT_LOWER_RIGHT_TO_BOTTOM
{ false, false, false, false, false, false, false, true }, // EJT_LOWER_LEFT_TO_BOTTOM
{ false, false, true, false, false, false, false, false }, // EJT_LOWER_LEFT_TO_LEFT
{ false, true, false, false, false, false, false, false }, // EJT_UPPER_LEFT_TO_LEFT
{ false, false, false, false, true, false, false, false } // EJT_UPPER_LEFT_TO_TOP
};
bool CGUIDynamicGrid::joint::canAttachToJoint(const joint* j) const
{
return joint_compatibility[Type][j->getType()] && ( Position.getDistanceFrom( j->getPosition() ) < 4 ); ///< Distance between two 'attachable' joint
}
void CGUIDynamicGrid::joint::attachToJoint(joint* j)
{
AttachedJoint = j;
j->AttachedJoint = this;
}
void CGUIDynamicGrid::joint::dettach()
{
if( isAttached() )
{
// mutually detach joints
AttachedJoint->AttachedJoint = 0;
AttachedJoint = 0;
}
}
bool CGUIDynamicGrid::joint::isAttachedToBone(const bone* b) const
{
if( !isAttached() )
return false;
return AttachedJoint->ParentBone == b;
}
CGUIDynamicGrid::bone* CGUIDynamicGrid::joint::getAttachedBone() const
{
return AttachedJoint->ParentBone;
}
void CGUIDynamicGrid::joint::update(u32 counter, const core::rect<s32>& rectangle)
{
if ( Counter != counter )
{
Counter = counter;
switch(Type)
{
case EJT_UPPER_RIGHT_TO_TOP:
case EJT_UPPER_RIGHT_TO_RIGHT:
Position = core::position2di( rectangle.LowerRightCorner.X, rectangle.UpperLeftCorner.Y );
break;
case EJT_LOWER_RIGHT_TO_RIGHT:
case EJT_LOWER_RIGHT_TO_BOTTOM:
Position = rectangle.LowerRightCorner;
break;
case EJT_LOWER_LEFT_TO_BOTTOM:
case EJT_LOWER_LEFT_TO_LEFT:
Position = core::position2di( rectangle.UpperLeftCorner.X, rectangle.LowerRightCorner.Y );
break;
case EJT_UPPER_LEFT_TO_LEFT:
case EJT_UPPER_LEFT_TO_TOP:
Position = rectangle.UpperLeftCorner;
break;
}
if ( AttachedJoint != 0 )
{
AttachedJoint->dragTo(counter,Position);
}
}
}
void CGUIDynamicGrid::joint::dragTo(u32 counter, const core::position2di& position)
{
if (Counter != counter)
{
Counter = counter;
const core::vector2di dist(position-Position);
Position = position;
ParentBone->update(counter,dist);
}
}
// xterminahte > if you have understood this code until this point, please, phone me and tell me how it is possible this is working not too bad ? ;-)
} // end namespace gui
} // end namespace irr
//#endif // _IRR_COMPILE_WITH_GUI_
Initial URL
http://irrlicht.sourceforge.net/forum/viewtopic.php?f=9&t=48291&p=278332#p278332
Initial Description
http://irrlicht.sourceforge.net/forum/viewtopic.php?f=9&t=48291&p=278332#p278332
Initial Title
CGUIDynamicGrid.cpp
Initial Tags
Initial Language
C++