@版权声明:本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出,
本文链接https://www.jianshu.com/p/1d566a1445bc
如有问题, 可邮件(yumxuanyi@qq.com)咨询。
关键字:OpenCascade、MFC、AIS_RubberBand
问题描述
在使用MFC创建的Opeen Cascade项目中,可以使用AIS_RubberBand来创建选择框。
现在需要达到如下效果:
- down-move-up模式:鼠标左键按下 开始绘制矩形框, 左键保持按下状态随着鼠标移动不断更新矩形框 ,鼠标左键弹起矩形框绘制结束。
- 单击 - move - 单击 模式:鼠标左键单击(连续的down -up)开始绘制矩形框-随着鼠标移动矩形框不断更新-鼠标左键再次单击结束绘制
- 双击 - move - 双击 模式:同上单击模式,只是单击改双击。
但在MFC中区分鼠标单击、双击、左键按下、左键抬起是比较困难的一件事。因为无论单击还是双击始终会先触发down 。
解决方法
不处理双击还是单击,仅仅在buttonDown 和buttonUp中进行判断
因此将以上过程简化为:
- 仅仅在鼠标左键按下LeftButton中决定是否开始绘制矩形框或结束矩形框的绘制
- 在鼠标左键抬起时,判断是否结束绘制。
- 鼠标移动过程中 ,如果开始绘制就不断的更新矩形框。
通过以上处理。我们将决定是否开始绘制矩形框的条件完全放在LeftButtonDown中,这样只用判断鼠标是否为down 还是up来终止和结束绘制。
需要定义的变量
- Handle(AIS_RubberBand) mySelectionRectangle;//选择框对象
- Mouse_SelectionState mySelectionState;//用于记录当前的绘制状态 是开始还是结束。
enum Mouse_SelectionState
{
Mouse_StartSelection,//表示当前状态为已经开始了矩形框的绘制
Mouse_EndSelection,//表示当前矩形框绘制结束
Mouse_SelectionNormal//正常状态
};
当然你也可以直接用Standard_Boolean来记录。- DWORD myStartSelectionTime;//用于记录开始选择框的时间。
- Standard_Integer myFirstCursorX;//用于记录矩形框起点坐标X
- .Standard_Integer myFirstCursorY;//用于记录矩形框起点坐标Y
- Standard_Integer mySecondCursorX;//用于记录矩形框终点坐标X
- Standard_Integer mySecondCursorY;//用于记录矩形框终点坐标Y
在View类构造函数中进行初始化
CMyView::CMyView()
{
// TODO: 在此处添加构造代码
this->mySelectionRectangle = new AIS_RubberBand();//初始化选择框
mySelectionState = Mouse_SelectionNormal; //设置初始选择状态
}
在View类的析构函数中删除
CMyView::~CMyView()
{
this->mySelectionRectangle.Nullify();
}
具体实现
OnLButtonDown事件处理方法
在OnLButtonDown中进行开始绘制或结束绘制的判断,如果没有开始绘制 就要开始绘制,如果已经开始了绘制 就要结束绘制(检查不重合后)
void CMyView::OnLButtonDown(UINT nFlags,CPoint point)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
if (mySelectionState == Mouse_StartSelection)//表示已经开始了矩形框的绘制
{
//如果矩形框的绘制已经开始,需要检查如果当前鼠标点击点point ,
//如果不与开始绘制点重合(不落在开始绘制点的范围内时)就要结束矩形框的绘制
//如果重合(落在开始点的tol范围内)将保持绘制状态
Standard_Boolean inRect = JudgeMouseInRect(CPoint(myFirstCursorX, myFirstCursorY), point);//用于判断当前点point是否落在开始绘制点的范围内
if (inRect == Standard_False)//不重合结束绘制
{
mySecondCursorX = point.x;//记录结束的x坐标
mySecondCursorY = point.y;//记录结束的y坐标
mySelectionState = Mouse_EndSelection;//将状态标记为结束
DrawSelectionRectangle(Standard_False);//结束矩形框绘制 参数False表示不绘制矩形框
}
}
else//表示没有开始绘制 这时就要开始绘制矩形框了
{
myFirstCursorX = point.x;//记录开始绘制的位置
myFirstCursorY = point.y;
mySecondCursorX = point.x;//同时也重置一下这个位置
mySecondCursorY = point.y;
mySelectionState = Mouse_StartSelection;//记录状态为开始绘制
DrawSelectionRectangle(Standard_True);//开始绘图啦
myStartSelectionTime = ::GetTickCount();//这里记录开始绘制的时间
}
CView::OnLButtonDown(nFlags,point);
}
OnLButtonUp事件处理方法
在LButtonUp事件中判断是否结束绘制判断条件
- 如果已经开始可绘制 就要比对当前鼠标位置是否与开始绘制的位置是否重合 这里要求不重合
- 当前时间与开始时间之差是否大于一个余量(这里给200,防止过快的操作) 这里要求大于余量
- 若果以上两个条件都满足 就结束绘制
void CMyView::OnLButtonUp(UINT nFlags,CPoint point)
{
if (mySelectionState == Mouse_StartSelection)//开始了绘制 就要判断是否去结束绘制
{
CPoint center(myFirstCursorX, myFirstCursorY);
Standard_Boolean inRect = JudgeMouseInRect(center, point);//判断是否重合
if (inRect == Standard_False)//不重合 进行条件2的判断
{
DWORD currentTime = ::GetTickCount();
if (currentTime - myStartSelectionTime > 200)//起始时间差大于200 结束绘制
{
mySecondCursorX = point.x;//记录结束时的坐标
mySecondCursorY = point.y;
mySelectionState = Mouse_EndSelection;//标记为结束
DrawSelectionRectangle(Standard_False);//结束矩形框绘制
}
}
}
CView::OnLButtonUp(nFlags,point);
}
OnMouseMove事件处理方法
鼠标移动时开始更新矩形框,如果已经开始了绘制 就要在鼠标移动时不断更新矩形框的大小
void CMyView::OnMouseMove(UINT nFlags,CPoint point)
{
if (mySelectionState == Mouse_StartSelection)
{
mySecondCursorX = point.x;
mySecondCursorY = point.y;
DrawSelectionRectangle(Standard_True);
}
CView::OnMouseMove(nFlags,point);
}
JudgeMouseInRect方法
判断当前坐标是否与center坐标重合
判断方法 mousePoint不落在以center为半径 r=4的范围内 为不重合 否则重合
Standard_Boolean CMyView::JudgeMouseInRect(CPoint center, CPoint mousePoint)
{
Standard_Real xdis = abs(center.x - mousePoint.x);
Standard_Real ydis = abs(center.y - mousePoint.y);
double distance = sqrt(pow(xdis, 2) + pow(ydis, 2));//求距离
if (distance - 4 <= Precision::Confusion())//这里设置以半径为4的范围 这里给了半径为4个像素
{
return Standard_True;
}
return Standard_False;
}
DrawSelectionRectangle方法
绘制矩形选择框 isDisplay 表示十分显示 或 不显示矩形框
Standard_Boolean CEquipmentCADView::DrawSelectionRectangle(Standard_Boolean isDisplay)
{
Handle(AIS_InteractiveContext) hContext = GetDocument()->GetAISContext();
if (!isDisplay)
{
hContext->Remove(this->mySelectionRectangle, Standard_False);
hContext->CurrentViewer()->RedrawImmediate();
return Standard_False;
}
//获取视口尺寸
Standard_Integer winViewWidth;//视口窗体宽度
Standard_Integer winViewHeight;//视口窗体高度
this->myView->Window()->Size(winViewWidth, winViewHeight);//获取窗体尺寸
//注意:这里用view的Window的size方法 而不是GetWindowRect()方法 因为GetWindowRect()方法左右上下会少像素
this->mySelectionRectangle->SetRectangle(myFirstCursorX, winViewHeight - myFirstCursorY, mySecondCursorX, winViewHeight - mySecondCursorY);//设置矩形框的范围
this->mySelectionRectangle->SetFillTransparency(0.8);//设置矩形填充的透明度为0.8
//下面判断是全选还是部分选取的矩形框
//当从左到右选取时为全选 只有物体的包围框完全落在矩形选择框之内才选择
//当从右到左时为部分选。一旦物体的包围框与矩形选择框有相交就选择
if (myFirstCursorX <= mySecondCursorX)
{
this->mySelectionRectangle->SetLineType(Aspect_TOL_SOLID);//设置边框线型为实线
this->mySelectionRectangle->SetFillColor(Quantity_NOC_BLUE2);//设置填充颜色为蓝色
}
else
{
this->mySelectionRectangle->SetLineType(Aspect_TOL_DOT);//设置边框线型为虚点
this->mySelectionRectangle->SetFillColor(Quantity_NOC_GREEN2);//设置填充颜色为绿色
}
this->mySelectionRectangle->SetFilling(Standard_True);//开启矩形填充模式
//下面判断 如果已经显示了就更新图形
//没有显示就显示图像
if (!hContext->IsDisplayed(this->mySelectionRectangle))
{
hContext->Display(this->mySelectionRectangle, Standard_False);//显示对象
}
else
{
hContext->Redisplay(this->mySelectionRectangle, Standard_False);//更新对象显示
}
hContext->CurrentViewer()->RedrawImmediate();
return Standard_True;
}