MFC下如何绘制一个类似云(cloud)的图形
2013-09-16 17:42
537 查看
How to draw a cloud shape with MFC
问题描述:添加一种云状,有曲线外边的绘图工具,类似line,circle,rectangle。通过鼠标点击的点确定图形的顶点,在由此形成的多边形外围绘制曲线,形成最终图形。多边形可以凸或者凹,但不可以自交叉(self-intersect)。
解决方案:每次取出两个相邻的顶点,通过计算两顶点的距离,得出可以绘制弧线的数量,并绘制数目不等的半圆弧连接顶点,这时弧线的半径是固定的。(当然也可以绘制固定个数的弧线,此时弧线的半径是可变的,但是忽大忽小的弧线导致绘制效果不太理想。)
遇到的问题:
1. 当弧线半径一定时,顶点间的距离无法保证可以绘制完整的弧线。
有两个选择:一个是在顶点处终止弧的绘制,每条边自行负责绘制弧线。另一个是当一个边的距离不够绘制一个完整的弧线时,跨越顶点绘制到邻近的边上,这时候绘制的不是半圆弧了。第二种方法需要更多的计算。相比之下,第一种方法比较简单,但是有两种情况需要区分:
(1)两顶点的距离非常近,无法绘制出一个完整的弧。(N=0)
此时需要直接用两个顶点作为弧线的起始点,绘制一个小弧。
(2)两顶点的距离足够绘制N(N>=1)个完整的弧,但还会有剩余的距离绘制一个不完整的弧。
为了使弧线显得连续好看,当剩余距离小于弧线半径时,前一个完整的弧吞并剩余距离。当剩余距离大于弧线半径时,直接用剩余距离绘制一个小弧。
void DrawCloudArc(HDC hDC)
{
int nCount = vcPoints.size();
if(nCount < 4)
return;
float arc_radius = 60;
float arc_diameter = arc_radius * 2;
CDC *pDC = CDC::FromHandle(hDC);
double sum = 0.0;
for(int i = 0; i < nCount; i++)
{
int j = (i+1) % nCount;
sum += (vcPoints[j].x - vcPoints[i].x) * ( vcPoints[j].y + vcPoints[i].y);
}
bool b_clockwise = sum > 0.0;
for(int i = 0; i < nCount; ++i)
{
int j = (i + 1) % nCount;
CPoint ptStart = vcPoints.at(i);
CPoint ptEnd = vcPoints.at(j);
float xdiff = ptEnd.x - ptStart.x;
float ydiff = ptEnd.y - ptStart.y;
float dis_pt = std::sqrt(ydiff * ydiff + xdiff * xdiff);
float cosangle = xdiff / dis_pt;
float sinangle = ydiff / dis_pt;
int nArcPos = 0;
int nArcCount = dis_pt/arc_diameter;
CPoint pt1, pt2;
// if the two points of cloud are too near to draw a whole arc curve.
if( nArcCount == 0 )
{
DrawCloudArcWithPoints(pDC, ptStart, ptEnd, b_clockwise);
}
while(nArcPos < nArcCount)
{
pt1.x = ptStart.x + nArcPos * arc_diameter * cosangle;
pt1.y = ptStart.y + nArcPos * arc_diameter * sinangle;
pt2.x = ptStart.x + (nArcPos + 1) * arc_diameter * cosangle;
pt2.y = ptStart.y + (nArcPos + 1) * arc_diameter * sinangle;
CPoint ptNext1 = pt2;
CPoint ptNext2 = ptEnd;
//add extra effect for the last arc curve.. with different touch points
if( nArcPos == nArcCount - 1 )
{
float xnd = ptNext1.x - ptNext2.x;
float ynd = ptNext1.y - ptNext2.y;
float dDis = std::sqrt(xnd * xnd + ynd * ynd);
// if the left distance is larger than radius, draw an extra arc curve from ptNext1 to ptEnd.
// otherwise change pt2 to be ptEnd and draw the arc curve from pt1 to ptEnd.
if( dDis > 0.0 )
{
if( dDis >= arc_diameter * 0.5 )
{
DrawCloudArcWithPoints(pDC, ptNext1, ptEnd, b_clockwise);
nArcPos++;
}
else
{
pt2 = ptEnd;
}
}
}
DrawCloudArcWithPoints(pDC, pt1, pt2, b_clockwise);
nArcPos++;
}
}
}
void DrawCloudArcWithPoints(CDC *pDC, CPoint pt1, CPoint pt2, bool b_clockwise)
{
CPoint pt0;
pt0.x = (pt1.x+pt2.x)/2;
pt0.y = (pt1.y+pt2.y)/2;
float xd = pt1.x-pt2.x;
float yd = pt1.y-pt2.y;
float radius = std::sqrt(xd*xd+yd*yd)/2;
CPoint tl(pt0.x - radius, pt0.y - radius);
CPoint br(pt0.x + radius, pt0.y + radius);
CRect rect(tl, br);
if(b_clockwise)
pDC->Arc(&rect, pt1, pt2);
else
pDC->Arc(&rect, pt2, pt1);
}
2.需要知道一个多边形的顶点的顺序是顺时针还是逆时针。因为需要把弧线绘制到多边形的边的外面,要注意绘制弧线的方向。
假设有n(0...n-1)个顶点P0....Pn-1。算法如下:
double sum = 0.0;
for(int pre
= 0; pre < n; pre++)
{
int next = (pre+1)%n;
sum += (P[next].x - P[pre].x) * (P[next].y + P[pre].y);
}
bool bclockwise = sum > 0;
此算法适合凹或凸多边形,自交叉多边形(本次没有用到,有待验证)。
参考资料:http://stackoverflow.com/questions/1165647/how-to-determine-if-a-list-of-polygon-points-are-in-clockwise-order
3. 需要额外的扩大刷新区域,因为有弧的存在。
刷新用的rgn至少需要扩大一个弧半径的大小,然而由于合并小弧,至少要扩大1.2倍左右的弧半径。
问题描述:添加一种云状,有曲线外边的绘图工具,类似line,circle,rectangle。通过鼠标点击的点确定图形的顶点,在由此形成的多边形外围绘制曲线,形成最终图形。多边形可以凸或者凹,但不可以自交叉(self-intersect)。
解决方案:每次取出两个相邻的顶点,通过计算两顶点的距离,得出可以绘制弧线的数量,并绘制数目不等的半圆弧连接顶点,这时弧线的半径是固定的。(当然也可以绘制固定个数的弧线,此时弧线的半径是可变的,但是忽大忽小的弧线导致绘制效果不太理想。)
遇到的问题:
1. 当弧线半径一定时,顶点间的距离无法保证可以绘制完整的弧线。
有两个选择:一个是在顶点处终止弧的绘制,每条边自行负责绘制弧线。另一个是当一个边的距离不够绘制一个完整的弧线时,跨越顶点绘制到邻近的边上,这时候绘制的不是半圆弧了。第二种方法需要更多的计算。相比之下,第一种方法比较简单,但是有两种情况需要区分:
(1)两顶点的距离非常近,无法绘制出一个完整的弧。(N=0)
此时需要直接用两个顶点作为弧线的起始点,绘制一个小弧。
(2)两顶点的距离足够绘制N(N>=1)个完整的弧,但还会有剩余的距离绘制一个不完整的弧。
为了使弧线显得连续好看,当剩余距离小于弧线半径时,前一个完整的弧吞并剩余距离。当剩余距离大于弧线半径时,直接用剩余距离绘制一个小弧。
void DrawCloudArc(HDC hDC)
{
int nCount = vcPoints.size();
if(nCount < 4)
return;
float arc_radius = 60;
float arc_diameter = arc_radius * 2;
CDC *pDC = CDC::FromHandle(hDC);
double sum = 0.0;
for(int i = 0; i < nCount; i++)
{
int j = (i+1) % nCount;
sum += (vcPoints[j].x - vcPoints[i].x) * ( vcPoints[j].y + vcPoints[i].y);
}
bool b_clockwise = sum > 0.0;
for(int i = 0; i < nCount; ++i)
{
int j = (i + 1) % nCount;
CPoint ptStart = vcPoints.at(i);
CPoint ptEnd = vcPoints.at(j);
float xdiff = ptEnd.x - ptStart.x;
float ydiff = ptEnd.y - ptStart.y;
float dis_pt = std::sqrt(ydiff * ydiff + xdiff * xdiff);
float cosangle = xdiff / dis_pt;
float sinangle = ydiff / dis_pt;
int nArcPos = 0;
int nArcCount = dis_pt/arc_diameter;
CPoint pt1, pt2;
// if the two points of cloud are too near to draw a whole arc curve.
if( nArcCount == 0 )
{
DrawCloudArcWithPoints(pDC, ptStart, ptEnd, b_clockwise);
}
while(nArcPos < nArcCount)
{
pt1.x = ptStart.x + nArcPos * arc_diameter * cosangle;
pt1.y = ptStart.y + nArcPos * arc_diameter * sinangle;
pt2.x = ptStart.x + (nArcPos + 1) * arc_diameter * cosangle;
pt2.y = ptStart.y + (nArcPos + 1) * arc_diameter * sinangle;
CPoint ptNext1 = pt2;
CPoint ptNext2 = ptEnd;
//add extra effect for the last arc curve.. with different touch points
if( nArcPos == nArcCount - 1 )
{
float xnd = ptNext1.x - ptNext2.x;
float ynd = ptNext1.y - ptNext2.y;
float dDis = std::sqrt(xnd * xnd + ynd * ynd);
// if the left distance is larger than radius, draw an extra arc curve from ptNext1 to ptEnd.
// otherwise change pt2 to be ptEnd and draw the arc curve from pt1 to ptEnd.
if( dDis > 0.0 )
{
if( dDis >= arc_diameter * 0.5 )
{
DrawCloudArcWithPoints(pDC, ptNext1, ptEnd, b_clockwise);
nArcPos++;
}
else
{
pt2 = ptEnd;
}
}
}
DrawCloudArcWithPoints(pDC, pt1, pt2, b_clockwise);
nArcPos++;
}
}
}
void DrawCloudArcWithPoints(CDC *pDC, CPoint pt1, CPoint pt2, bool b_clockwise)
{
CPoint pt0;
pt0.x = (pt1.x+pt2.x)/2;
pt0.y = (pt1.y+pt2.y)/2;
float xd = pt1.x-pt2.x;
float yd = pt1.y-pt2.y;
float radius = std::sqrt(xd*xd+yd*yd)/2;
CPoint tl(pt0.x - radius, pt0.y - radius);
CPoint br(pt0.x + radius, pt0.y + radius);
CRect rect(tl, br);
if(b_clockwise)
pDC->Arc(&rect, pt1, pt2);
else
pDC->Arc(&rect, pt2, pt1);
}
2.需要知道一个多边形的顶点的顺序是顺时针还是逆时针。因为需要把弧线绘制到多边形的边的外面,要注意绘制弧线的方向。
假设有n(0...n-1)个顶点P0....Pn-1。算法如下:
double sum = 0.0;
for(int pre
= 0; pre < n; pre++)
{
int next = (pre+1)%n;
sum += (P[next].x - P[pre].x) * (P[next].y + P[pre].y);
}
bool bclockwise = sum > 0;
此算法适合凹或凸多边形,自交叉多边形(本次没有用到,有待验证)。
参考资料:http://stackoverflow.com/questions/1165647/how-to-determine-if-a-list-of-polygon-points-are-in-clockwise-order
3. 需要额外的扩大刷新区域,因为有弧的存在。
刷新用的rgn至少需要扩大一个弧半径的大小,然而由于合并小弧,至少要扩大1.2倍左右的弧半径。
相关文章推荐
- MFC上如何绘制一个可以旋转的椭圆
- webgl学习笔记1--如何绘制一个基本图形
- (他山之石)MFC学习之路(二)如何在多文档视图中,程序一开始运行,只出现主框架,不打开一个文档?
- Delphi如何创建并绘制EMF图形文件
- 孙鑫MFC学习心得:Lesson10:图形的绘制
- 一个蛮不错的图形绘制控件 dotnetCHARTING (提供license下载)
- 如何使用matplotlib绘制一个函数的图像
- 如何去绘制一个圆并且转为imageView格式
- 在同一个canvas中绘制多个图形
- 如何在VC++ MFC的背景位图上重绘一个区域时避免闪屏
- duilib篇 如何在WTL和MFC中使用duilib及如何静态使用duilib库!(初级讲解 附带一个Demo)
- (译)如何制作一个类似tiny wings的游戏:第二部分(完)
- 终于手动做好了第一个稍微有的形状的MFC程序~一个简陋的图形绘图工具
- 21. 如何制作一个类似tiny wings的游戏:第一部分
- 不知道在MFC中如何从线程工作函数中向窗口发送消息?用好一个指针就够了
- 教你一招 - 如何给nopcommerce增加一个类似admin的area
- 把一个图形沿它的几何中心(形心)方向偏移一定的距离d?? lisp 如何编程??
- 如何在MFC程序中添加一个登陆框
- 在vs2010下如何纯手写一个MFC程序
- Burp Suite出了一个类似cloudeye.me的功能