You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

304 lines
9.5 KiB
C++

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#include "StdAfx.h"
#include "MouseToolCut.h"
#include "GlobalDrawMgr.h"
#include "Layer.h"
#include "GlobalFunction.h"
#include "LogMgr.h"
#include "ObjPline.h"
#include "CommandCut.h"
#include "CommandMgr.h"
#if 1//排序用比较函数
//相等返回0 如果第一个参数优先,返回>0的值 否则返回小于0的值
bool CompareDbPointX(Dbxy pt1,Dbxy pt2)
{
return pt1.x>pt2.x;
}
bool CompareDbPointY(Dbxy pt1,Dbxy pt2)
{
return pt1.y>pt2.y;
}
#endif
CMouseToolCut::CMouseToolCut(void)
{
}
CMouseToolCut::~CMouseToolCut(void)
{
}
void CMouseToolCut::OnLButtonDown(UINT nFlags, CPoint point,CClientDC &dc)
{
Dbxy pt = gDraw->CPoint2Dbxy(point);
DbRect rect = gDraw->GetCurPointRect(pt);
CutLineInRect(rect);
}
//剪切rect 中的line
void CMouseToolCut::CutLineInRect(DbRect &rect)
{
CLayer &layer = gLayer;
if(!layer.HasObjectInRect(rect))
return;
//先找到第一个在rect 的obj -----------------------------------------------
Sptr<CObjBase> pObj = layer.GetFirstObjInRect(rect);
//获得obj 在rect 中的第一条line -----------------------------------------------
Dbxy LinePt1,LinePt2;
GetFirstLineInRect(pObj,rect,LinePt1,LinePt2);
if(LinePt1.Equal(LinePt2))
return;
//获得line 形成的矩形中所有的线段到LineVec -----------------------------------------------
vector<DbLine> LineVec;
DbRect LineRect = GetLineRect(LinePt1,LinePt2);
layer.GetLineInRect(LineRect,LineVec,false);
//计算所有的交点到PointVec -----------------------------------------------
vector<Dbxy> PointVec;
CalAllIntersection(LinePt1,LinePt2,LineRect,LineVec,PointVec);
//如果一个交点没有则只剩最后一个线段了, 不能修剪
if(PointVec.empty())
return;
//根据交点情况处理obj -----------------------------------------------
CutObj(LinePt1,LinePt2,pObj,PointVec,rect);
}
//获得被切割线段形成的矩形
DbRect CMouseToolCut::GetLineRect(const Dbxy &LinePt1,const Dbxy &LinePt2)
{
double MixGap = 0.01;
//避免水平或垂直的情况
Dbxy RectPt1 = LinePt1;
Dbxy RectPt2 = LinePt2;
if(IsTwoDbEqual(LinePt1.x,LinePt2.x))
{
RectPt1.x -= MixGap;
RectPt2.x += MixGap;
}
if(IsTwoDbEqual(LinePt1.y,LinePt2.y))
{
RectPt1.y += MixGap;
RectPt2.y -= MixGap;
}
DbRect LineRect(RectPt1,RectPt2);
return LineRect;
}
//获得obj 在rect 中的第一条line
void CMouseToolCut::GetFirstLineInRect(Sptr<CObjBase> &pObj,DbRect &rect,Dbxy &LinePt1,Dbxy &LinePt2)
{
vector<DbLine> LineVec;
pObj->GetLineInRect(rect,LineVec);
LinePt1 = LineVec[0].m_pt1.GetPt();
LinePt2 = LineVec[0].m_pt2.GetPt();
}
//计算所有交点,保存到PointVec 中
void CMouseToolCut::CalAllIntersection(Dbxy LinePt1,Dbxy LinePt2,DbRect &LineRect,vector<DbLine> &LineVec,vector<Dbxy> &PointVec)
{
vector<DbLine>::iterator iter = LineVec.begin();
vector<DbLine>::iterator iter_end = LineVec.end();
for(;iter!=iter_end;iter++)
{
Dbxy pt3 = (*iter).m_pt1.GetPt();
Dbxy pt4 = (*iter).m_pt2.GetPt();
if(pt3.Equal(pt4))//不要处理两个点在一起的线段
{
continue;
}
if(IsTwoLineIntersect(LinePt1,LinePt2,pt3,pt4))
{
//如果相交求出交点
Dbxy pt = CalIntersection(LinePt1,LinePt2,pt3,pt4);
//交点在rect 内则加入捕捉点
if(IsPointInRect(pt,LineRect))
{
PointVec.push_back(pt);
}
}
}
}
//根据交点来修剪obj
//rect 是鼠标点的rect 范围
//pObj 是当前剪切的对象
//PointVec 是线段交点的容器
void CMouseToolCut::CutObj(Dbxy LinePt1,Dbxy LinePt2,Sptr<CObjBase> pObj,vector<Dbxy> &PointVec,DbRect &rect)
{
//获取离鼠标最近的剪切点-------------------------------------
Dbxy CutPt1,CutPt2;
if(GetCutPoint(LinePt1,LinePt2,PointVec,rect,CutPt1,CutPt2)==false)
{
return;
}
//将obj 剪切为两个部分------------------------------------------
CObjPline *pObj1 = NULL;
CObjPline *pObj2 = NULL;
CutObjExt(LinePt1,LinePt2,pObj,CutPt1,CutPt2,pObj1,pObj2);
//创建撤销指令-----------------------------------
m_TmpObjContainer.Clear();
CreatCommandCut(pObj,pObj1,pObj2);
GetCurViewPtr()->RefreshView();
}
//创建修剪指令
//pObj 时修剪前obj
//pObj1pObj2 是修剪后的obj
void CMouseToolCut::CreatCommandCut(Sptr<CObjBase> pObj,CObjPline *&pObj1,CObjPline *&pObj2)
{
CCommandCut *p = new CCommandCut;
p->AddOpObj(pObj);
AddToCmd(pObj1,p);
AddToCmd(pObj2,p);
m_TmpObjContainer.AllObjAddToLayer();
gCommandMgr.AddUndoCommand(p);
gLayer.DelObj(pObj);
}
void CMouseToolCut::AddToCmd(CObjPline *&pObj,CCommandCut *&pCommandCut)
{
if(pObj)
{
//保存到智能指针
Sptr<CObjBase> sPtr(pObj);
m_TmpObjContainer.AddObject(sPtr);
pCommandCut->AddOpObj(sPtr);
}
else
{
delete pObj;
}
}
//将obj 剪切为两个部分
bool CMouseToolCut::CutObjExt(Dbxy LinePt1,Dbxy LinePt2,Sptr<CObjBase> pObj,Dbxy CutPt1,Dbxy CutPt2,CObjPline *&pObj1,CObjPline *&pObj2)
{
vector<CDataPoint>&Container = pObj->GetPtContainer();
int size = Container.size();
Dbxy NearPt;//第一部分最后的点
Dbxy FarPt;//第二部分第一个点
//分割第一部分------------------------------------------------------------
int i=0;
for(;i<size;i++)
{
Dbxy pt = Container[i].GetPt();
Dbxy Nextpt = Container[i+1].GetPt();
//检查是否为当前操作的线段
if(IsSelLine(LinePt1,LinePt2,pt,Nextpt))
{
GetNearFarPoint(CutPt1,CutPt2,pt,NearPt,FarPt);
//如果剪切点是obj 的端点, 则没有第一部分
if(i==0 && (pt.Equal(CutPt1) || pt.Equal(CutPt2)))
{
i++;//break 了以后i 不会自增
break;
}
}
if(i==0)//创建第一部分
{
pObj1 = new CObjPline;
//当前点加入第一部分
CDataPoint DataPoint(Container[i].GetPt());
DataPoint.SetIsNode(true);
pObj1->AddDataPoint(DataPoint);
}
else
{
pObj1->AddDataPoint(Container[i]);
}
//如果当前线段为操作线段,结束第一部分
if(IsSelLine(LinePt1,LinePt2,pt,Nextpt))
{
GetNearFarPoint(CutPt1,CutPt2,pt,NearPt,FarPt);
Dbxy pt = Container[i].GetPt();
if(!(pt == NearPt))//避免结尾重复的点2015-12-10
{
CDataPoint DataPoint(NearPt);
DataPoint.SetIsNode(true);
pObj1->AddDataPoint(DataPoint);
}
i++;//break 了以后i 不会自增
break;
}
}
//分割第二部分--------------------------------------------------------------
if(!(i==size-1 && FarPt.Equal(Container[i].GetPt())))
{
bool bflg = true;
for(;i<size;i++)
{
if(bflg)//分割点的第二个点为第二段的起点
{
pObj2 = new CObjPline;
CDataPoint DataPoint(FarPt);
DataPoint.SetIsNode(true);
pObj2->AddDataPoint(DataPoint);
}
//当前点加入第二部分
if(i==size-1)//最后一个点是节点
{
CDataPoint DataPoint(Container[i].GetPt());
DataPoint.SetIsNode(true);
pObj2->AddDataPoint(DataPoint);
}
else
{
//如果FarPt 等于第一个点则不要重复加入
if(!(bflg && FarPt.Equal(Container[i].GetPt())))
{
pObj2->AddDataPoint(Container[i]);
}
}
bflg = false;
}
}
return true;
}
//获取离鼠标最近的剪切点
bool CMouseToolCut::GetCutPoint(Dbxy LinePt1,Dbxy LinePt2,vector<Dbxy> &PointVec,DbRect &rect,Dbxy &CutPt1,Dbxy &CutPt2)
{
//先得到鼠标rect 线段的交点--------------------------------------------------
Dbxy MousePt = IntersectionOfRectAndLine(LinePt1,LinePt2,rect);
//计算和MousePt 最近的两个交点----------------------------------------------
PointVec.push_back(MousePt);
PointVec.push_back(LinePt1);
PointVec.push_back(LinePt2);
if(IsTwoDbEqual(LinePt1.x,LinePt2.x))//垂直的情况单独处理
{
//按y 排序
sort(PointVec.begin(),PointVec.end(),CompareDbPointY);
}
else//不垂直
{
//按x 排序
sort(PointVec.begin(),PointVec.end(),CompareDbPointX);
}
vector<Dbxy>::iterator MousePtIter = find(PointVec.begin(),PointVec.end(),MousePt);
//只处理MousePtIter 在中间的情况
if(MousePtIter==PointVec.end() || MousePtIter==PointVec.end()-1 || MousePtIter==PointVec.begin())
return false;
CutPt1 = (*(MousePtIter-1));
CutPt2 = (*(MousePtIter+1));
if(CutPt1.Equal(CutPt2))
return false;
return true;
}
//检查是否为当前操作的线段
bool CMouseToolCut::IsSelLine(Dbxy LinePt1,Dbxy LinePt2,Dbxy pt,Dbxy Nextpt)
{
return ((LinePt1.Equal(pt) && LinePt2.Equal(Nextpt))||(LinePt1.Equal(Nextpt) && LinePt2.Equal(pt)));
}
//获得两个点中离pt 较近的点
void CMouseToolCut::GetNearFarPoint(Dbxy pt1,Dbxy pt2,Dbxy pt,Dbxy &NearPt,Dbxy &FarPt)
{
if(CalDistance(pt1,pt)<CalDistance(pt2,pt))
{
NearPt = pt1;
FarPt = pt2;
}
else
{
NearPt = pt2;
FarPt = pt1;
}
}