利用mfc子窗口进行二维碰撞检测



我想用MFC在C++中做一个布局管理器。这个布局将包含一个窗口矩阵。布局初始形式将由矩阵选择器定义(类似于MS Word)。之后,用户可以通过拉动窗口的边缘来调整每个窗口的大小。我正在考虑使用2d碰撞检测算法,但我不知道这将如何在实时调整大小(或如何使用mfc)上执行

我想对每个元素施加一个最小大小,并在违反此限制的情况下阻止元素收缩(当另一个元素正在扩展时)。正在等待任何建议

我不得不在C++和MFC中做类似的布局管理器。我实现了一个包含矩形的N元树(称为CDisplayObject),树中的每个叶子都可以有一个CFrame。为了构建这样一个树,需要执行一个简单的文本解析。每个矩形都在0和1之间,并且每个矩形都有大小之和为1的子对象。

我不调整窗口的大小,但它可能很容易实现,因为你只需要调整CDisplayObject的大小,然后再调整窗口的尺寸。

这是处理显示对象的代码,这些显示对象根据"显示"矩形调整大小,在我们的程序中,矩形是MainView。

创建一个左侧有一个大矩形,右侧有三个小矩形的"显示树":

CDisplayObject* tmp;
CString szCurrentName = "Big + three"
tmp = new CDisplayObject(szCurrentName);
tmp->Add(szCurrentName + "|Column1",0.75);
tmp->Add(szCurrentName + "|Column2",0.25);
tmp->Add(szCurrentName + "|Column2-Row1",0.3333333);
tmp->Add(szCurrentName + "|Column2-Row2",0.3333333);
tmp->Add(szCurrentName + "|Column2-Row3",0.3333333);

"|"表示它是一列,"-"表示这是一行。

使用SwitchFrameWnd将叶子设置为包含子窗口,并使用SetPos(CRect)调整树中所有显示对象的大小。您可以通过实现一个函数来增加窗口的大小,该函数通过树来更改行/列/叶的权重。

#pragma once
#ifndef __AFXWIN_H__
#error include 'stdafx.h' before including this file for PCH
#endif
class CDisplayObject : public CObject
{
friend class CMainView;
typedef std::list<CDisplayObject*> Childrens;
public:
typedef std::list<CDisplayObject*> Leafs;
typedef enum 
{
LEAF,
ROW,
COLUMN
} ITEM_TYPE;
CDisplayObject(LPCTSTR name,CDisplayObject* Parent = NULL, CoordinateType weight = 1.0);
virtual             ~CDisplayObject();
bool                HasViewOrFrame(const CWnd* wnd) const;              
void                TRACE_IT();
void                Draw(CDC* pDC, const CRect& drawRect,BOOL isIconMode);
CView*          GetView() const;
void                Add(LPCSTR layoutItemDesc, CoordinateType weigth);
//returns all the leafs in the tree
virtual Leafs       GetLeafs() ;
//this removes the framewnd from any other object in same layout-tree 
//and sets the view in this object 
virtual BOOL        SwitchFrameWnd(CFrameWnd* view);
CFrameWnd*          GetFrameWnd() const                 {return m_pFrameWnd;}
void                SetPos(const RECT& rect);
CString             GetName() const             { return m_Name;}
CString             GetUniqueName() const {return m_UUID;}
//returns the complete name for this leaf i.e: main|column-row
CString             GetCompleteName() const;
CRect               GetDisplayRect() const      { return m_DisplayRect; }
CDisplayObject*     GetFirstEmptyLeaf();
CDisplayObject*     GetFirstEmptyLeafAfterLastFrame();
//removes the wnd in the subtree
void                RemoveWnd(CFrameWnd* view);
//calls updateSlaveWindows for each view
void                UpdateSlaveWindows();
//returns the CDisplayObject that has the wnd as framewnd or view
//can return null!
CDisplayObject*     GetObjectWithWnd(const CWnd* wnd) ;
CDisplayObject*     GetLeafViewFromPoint(const CPoint& point);
protected:
CDisplayObject(){};
BOOL                IsLeaf() const          { return m_type == LEAF && m_Childrens.size() == 0;}    
CRect               CalculateChildrenRect(CDisplayObject* children, const CRect& mainRect,Vector2d &topLeft);
void                SetDisplayRect(CRect val) ;
CFrameWnd*          GetLastView() const         { return m_pLastView; }
void                HideViews();
void                ShowViews();
CDisplayObject::ITEM_TYPE           GetType() const { return m_type; }
private:
static CString      translateString(LPCTSTR str);
static CString      GetPrefix( LPCSTR str);
static CString      GetSuffix( LPCTSTR str );
static ITEM_TYPE    GetDividerType( LPCTSTR str );
void                RepositionView();
void                SetWeight( CoordinateType weight );
CoordinateType      GetWeight() const;
CoordinateType      GetChildrenWeight() const;
CDisplayObject*     GetRootObject();

CFrameWnd           *m_pFrameWnd;
//used for distributing documents correctly (=last place used) when changing layouts
CFrameWnd           *m_pLastView;
Childrens           m_Childrens;
CoordinateType      m_Weight;
CString             m_Name;
CString             m_UUID;
ITEM_TYPE           m_type;
//this is the client rect for this window
CRect               m_DisplayRect;
//the parent for this CDisplayObject. if m_parent == NULL => root-object
CDisplayObject*     m_Parent;
//used for draw the name of the object
#ifdef _DEBUG
CFont *m_Font;
#endif // _DEBUG
DECLARE_DYNCREATE(CDisplayObject)
};

cpp实现

#include "stdafx.h"
#include "DisplayObject.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
IMPLEMENT_DYNCREATE(CDisplayObject, CObject )
//////////////////////////////////////////////////////////////////////////
///CDisplayObject
//////////////////////////////////////////////////////////////////////////
CDisplayObject::CDisplayObject(LPCTSTR name, CDisplayObject* Parent /*= NULL*/, CoordinateType weight /*= 1.0*/)
:CObject()
{
m_UUID = CreateUUID();
m_Name = GetPrefix(name);
m_type = LEAF;
ASSERT(!m_Name.IsEmpty());
if( !GetSuffix(name).IsEmpty() )
{
this->Add(name, weight);
}
else
{
m_Weight = weight;
}
m_Parent = Parent;
m_pFrameWnd = NULL;
m_pLastView = NULL;
CViewSettings viewSettings;
viewSettings = AfxGetProperties()->m_ViewSettings;
m_DisplayRect = CRect(0,0,0,0);
#ifdef _DEBUG
m_Font = new CFont;     
m_Font->CreateFont( 
12, // nHeight
0,                      // nWidth
0,                      // nEscapement
0,                      // nOrientation
FW_NORMAL,              // nWeight,
FALSE,                  // bItalic
FALSE,                  // bUnderline
FALSE,                  // cStrikeOut
DEFAULT_CHARSET,        // CharSet
OUT_CHARACTER_PRECIS,
CLIP_DEFAULT_PRECIS,
PROOF_QUALITY,
FF_DONTCARE,
"Arial" );
#endif // _DEBUG
}

CDisplayObject::~CDisplayObject()
{
// TRACE_LINE;
for(Childrens::iterator it = m_Childrens.begin(); it != m_Childrens.end(); ++it)
delete *it;
m_Childrens.clear();
#ifdef _DEBUG
if( m_Font )
{
m_Font->DeleteObject();
SAFE_DELETE(m_Font);
}
#endif // _DEBUG
}

void CDisplayObject::Add( LPCSTR layoutItemDesc, CoordinateType weigth )
{
CString prefix = GetPrefix(layoutItemDesc);
CString suffix = GetSuffix(layoutItemDesc);
if(prefix == m_Name )
{
if(!suffix.IsEmpty())
{
m_type = (m_type == LEAF) ? GetDividerType(layoutItemDesc) : m_type;
CString prefixSuffix = GetPrefix(suffix);
CDisplayObject* tmp = NULL;
if( !prefixSuffix.IsEmpty() )
{
for(Childrens::iterator it = m_Childrens.begin(); it != m_Childrens.end(); ++it)
{
if( (*it)->GetName() == prefixSuffix )
{
tmp = *it;
break;
}
}
if( tmp == NULL)
{
tmp = new CDisplayObject(prefixSuffix, this);
m_Childrens.push_back(tmp);
}
}
tmp->Add(suffix, weigth);
}
else
{
SetWeight(weigth);
}
}
}
void CDisplayObject::TRACE_IT()
{
TRACE( m_Name );
TRACE( " %.2f", m_Weight );
if( m_Childrens.size() > 0)
{
TRACE(" ( ");
CString divider;
for(Childrens::const_iterator it = m_Childrens.begin(); it != m_Childrens.end(); ++it)
{
TRACE(divider);
(*it)->TRACE_IT();
divider = (m_type == ROW) ? " - " : " | ";
}
TRACE( " ) ");
}
}

CString CDisplayObject::translateString( LPCTSTR str )
{
CString retValue = str;
retValue.MakeLower();
retValue.Trim();
return str;
}
CString CDisplayObject::GetPrefix( LPCSTR str )
{
CString tmpStr = translateString(str);
int index = tmpStr.FindOneOf("|-");
if( index == -1 )
{
return tmpStr;
}
else
{
return tmpStr.Left(index);
}
}
CString CDisplayObject::GetSuffix( LPCTSTR str )
{
CString tmpStr = translateString(str);
int index = tmpStr.FindOneOf("|-");
if( index == -1 )
{
return "";
}
else
{
return tmpStr.Mid(index + 1);
}
}
CDisplayObject::ITEM_TYPE CDisplayObject::GetDividerType( LPCTSTR str )
{
CString tmpStr = translateString(str);
int index = tmpStr.Find("-");
if(index != -1) //horizontal
{
return ROW;
}
index = tmpStr.Find("|");
if(index != -1) //vertical
{
return COLUMN;
}
return LEAF;
}

CoordinateType CDisplayObject::GetWeight() const 
{ 
return m_Weight;
}
CoordinateType CDisplayObject::GetChildrenWeight() const 
{ 
CoordinateType sum = 0;
for(Childrens::const_iterator it = m_Childrens.begin(); it != m_Childrens.end(); ++it)
{
sum += (*it)->GetWeight( );
}
return sum;
}
static bool IsRectEqual(const CRect &obj1, const CRect &obj2)
{
return obj1.left == obj2.left && obj1.right == obj2.right && obj1.top == obj2.top && obj1.bottom == obj2.bottom;
}
void CDisplayObject::SetWeight( CoordinateType weight )
{
m_Weight = weight;
}
void CDisplayObject::SetPos(const RECT& rect)
{
SetDisplayRect(rect);
if(IsLeaf())
{
SetDisplayRect(rect);
}
else
{
Vector2d topLeft;
for(Childrens::iterator it = m_Childrens.begin(); it != m_Childrens.end(); ++it)
{
CRect newDrawRect = CalculateChildrenRect(*it,rect,topLeft);
(*it)->SetPos(newDrawRect);
}
}
}

CRect CDisplayObject::CalculateChildrenRect( CDisplayObject* children, const CRect& mainRect, Vector2d& topLeft )
{
ASSERT(m_Childrens.size() > 0);
ASSERT(m_type == COLUMN || m_type == ROW);
CRect drawRectangle;
if(m_type == COLUMN)
{
CoordinateType weight = children->GetWeight();
Vector2d BottomRight(topLeft.X() + weight, 1.0);
CLayoutRectangle childrenRectangle(topLeft, BottomRight);
drawRectangle = childrenRectangle.ToCRect(mainRect);
topLeft = childrenRectangle.TopRight();
}
else if( m_type == ROW)
{
CoordinateType weight = children->GetWeight();
Vector2d BottomRight(1.0,topLeft.Y() + weight);
CLayoutRectangle childrenRectangle(topLeft, BottomRight);
drawRectangle = childrenRectangle.ToCRect(mainRect);
topLeft = childrenRectangle.BottomLeft();
}
return drawRectangle;
}
void CDisplayObject::Draw( CDC* pDC, const CRect& drawRect,BOOL isIconMode )
{
if(IsLeaf())
{
if(!isIconMode)
{
SetDisplayRect(drawRect);
}
pDC->FillRect(drawRect,&CBrush( m_ViewSettings.m_BackgroundColor ) );
/*
#ifdef _DEBUG
if( !isIconMode )
{
pDC->SetTextAlign( TA_CENTER );
CFont* pOldFont = pDC->SelectObject( m_Font );
pDC->SetBkMode( TRANSPARENT );
pDC->SetTextColor( RGB( 255, 255, 255 ) );
pDC->TextOut(drawRect.left + drawRect.Width() / 2,
drawRect.top + drawRect.Height() / 2, 
GetCompleteName() );
pDC->SelectObject( pOldFont);   
}
#endif // _DEBUG
*/
CPen FramePen(PS_SOLID, m_ViewSettings.m_FrameWidth, m_ViewSettings.m_FrameColor);
CPen* oldPen;
oldPen = pDC->SelectObject(&FramePen);
CLayoutRectangle(drawRect).DoPaint(pDC);
pDC->SelectObject(oldPen);
}
else
{   
Vector2d topLeft;
for(Childrens::iterator it = m_Childrens.begin(); it != m_Childrens.end(); ++it)
{
CRect newDrawRect = CalculateChildrenRect(*it,drawRect,topLeft);
(*it)->Draw(pDC,newDrawRect,isIconMode);
}
}
}

CDisplayObject* CDisplayObject::GetFirstEmptyLeaf() 
{
if(IsLeaf() && m_pFrameWnd == NULL)
{
return this;
}
else
{
for(Childrens::const_iterator it = m_Childrens.begin(); it != m_Childrens.end(); ++it)
{
CDisplayObject* tmpDisplayObject = (*it)->GetFirstEmptyLeaf();
if( tmpDisplayObject != NULL )
{
return tmpDisplayObject;
}
}
}
return NULL;
}

CDisplayObject* CDisplayObject::GetFirstEmptyLeafAfterLastFrame()
{
Leafs leafs = GetLeafs();
Leafs::reverse_iterator it = leafs.rbegin(); 
while(1)
{
Leafs::reverse_iterator tmpIt = it;
if((*tmpIt)->GetFrameWnd() != NULL)
{
return NULL;
}
it++;
if(it != leafs.rend())
{
if( (*it)->GetFrameWnd() != NULL)
return *tmpIt;
}
else
{
//no frameWnd present
return (*leafs.begin());
}
}
return NULL;
}

CDisplayObject* CDisplayObject::GetLeafViewFromPoint( const CPoint& point ) 
{
if(IsLeaf() && m_DisplayRect.PtInRect(point))
{
return this;
}
else
{
for(Childrens::const_iterator it = m_Childrens.begin(); it != m_Childrens.end(); ++it)
{
CDisplayObject* tmpDisplayObject = (*it)->GetLeafViewFromPoint( point );
if( tmpDisplayObject != NULL )
{
return tmpDisplayObject;
}
}
}
return NULL;
}
BOOL CDisplayObject::SwitchFrameWnd( CFrameWnd* View )
{
//walk through whole tree and remove the view
GetRootObject()->RemoveWnd(View);
if(!IsLeaf())
return FALSE;
if(m_pFrameWnd && ::IsWindow(m_pFrameWnd->GetSafeHwnd()))
RemoveWindow(m_pFrameWnd); //hide the window
m_pLastView = m_pFrameWnd;
m_pFrameWnd = View;
if(m_pFrameWnd)
{
RepositionView();
if(m_pFrameWnd && ::IsWindow(m_pFrameWnd->GetSafeHwnd()))
AddWindow(m_pFrameWnd);
if(m_pFrameWnd && ::IsWindow(m_pFrameWnd->GetSafeHwnd()))
{
if(m_pFrameWnd->GetActiveView())
{
CImageView* pImageView = (CImageView*)m_pFrameWnd->GetActiveView();
pImageView->ResetOrigoPanPos();
pImageView->ResetPanPos(FALSE);
pImageView->UpdateShownImage();
}
m_pFrameWnd->Invalidate();
m_pFrameWnd->RedrawWindow();
}
}
return TRUE;
}

CDisplayObject::Leafs CDisplayObject::GetLeafs( ) 
{
Leafs retValue;
if(IsLeaf())
{
retValue.push_back(this);
}
else
{
for(Childrens::const_iterator it = m_Childrens.begin(); it != m_Childrens.end(); ++it)
{
Leafs tmpLeafs = (*it)->GetLeafs( );
retValue.merge(tmpLeafs);
}
}
return retValue;
}
void CDisplayObject::SetDisplayRect( CRect val )
{
if( IsRectEqual( val , m_DisplayRect) )
return;
//TRACE_LINE;
int frameWidth = m_ViewSettings.m_FrameWidth;
m_DisplayRect = val; 
//deflate the rect for accomodate the own frame.
if(IsLeaf())
m_DisplayRect.DeflateRect(frameWidth,frameWidth,frameWidth,frameWidth);
//m_DisplayRect.OffsetRect(CPoint(0,-100));
RepositionView();
}
void CDisplayObject::RepositionView()
{
if(m_pFrameWnd && ::IsWindow(m_pFrameWnd->GetSafeHwnd()))
{
m_pFrameWnd->SetWindowPos(NULL,m_DisplayRect.left,m_DisplayRect.top,m_DisplayRect.Width(),m_DisplayRect.Height(),SWP_NOZORDER);
}
}
void CDisplayObject::HideViews()
{
if(GetFrameWnd() && ::IsWindow(GetFrameWnd()->GetSafeHwnd()))
RemoveWindow(GetFrameWnd());
if(!IsLeaf())
{
for(Childrens::iterator it = m_Childrens.begin(); it != m_Childrens.end(); ++it)
{
(*it)->HideViews( );
}
}
}
void CDisplayObject::ShowViews()
{
if(GetFrameWnd() && ::IsWindow(GetFrameWnd()->GetSafeHwnd()))
AddWindow(GetFrameWnd());
if(!IsLeaf())
{
for(Childrens::iterator it = m_Childrens.begin(); it != m_Childrens.end(); ++it)
{
(*it)->ShowViews( );
}
}
}
void CDisplayObject::RemoveWnd( CFrameWnd* view )
{
if(m_pFrameWnd == view)
{
m_pFrameWnd = NULL;
}
if(!IsLeaf())
{
for(Childrens::iterator it = m_Childrens.begin(); it != m_Childrens.end(); ++it)
{
(*it)->RemoveWnd( view );
}
}
}

CDisplayObject* CDisplayObject::GetRootObject()
{
//get the root display object
CDisplayObject* rootParent = m_Parent;
CDisplayObject* currentObject = this;
while(rootParent)
{
CDisplayObject* tmp = rootParent;
currentObject = rootParent;
rootParent = tmp->m_Parent;
}
ASSERT(currentObject);
return currentObject;
}
CView* CDisplayObject::GetView() const
{
if( m_pFrameWnd && m_pFrameWnd->IsKindOf(RUNTIME_CLASS(CImageFrameWnd)))
{
CView* view = m_pFrameWnd->GetActiveView();
if(view && view->IsKindOf(RUNTIME_CLASS(CImageView)))
{
return (CImageView*)view;
}
}
return NULL;
}

bool CDisplayObject::HasViewOrFrame( const CWnd* wnd ) const
{
if( this->m_pFrameWnd == wnd)
return true;
if(this->GetFrameWnd() && this->GetView() == wnd)
{
return true;
}
return false;
}
CDisplayObject* CDisplayObject::GetObjectWithWnd(const CWnd* wnd)  
{
if(this->HasViewOrFrame( wnd ))
{
return this;
}
if(!IsLeaf())
{
for(Childrens::iterator it = m_Childrens.begin(); it != m_Childrens.end(); ++it)
{
CDisplayObject* retValue;
if( (retValue = (*it)->GetObjectWithWnd( wnd )) != NULL )
{
return retValue;
}
}
}
return NULL;
}
CString CDisplayObject::GetCompleteName() const
{
CString name = GetName();
const CDisplayObject* rootParent = m_Parent;
const CDisplayObject* currentObject = this;
while(rootParent)
{
if( rootParent->GetType() == COLUMN )
{
name = rootParent->GetName() + CString("|") + name;
}
else if( rootParent->GetType() == ROW )
{
name = rootParent->GetName() + CString("-") + name;
}
else
{
name = rootParent->GetName() + CString(" ERROR! ") + name;
}
const CDisplayObject* tmp = rootParent;
currentObject = rootParent;
rootParent = tmp->m_Parent;
}
return name;
}

最新更新