📄 arcobj.cpp
字号:
#include "StdAfx.h"
#include ".\arcobj.h"
#include "visdrawdoc.h"
#include <math.h>
#include "visdrawview.h"
#define pi 3.1415926
#define DISTANCE_ZERO 10e-6
IMPLEMENT_SERIAL(CArcObj, CCircleObj, 0)
CArcObj::CArcObj(void)
{
}
CArcObj::~CArcObj(void)
{
}
CArcObj::CArcObj(const CRect& position)
: CCircleObj(position)
{
ASSERT_VALID(this);
m_StartAngle = 0.0;
m_EndAngle = 0.0;
}
void CArcObj::Draw(CVisDrawView* pView, CDC* pDC)
{
ASSERT_VALID(this);
//创建画刷
CBrush brush;
if (!brush.CreateBrushIndirect(&m_logbrush))
return;
//创建画笔
CPen pen;
if (!pen.CreatePenIndirect(&m_logpen))
return;
//设置DC
CBrush* pOldBrush;
CPen* pOldPen;
if (m_bBrush)
pOldBrush = pDC->SelectObject(&brush);
else
pOldBrush = (CBrush*)pDC->SelectStockObject(NULL_BRUSH);
if (m_bPen)
pOldPen = pDC->SelectObject(&pen);
else
pOldPen = (CPen*)pDC->SelectStockObject(NULL_PEN);
//采用逻辑坐标绘制圆弧
double x1,y1,x2,y2;
CPoint nCenterPoint;
long nRadium;
CPoint nStart, nEnd;
//得到起点坐标
x1=m_CenterPointx +m_Radium*cos(m_StartAngle);
y1=m_CenterPointy+m_Radium*sin(m_StartAngle);
//得到终点的坐标
x2=m_CenterPointx +m_Radium*cos(m_EndAngle);
y2=m_CenterPointy+m_Radium*sin(m_EndAngle);
pView->WorldToClient(nStart,x1,y1);
pView->WorldToClient(nEnd,x2,y2);
//把圆心世界坐标转化为逻辑坐标
pView->WorldToClient(nCenterPoint,m_CenterPointx,m_CenterPointy);
//把半径转化为逻辑长度
nRadium = (long)(pView->WorldToClient(m_Radium));
//绘制圆弧GDI函数
pDC->Arc((nCenterPoint.x - nRadium),(nCenterPoint.y - nRadium),
(nCenterPoint.x + nRadium),(nCenterPoint.y + nRadium),
nStart.x,nStart.y,nEnd.x,nEnd.y);
//恢复DC
pDC->SelectObject(pOldBrush);
pDC->SelectObject(pOldPen);
}
void CArcObj::Serialize(CArchive& ar)
{
ASSERT_VALID(this);
//调用基类串行化
CCircleObj::Serialize(ar);
if (ar.IsStoring())
{
ar << m_StartAngle << m_EndAngle;
}
else
{
//WORD wTemp;
ar >> m_StartAngle >> m_EndAngle;
}
}
//计算圆弧上点arcpoint相对于X轴夹角弧度,采用世界坐标计算
double CArcObj::CalcArcAngle(CVisDrawView* pView, CPoint arcpoint)
{
double arcpointx, arcpointy;
double nCenterPointx, nCenterPointy;
//得到圆心的世界坐标
GetCenterPoint(nCenterPointx, nCenterPointy);
//把圆弧上点arcpoint转化为世界坐标
pView->ClientToWorld(arcpoint, arcpointx, arcpointy);
double radium;
//计算世界坐标下的半径
radium = CalcRadium(arcpointx, arcpointy);
if(radium < DISTANCE_ZERO)
return 0 ;
double dx,dy;
dx = arcpointx - nCenterPointx;
dy = arcpointy - nCenterPointy;
double cosv = dx/radium;
double sinv = dy/radium;
//通过反余弦函数求得夹角弧度
if(sinv >= 0)
return acos(cosv) ;
else if(sinv < 0)
return 2.*pi-acos(cosv) ;
return 0;
}
//圆弧上三点计算圆弧参数,采用世界坐标
void CArcObj::CalculateArcParameterbytp(CVisDrawView* pView, CPoint startpoint,CPoint middlepoint,CPoint endpoint)
{
double an1,an2,an3;
double x1,y1,x2,y2,x3,y3;
double xx1,xx2,yy1,yy2,xx,yy,rr;
double k1,k2;
//确保两条直线不为水平线
int dy;
dy = middlepoint.y - startpoint.y;
if(fabs((float)dy)< DISTANCE_ZERO)middlepoint.y +=1;
dy = endpoint.y - middlepoint.y;
if(fabs((float)dy) < DISTANCE_ZERO)endpoint.y +=1;
//把逻辑坐标转化为世界坐标
pView->ClientToWorld(startpoint,x1,y1);
pView->ClientToWorld(middlepoint,x2,y2);
pView->ClientToWorld(endpoint,x3,y3);
//得到顺三点方向两直线中点坐标
xx1=(x1+x2)/2;
yy1=(y1+y2)/2;
xx2=(x2+x3)/2;
yy2=(y2+y3)/2;
if(fabs(y2-y1)>DISTANCE_ZERO) //如果第一条直线段的垂线不是垂直线
k1=-(x2-x1)/(y2-y1); //得到第一条直线段垂线的斜率
if(fabs(y3-y2)>DISTANCE_ZERO) //如果第二条直线段的垂线不是垂直线
k2=-(x3-x2)/(y3-y2); //得到第二条直线段垂线的斜率
//如果两条直线段都不是水平线
if(k1==k2) //如果两条直线段平行
{
//圆心坐标
xx=x2+(double)(10000/sqrt(1+k1*k1));
yy=y2+(double)(10000/sqrt((1+k1*k1)/(k1*k1)));
rr=10000;
}
else //如果两条直线段不平行
{
//圆心坐标,半径
xx=(yy2-yy1+k1*xx1-k2*xx2)/(k1-k2);
yy=yy1+k1*(xx-xx1);
rr=(double)sqrt((xx-x1)*(xx-x1)+(yy-y1)*(yy-y1)); //
}
SetCenterPoint(xx,yy);
SetRadium( rr);
//得到第一个点相对于圆心的弧度
an1=(double)acos((x1-xx)/rr);
if(y1-yy<0) an1=(double)(pi*2-an1);
//得到第二个点相对于圆心的弧度
an2=(double)acos((x2-xx)/rr);
if(y2-yy<0) an2=(double)(pi*2-an2);
//得到第三个点相对于圆心的弧度
an3=(double)acos((x3-xx)/rr);
if(y3-yy<0) an3=(double)(pi*2-an3);
//如果圆弧是逆时针方向画的
if(an2>an1&&an2<an1+pi||an2<an1&&an2+pi<an1)
{
m_StartAngle=an1; //起始弧度
m_EndAngle=an3; //终止弧度
}
//如果圆弧是逆时针方向画的
//如果圆弧是顺时针方向画的
else
{
m_StartAngle=an3; //起始弧度
m_EndAngle=an1; //终止弧度
}
}
//计算矩形图形元边界矩形,以逻辑坐标表示
CRect CArcObj::CalcBounds(CVisDrawView* pView)
{
ASSERT_VALID(this);
double MinX, MinY, MaxX, MaxY ,tempX, tempY;
int pb = 0 ;
double m_beginx, m_beginy, m_endx, m_endy;
m_beginx = m_CenterPointx + m_Radium*cos(m_StartAngle);
m_beginy = m_CenterPointy + m_Radium*sin(m_StartAngle);
m_endx = m_CenterPointx + m_Radium*cos(m_EndAngle);
m_endy = m_CenterPointy + m_Radium*sin(m_EndAngle);
MinX = min( m_beginx, m_endx );
MinY = min( m_beginy, m_endy );
MaxX = max( m_beginx, m_endx );
MaxY = max( m_beginy, m_endy );
if(m_StartAngle < m_EndAngle) pb = 1 ;
else
{
MinX = min(MinX, m_CenterPointx + m_Radium) ;
MinY = min(MinY, m_CenterPointy) ;
MaxX = max(MaxX, m_CenterPointx + m_Radium) ;
MaxY = max(MaxY, m_CenterPointy) ;
tempX = MaxX ;
tempY = MaxY ;
pb = 2 ;
}
for(int i=0 ;i<4 ;i++)
{
if(pb==1 &&pi*i/2.>m_StartAngle &&pi*i/2.<m_EndAngle||
pb==2 &&!(pi*i/2.>m_EndAngle &&pi*i/2.<m_StartAngle))
{
if(i==1)
{
tempX = m_CenterPointx ; tempY = m_CenterPointy + m_Radium;
}
if(i==2)
{
tempX = m_CenterPointx - m_Radium ; tempY = m_CenterPointy ;
}
if(i==3)
{
tempX = m_CenterPointx ; tempY = m_CenterPointy - m_Radium ;
}
MinX = min(MinX, tempX) ;
MinY = min(MinY, tempY) ;
MaxX = max(MaxX, tempX) ;
MaxY = max(MaxY, tempY) ;
}
}
CPoint pt1,pt2;
pView->WorldToClient(pt1,MinX,MinY);
pView->WorldToClient(pt2,MaxX,MaxY);
m_position.TopLeft() = pt1;
m_position.BottomRight() = pt2;
return m_position;
}
//图元被选择判断
BOOL CArcObj::IsSelected(CVisDrawView* pView, const CPoint& point)
{
//参数point是鼠标的逻辑坐标
CPoint local = point;
//参数point是鼠标的逻辑坐标
double distance,radium;
int nSelectDistance;
//鼠标点的设备坐标
pView->DocToClient(local);
//识别精度值
nSelectDistance = pView->GetDocument()->GetSetectDistance()/2;
CPoint pt;
//把圆心坐标转化为逻辑坐标
pView->WorldToClient(pt, m_CenterPointx, m_CenterPointy);
//把圆心坐标由逻辑坐标转化为设备坐标
pView->DocToClient(pt);
//计算鼠标点point与圆心之间的像素距离
distance = PointToPoint(pt,local);
radium = pView->WorldToClient(m_Radium);
distance = fabs(distance - radium);
//如果拾取点到圆弧距离小于识别精度,则继续判断弧度范围
if(distance > nSelectDistance) return false;
else
{
//拾取点与圆心中心连线与X轴正向夹角弧度
double angle;
//函数参数local为逻辑坐标
angle = CalcArcAngle(pView, local);
if((m_StartAngle < m_EndAngle && angle < m_EndAngle && angle > m_StartAngle)
||m_StartAngle > m_EndAngle && angle < m_StartAngle && angle > m_EndAngle)
return TRUE;
else
return FALSE;
}
}
//返回手柄个数
int CArcObj::GetHandleCount()
{
ASSERT_VALID(this);
return 4;
}
// 返回手柄中心逻辑坐标
CPoint CArcObj::GetHandle(CVisDrawView* pView, int nHandle)
{
ASSERT_VALID(this);
CPoint point;
double pointx,pointy;
double angle;
switch(nHandle)
{
case 1:
//起点
pointx = m_CenterPointx + m_Radium*cos(m_StartAngle);
pointy = m_CenterPointy + m_Radium*sin(m_StartAngle);
break;
case 2:
//终点
pointx = m_CenterPointx + m_Radium*cos(m_EndAngle);
pointy = m_CenterPointy + m_Radium*sin(m_EndAngle);
break;
case 3:
//中点
angle = (m_StartAngle + m_EndAngle)/2.0;
pointx = m_CenterPointx + m_Radium*cos(angle);
pointy = m_CenterPointy + m_Radium*sin(angle);
break;
case 4:
//圆弧中心
pointx = m_CenterPointx;
pointy = m_CenterPointy;
break;
}
//把手柄世界坐标转化为逻辑坐标
pView->WorldToClient(point,pointx,pointy);
return point;
}
//移动手柄
void CArcObj::MoveHandleTo(int nHandle, CPoint point, CVisDrawView* pView)
{
ASSERT_VALID(this);
//圆弧上三点逻辑坐标
CPoint pt1, pt2, pt3;
pt1 = GetHandle(pView, 1);
pt2 = GetHandle(pView, 2);
pt3 = GetHandle(pView, 3);
switch (nHandle)
{
default:
ASSERT(FALSE);
case 1:
CalculateArcParameterbytp(pView, point,pt2,pt3);
break;
case 2:
CalculateArcParameterbytp(pView, pt1,point,pt3);
break;
case 3:
CalculateArcParameterbytp(pView, pt1,pt2,point);
break;
}
CalcBounds(pView);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -