496 lines
16 KiB
C++
496 lines
16 KiB
C++
#include <mesh/mesh.hpp>
|
|
#include <mesh/main.hpp>
|
|
#include <common/stdio.hpp>
|
|
#include <common/math.hpp>
|
|
#include <common/rect.hpp>
|
|
#include <common/purehdc.hpp>
|
|
|
|
char GridMesh::szClassName[]="MESH97A";
|
|
char GridMesh::szMenuName[]={'\0'};
|
|
|
|
GridMesh::GridMesh(HWND hParent,int width,int height,GridShow visibility)
|
|
: mhParent(hParent), mStatus(InActive), mGridLines(GridLines),
|
|
mIsSuspended(FALSE), mhInstance(Main::processInstance()),
|
|
mDrawingPen(RGBColor(0,0,0),1)
|
|
{
|
|
mCreateHandler.setCallback(this,&GridMesh::createHandler);
|
|
mPaintHandler.setCallback(this,&GridMesh::paintHandler);
|
|
mLeftButtonUpHandler.setCallback(this,&GridMesh::leftButtonUpHandler);
|
|
mLeftButtonDownHandler.setCallback(this,&GridMesh::leftButtonDownHandler);
|
|
mMouseMoveHandler.setCallback(this,&GridMesh::mouseMoveHandler);
|
|
mDestroyHandler.setCallback(this,&GridMesh::destroyHandler);
|
|
insertHandlers();
|
|
mVersionInfo=szClassName;
|
|
registerClass();
|
|
Rect winRect(CW_USEDEFAULT,CW_USEDEFAULT,width,height);
|
|
createWindow(WS_EX_TRANSPARENT,szClassName,String(),WS_CHILD|WS_CLIPSIBLINGS,winRect,mhParent,(HMENU)0,processInstance(),(LPSTR)(Window*)this);
|
|
if(GridMesh::Show!=visibility)return;
|
|
show(SW_SHOW);
|
|
update();
|
|
}
|
|
|
|
GridMesh::~GridMesh()
|
|
{
|
|
Window::destroy();
|
|
removeHandlers();
|
|
}
|
|
|
|
void GridMesh::insertHandlers(void)
|
|
{
|
|
insertHandler(VectorHandler::CreateHandler,&mCreateHandler);
|
|
insertHandler(VectorHandler::PaintHandler,&mPaintHandler);
|
|
insertHandler(VectorHandler::LeftButtonUpHandler,&mLeftButtonUpHandler);
|
|
insertHandler(VectorHandler::LeftButtonDownHandler,&mLeftButtonDownHandler);
|
|
insertHandler(VectorHandler::MouseMoveHandler,&mMouseMoveHandler);
|
|
insertHandler(VectorHandler::DestroyHandler,&mDestroyHandler);
|
|
}
|
|
|
|
void GridMesh::removeHandlers(void)
|
|
{
|
|
removeHandler(VectorHandler::CreateHandler,&mCreateHandler);
|
|
removeHandler(VectorHandler::PaintHandler,&mPaintHandler);
|
|
removeHandler(VectorHandler::LeftButtonUpHandler,&mLeftButtonUpHandler);
|
|
removeHandler(VectorHandler::LeftButtonDownHandler,&mLeftButtonDownHandler);
|
|
removeHandler(VectorHandler::MouseMoveHandler,&mMouseMoveHandler);
|
|
removeHandler(VectorHandler::DestroyHandler,&mDestroyHandler);
|
|
}
|
|
|
|
void GridMesh::registerClass(void)
|
|
{
|
|
WNDCLASS wndClass;
|
|
|
|
if(::GetClassInfo(mhInstance,szClassName,(WNDCLASS FAR *)&wndClass))return;
|
|
wndClass.style =CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS;
|
|
wndClass.lpfnWndProc =(WNDPROC)Window::WndProc;
|
|
wndClass.cbClsExtra =0;
|
|
wndClass.cbWndExtra =sizeof(GridMesh*);
|
|
wndClass.hInstance =mhInstance;
|
|
wndClass.hIcon =0;
|
|
wndClass.hCursor =::LoadCursor(NULL,IDC_ARROW);
|
|
wndClass.hbrBackground =(HBRUSH)::GetStockObject(NULL_BRUSH);;
|
|
wndClass.lpszMenuName =szMenuName;
|
|
wndClass.lpszClassName =szClassName;
|
|
::RegisterClass(&wndClass);
|
|
}
|
|
|
|
CallbackData::ReturnType GridMesh::createHandler(CallbackData &/*someCallbackData*/)
|
|
{
|
|
sendMessage(WM_NCACTIVATE,TRUE,0L);
|
|
newMesh();
|
|
return (CallbackData::ReturnType)FALSE;
|
|
}
|
|
|
|
CallbackData::ReturnType GridMesh::paintHandler(CallbackData &someCallbackData)
|
|
{
|
|
int segmentCount((int)Array<Segment>::size());
|
|
PaintInformation *pPaintInfo=(PaintInformation*)someCallbackData.lParam();
|
|
PureDevice &pureDevice=*pPaintInfo;
|
|
for(int itemIndex=0;itemIndex<segmentCount;itemIndex++)
|
|
(Array<Segment>::operator[](itemIndex)).drawSegment((PureDevice&)*pPaintInfo,mDrawingPen);
|
|
return (CallbackData::ReturnType)FALSE;
|
|
}
|
|
|
|
CallbackData::ReturnType GridMesh::leftButtonUpHandler(CallbackData &someCallbackData)
|
|
{
|
|
mouseUp(someCallbackData.loWord(),someCallbackData.hiWord());
|
|
return (CallbackData::ReturnType)FALSE;
|
|
}
|
|
|
|
CallbackData::ReturnType GridMesh::leftButtonDownHandler(CallbackData &someCallbackData)
|
|
{
|
|
mouseDown(someCallbackData.loWord(),someCallbackData.hiWord());
|
|
return (CallbackData::ReturnType)FALSE;
|
|
}
|
|
|
|
CallbackData::ReturnType GridMesh::mouseMoveHandler(CallbackData &someCallbackData)
|
|
{
|
|
mouseMove(someCallbackData.loWord(),someCallbackData.hiWord());
|
|
return FALSE;
|
|
}
|
|
|
|
CallbackData::ReturnType GridMesh::destroyHandler(CallbackData &someCallbackData)
|
|
{
|
|
return (CallbackData::ReturnType)FALSE;
|
|
}
|
|
|
|
void GridMesh::showWindow(int visible)
|
|
{
|
|
show(visible?SW_SHOW:SW_HIDE);
|
|
invalidate();
|
|
}
|
|
|
|
void GridMesh::newMesh(WORD gridLines)
|
|
{
|
|
createMesh(gridLines,(Array<Segment>&)*this);
|
|
::InvalidateRect(mhParent,0,TRUE);
|
|
invalidate();
|
|
}
|
|
|
|
void GridMesh::createMesh(WORD gridLines,Array<Segment> &someSegmentVector)
|
|
{
|
|
RECT clientRect;
|
|
WORD xLength;
|
|
WORD yLength;
|
|
GDIPoint tempFirstPoint;
|
|
GDIPoint tempSecondPoint;
|
|
WORD vectorSize(0);
|
|
Index vectorIndex(0);
|
|
|
|
if(gridLines>MaxGridLines)mGridLines=GridLines;
|
|
else mGridLines=gridLines;
|
|
::GetClientRect(*this,(RECT FAR *)&clientRect);
|
|
xLength=clientRect.right/(mGridLines+1);
|
|
yLength=clientRect.bottom/(mGridLines+1);
|
|
for(int x=0,xCount=0;xCount<=mGridLines;x+=xLength,xCount++)
|
|
for(int y=0,yCount=0;yCount<=mGridLines;y+=yLength,yCount++)vectorSize+=2;
|
|
someSegmentVector.size(vectorSize);
|
|
for(x=0,xCount=0;xCount<=mGridLines;x+=xLength,xCount++)
|
|
{
|
|
for(int y=0,yCount=0;yCount<=mGridLines;y+=yLength,yCount++)
|
|
{
|
|
tempFirstPoint.setPoint(x+xLength,y+yLength);
|
|
tempSecondPoint.setPoint(x+xLength,y);
|
|
someSegmentVector[vectorIndex]=Segment(tempFirstPoint,tempSecondPoint,(WORD)vectorIndex);
|
|
vectorIndex++;
|
|
tempSecondPoint.setPoint(x,y+yLength);
|
|
someSegmentVector[vectorIndex]=Segment(tempFirstPoint,tempSecondPoint,(WORD)vectorIndex);
|
|
vectorIndex++;
|
|
}
|
|
}
|
|
}
|
|
|
|
void GridMesh::mouseDown(int x,int y)
|
|
{
|
|
GDIPoint mousePoint;
|
|
size_t size;
|
|
|
|
if(mIsSuspended)return;
|
|
mCurrentIntersection.remove();
|
|
mousePoint.setPoint(x,y);
|
|
if(!closestIntersection(mCurrentIntersection,mousePoint))
|
|
{
|
|
mStatus=InActive;
|
|
::MessageBeep(-1);
|
|
return;
|
|
}
|
|
mStatus=Active;
|
|
PureDevice displayDevice(*this);
|
|
size=(int)mCurrentIntersection.size();
|
|
for(int i=0;i<size;i++)mCurrentIntersection[i].drawSegment(displayDevice,mDrawingPen);
|
|
for(i=0;i<size;i++)mCurrentIntersection[i].drawSegment(displayDevice,mDrawingPen);
|
|
}
|
|
|
|
void GridMesh::mouseMove(int x,int y)
|
|
{
|
|
GDIPoint mousePoint;
|
|
Rect boundingRect;
|
|
size_t size;
|
|
int index;
|
|
|
|
if(InActive==mStatus)return;
|
|
PureDevice displayDevice(*this);
|
|
mousePoint.setPoint(x,y);
|
|
size=(int)mCurrentIntersection.size();
|
|
getBoundingRect(mCurrentIntersection,boundingRect);
|
|
if(!boundingRect.ptInRect(mousePoint))return;
|
|
for(index=0;index<size;index++)mCurrentIntersection[index].drawSegment(displayDevice,mDrawingPen);
|
|
for(index=0;index<size;index++)mCurrentIntersection[index].firstPoint(mousePoint);
|
|
for(index=0;index<size;index++)mCurrentIntersection[index].drawSegment(displayDevice,mDrawingPen);
|
|
}
|
|
|
|
void GridMesh::mouseUp(int x,int y)
|
|
{
|
|
Rect boundingRect;
|
|
GDIPoint swapPoint;
|
|
GDIPoint mousePoint;
|
|
size_t size;
|
|
int index;
|
|
|
|
if(InActive==mStatus)return;
|
|
mStatus=InActive;
|
|
mousePoint.setPoint(x,y);
|
|
size=(int)mCurrentIntersection.size();
|
|
for(index=0;index<size;index++)Array<Segment>::operator[](mCurrentIntersection[index].vectorIndex())=mCurrentIntersection[index];
|
|
}
|
|
|
|
WORD GridMesh::closestIntersection(Block<Segment> &filterBlock,GDIPoint &mousePoint)
|
|
{
|
|
size_t size((int)Array<Segment>::size());
|
|
DWORD tempDistance;
|
|
DWORD leastDistance;
|
|
Segment tempSegment;
|
|
Segment minSegment;
|
|
|
|
if(!size)return FALSE;
|
|
filterBlock.remove();
|
|
while(IntersectionSegments!=filterBlock.size())
|
|
{
|
|
for(int index=0;index<size;index++)
|
|
{
|
|
if(!index)
|
|
{
|
|
tempSegment=Array<Segment>::operator[](index);
|
|
while(isInBlock(filterBlock,tempSegment))tempSegment=Array<Segment>::operator[](++index);
|
|
leastDistance=minDistance(tempSegment,mousePoint);
|
|
}
|
|
else
|
|
{
|
|
tempDistance=minDistance(Array<Segment>::operator[](index),mousePoint);
|
|
if(tempDistance<leastDistance)
|
|
{
|
|
minSegment=Array<Segment>::operator[](index);
|
|
if(!isInBlock(filterBlock,minSegment))
|
|
{
|
|
tempSegment=minSegment;
|
|
leastDistance=tempDistance;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
filterBlock.insert(&tempSegment);
|
|
}
|
|
return orderIntersection(filterBlock);
|
|
}
|
|
|
|
WORD GridMesh::orderIntersection(Block<Segment> &intersection)
|
|
{
|
|
GDIPoint tempPoint;
|
|
GDIPoint swapPoint;
|
|
size_t size((int)intersection.size());
|
|
int firstSection(0);
|
|
int secondSection(0);
|
|
int index;
|
|
|
|
if(IntersectionSegments!=size)return FALSE;
|
|
tempPoint=intersection[0].firstPoint();
|
|
firstSection=(tempPoint==intersection[1].firstPoint());
|
|
if(!firstSection)firstSection=(tempPoint==intersection[1].secondPoint());
|
|
if(!firstSection)
|
|
{
|
|
tempPoint=intersection[0].secondPoint();
|
|
secondSection=tempPoint==intersection[1].firstPoint();
|
|
if(!secondSection)secondSection=tempPoint==intersection[1].secondPoint();
|
|
}
|
|
if(!firstSection && !secondSection)return FALSE;
|
|
for(index=1;index<size;index++)
|
|
{
|
|
if(!(tempPoint==intersection[index].firstPoint())&&
|
|
!(tempPoint==intersection[index].secondPoint()))return FALSE;
|
|
}
|
|
if(secondSection)
|
|
{
|
|
swapPoint=intersection[0].firstPoint();
|
|
intersection[0].firstPoint(intersection[0].secondPoint());
|
|
intersection[0].secondPoint(swapPoint);
|
|
}
|
|
for(index=1;index<size;index++)
|
|
{
|
|
if(!(tempPoint==intersection[index].firstPoint()))
|
|
{
|
|
intersection[index].secondPoint(intersection[index].firstPoint());
|
|
intersection[index].firstPoint(tempPoint);
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
WORD GridMesh::isInBlock(Block<Segment> &source,Segment &someSegment)
|
|
{
|
|
size_t size((int)source.size());
|
|
|
|
for(int i=0;i<size;i++)if(source[i]==someSegment)return TRUE;
|
|
return FALSE;
|
|
}
|
|
|
|
DWORD GridMesh::minDistance(const Segment &someSegment,const GDIPoint &somePoint)
|
|
{
|
|
LONG yTemp;
|
|
LONG xTemp;
|
|
LONG midy;
|
|
LONG midx;
|
|
LONG distancePointOne;
|
|
LONG distancePointTwo;
|
|
LONG distancePointThree;
|
|
|
|
yTemp=((LONG)someSegment.yFirst()-(LONG)somePoint.y())*((LONG)someSegment.yFirst()-(LONG)somePoint.y());
|
|
xTemp=((LONG)someSegment.xFirst()-(LONG)somePoint.x())*((LONG)someSegment.xFirst()-(LONG)somePoint.x());
|
|
distancePointOne=Math::sqrt(yTemp+xTemp);
|
|
yTemp=((LONG)someSegment.ySecond()-(LONG)somePoint.y())*((LONG)someSegment.ySecond()-(LONG)somePoint.y());
|
|
xTemp=((LONG)someSegment.xSecond()-(LONG)somePoint.x())*((LONG)someSegment.xSecond()-(LONG)somePoint.x());
|
|
distancePointTwo=Math::sqrt(yTemp+xTemp);
|
|
midx=((LONG)someSegment.xFirst()+(LONG)someSegment.xSecond())/2;
|
|
midy=((LONG)someSegment.yFirst()+(LONG)someSegment.ySecond())/2;
|
|
yTemp=((LONG)midy-(LONG)somePoint.y())*((LONG)midy-(LONG)somePoint.y());
|
|
xTemp=((LONG)midx-(LONG)somePoint.x())*((LONG)midx-(LONG)somePoint.x());
|
|
distancePointThree=Math::sqrt(yTemp+xTemp);
|
|
if(distancePointThree<=distancePointTwo&&distancePointTwo<=distancePointOne)
|
|
return distancePointThree;
|
|
if(distancePointTwo<=distancePointThree&&distancePointThree<=distancePointOne)
|
|
return distancePointTwo;
|
|
return distancePointOne;
|
|
}
|
|
|
|
WORD GridMesh::saveMesh(String &pathFileName)
|
|
{
|
|
FILE *fp;
|
|
GDIPoint tempPoint;
|
|
size_t size((int)Array<Segment>::size());
|
|
|
|
if(!size)return FALSE;
|
|
if(0==(fp=::fopen((LPSTR)pathFileName,"wb")))return FALSE;
|
|
::fwrite((void*)(LPSTR)mVersionInfo,::strlen(mVersionInfo),1,fp);
|
|
::fwrite((void*)&size,sizeof(size),1,fp);
|
|
::fwrite((void*)&mGridLines,sizeof(mGridLines),1,fp);
|
|
for(int i=0;i<size;i++)
|
|
{
|
|
tempPoint=Array<Segment>::operator[](i).firstPoint();
|
|
::fwrite((void *)&tempPoint,sizeof(GDIPoint),1,fp);
|
|
tempPoint=Array<Segment>::operator[](i).secondPoint();
|
|
::fwrite((void*)&tempPoint,sizeof(Point),1,fp);
|
|
}
|
|
::fclose(fp);
|
|
return TRUE;
|
|
}
|
|
|
|
WORD GridMesh::loadMesh(String &pathFileName)
|
|
{
|
|
FILE *fp;
|
|
GDIPoint firstPoint;
|
|
GDIPoint secondPoint;
|
|
String versionString;
|
|
int vectorIndex(0);
|
|
size_t size;
|
|
|
|
if(0==(fp=::fopen((LPSTR)pathFileName,"rb")))return upgradeStatus(FALSE);
|
|
versionString.reserve(String::MaxString);
|
|
::fread((void*)(LPSTR)versionString,::strlen(mVersionInfo),1,fp);
|
|
if(versionString!=mVersionInfo)
|
|
{
|
|
::fclose(fp);
|
|
return upgradeStatus(FALSE);
|
|
}
|
|
::fread((void*)&size,sizeof(size),1,fp);
|
|
::fread((void*)&mGridLines,sizeof(mGridLines),1,fp);
|
|
Array<Segment>::size(size);
|
|
for(int i=0;i<size;i++)
|
|
{
|
|
::fread((void*)&firstPoint,sizeof(Point),1,fp);
|
|
::fread((void*)&secondPoint,sizeof(Point),1,fp);
|
|
Array<Segment>::operator[](vectorIndex)=Segment(firstPoint,secondPoint,vectorIndex);
|
|
vectorIndex++;
|
|
}
|
|
::fclose(fp);
|
|
return upgradeStatus(TRUE);
|
|
}
|
|
|
|
WORD GridMesh::upgradeStatus(WORD retCode)const
|
|
{
|
|
if(!isVisible())show(SW_SHOW);
|
|
::InvalidateRect(mhParent,0,TRUE);
|
|
invalidate();
|
|
return retCode;
|
|
}
|
|
|
|
WORD GridMesh::retrieveMesh(Array<GDIPoint> &sourcePoints,Array<GDIPoint> &destPoints)
|
|
{
|
|
Array<Segment> sourceSegments;
|
|
createMesh(mGridLines,sourceSegments);
|
|
retrieveMesh(sourcePoints,sourceSegments);
|
|
retrieveMesh(destPoints,(Array<Segment>&)*this);
|
|
if(sourcePoints.size()!=destPoints.size())return FALSE;
|
|
return (WORD)sourcePoints.size();
|
|
}
|
|
|
|
WORD GridMesh::retrieveMesh(Array<GDIPoint> &meshPoints,Array<Segment> &someSegmentVector)const
|
|
{
|
|
Segment tempSegment;
|
|
int vectorIndex;
|
|
int pointIndex(0);
|
|
size_t vectorSize((int)someSegmentVector.size());
|
|
int haveFirstColumn(FALSE);
|
|
Array<Point> dummyPoint;
|
|
|
|
meshPoints.size(0);
|
|
meshPoints.size((mGridLines+2)*(mGridLines+2));
|
|
for(vectorIndex=0;vectorIndex<vectorSize;vectorIndex+=2)
|
|
{
|
|
if(!(vectorIndex%(mGridLines+1)))
|
|
{
|
|
if(!vectorIndex&&!haveFirstColumn)meshPoints[pointIndex++]=GDIPoint(0,0);
|
|
else if(haveFirstColumn)
|
|
{
|
|
if(someSegmentVector[vectorIndex].firstPoint().y()<
|
|
someSegmentVector[vectorIndex].secondPoint().y())
|
|
meshPoints[pointIndex++]=someSegmentVector[vectorIndex].firstPoint();
|
|
else
|
|
meshPoints[pointIndex++]=someSegmentVector[vectorIndex].secondPoint();
|
|
}
|
|
}
|
|
else if(vectorIndex<=(mGridLines*2)&&!haveFirstColumn)
|
|
{
|
|
tempSegment=someSegmentVector[vectorIndex-1];
|
|
if(tempSegment.firstPoint()==someSegmentVector[vectorIndex].firstPoint()||
|
|
tempSegment.firstPoint()==someSegmentVector[vectorIndex].secondPoint())
|
|
meshPoints[pointIndex++]=tempSegment.secondPoint();
|
|
else
|
|
meshPoints[pointIndex++]=tempSegment.firstPoint();
|
|
if(vectorIndex==mGridLines*2)
|
|
{
|
|
if(someSegmentVector[vectorIndex].firstPoint().y()<
|
|
someSegmentVector[vectorIndex].secondPoint().y())
|
|
meshPoints[pointIndex++]=GDIPoint(0,someSegmentVector[vectorIndex].secondPoint().y());
|
|
else
|
|
meshPoints[pointIndex++]=GDIPoint(0,someSegmentVector[vectorIndex].firstPoint().y());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(!haveFirstColumn){haveFirstColumn=TRUE;vectorIndex=-2;continue;}
|
|
if(someSegmentVector[vectorIndex].firstPoint().y()<
|
|
someSegmentVector[vectorIndex].secondPoint().y())
|
|
meshPoints[pointIndex++]=someSegmentVector[vectorIndex].firstPoint();
|
|
else
|
|
meshPoints[pointIndex++]=someSegmentVector[vectorIndex].secondPoint();
|
|
if(!((vectorIndex+2)%(mGridLines+1)))
|
|
{
|
|
if(someSegmentVector[vectorIndex].firstPoint().y()<
|
|
someSegmentVector[vectorIndex].secondPoint().y())
|
|
meshPoints[pointIndex++]=someSegmentVector[vectorIndex].secondPoint();
|
|
else
|
|
meshPoints[pointIndex++]=someSegmentVector[vectorIndex].firstPoint();
|
|
}
|
|
}
|
|
}
|
|
return (WORD)meshPoints.size();
|
|
}
|
|
|
|
void GridMesh::getBoundingRect(Block<Segment> ¤tIntersection,Rect &boundingRect)
|
|
{
|
|
for(int itemIndex=0;itemIndex<currentIntersection.size();itemIndex++)
|
|
{
|
|
const GDIPoint &firstPoint=currentIntersection[itemIndex].firstPoint();
|
|
const GDIPoint &secondPoint=currentIntersection[itemIndex].secondPoint();
|
|
|
|
if(!itemIndex)
|
|
{
|
|
boundingRect.left(firstPoint.x()<secondPoint.x()?firstPoint.x():secondPoint.x());
|
|
boundingRect.top(firstPoint.y()<secondPoint.y()?firstPoint.y():secondPoint.y());
|
|
boundingRect.right(firstPoint.x()>secondPoint.x()?firstPoint.x():secondPoint.x());
|
|
boundingRect.bottom(firstPoint.y()<secondPoint.y()?firstPoint.y():secondPoint.y());
|
|
}
|
|
else
|
|
{
|
|
GDIPoint minPoint;
|
|
GDIPoint maxPoint;
|
|
minPoint.x(firstPoint.x()<secondPoint.x()?firstPoint.x():secondPoint.x());
|
|
minPoint.y(firstPoint.y()<secondPoint.y()?firstPoint.y():secondPoint.y());
|
|
maxPoint.x(firstPoint.x()>secondPoint.x()?firstPoint.x():secondPoint.x());
|
|
maxPoint.y(firstPoint.y()>secondPoint.y()?firstPoint.y():secondPoint.y());
|
|
if(minPoint.x()<boundingRect.left())boundingRect.left(minPoint.x());
|
|
if(minPoint.y()<boundingRect.top())boundingRect.top(minPoint.y());
|
|
if(maxPoint.x()>boundingRect.right())boundingRect.right(maxPoint.x());
|
|
if(maxPoint.y()>boundingRect.bottom())boundingRect.bottom(maxPoint.y());
|
|
}
|
|
}
|
|
}
|