2010-07-16 50 views
2

我用下面的算法来生成多边形的起点和终点概述:四舍五入轮廓

void OGLSHAPE::GenerateLinePoly(std::vector<DOUBLEPOINT> &input, int width) 
{ 
    OutlineVec.clear(); 
    if(input.size() < 2) 
    { 
     return; 
    } 


    if(connected) 
    { 
     input.push_back(input[0]); 
     input.push_back(input[1]); 
    } 


    float w = width/2.0f; 

    //glBegin(GL_TRIANGLES); 
    for(size_t i = 0; i < input.size()-1; ++i) 
    { 
     POINTFLOAT cur; 
     cur.x = input[i].point[0]; 
     cur.y = input[i].point[1]; 


     POINTFLOAT nxt; 


     nxt.x = input[i+1].point[0]; 
     nxt.y = input[i+1].point[1]; 

     POINTFLOAT b; 
     b.x = nxt.x - cur.x; 
     b.y = nxt.y - cur.y; 

     b = normalize(b); 



     POINTFLOAT b_perp; 
     b_perp.x = -b.y; 
     b_perp.y = b.x; 


     POINTFLOAT p0; 
     POINTFLOAT p1; 
     POINTFLOAT p2; 
     POINTFLOAT p3; 

     p0.x = cur.x + b_perp.x * w; 
     p0.y = cur.y + b_perp.y * w; 

     p1.x = cur.x - b_perp.x * w; 
     p1.y = cur.y - b_perp.y * w; 

     p2.x = nxt.x + b_perp.x * w; 
     p2.y = nxt.y + b_perp.y * w; 

     p3.x = nxt.x - b_perp.x * w; 
     p3.y = nxt.y - b_perp.y * w; 

     OutlineVec.push_back(p0.x); 
     OutlineVec.push_back(p0.y); 
     OutlineVec.push_back(p1.x); 
     OutlineVec.push_back(p1.y); 
     OutlineVec.push_back(p2.x); 
     OutlineVec.push_back(p2.y); 

     OutlineVec.push_back(p2.x); 
     OutlineVec.push_back(p2.y); 
     OutlineVec.push_back(p1.x); 
     OutlineVec.push_back(p1.y); 
     OutlineVec.push_back(p3.x); 
     OutlineVec.push_back(p3.y); 



     // only do joins when we have a prv 
     if(i == 0) continue; 


     POINTFLOAT prv; 
     prv.x = input[i-1].point[0]; 
     prv.y = input[i-1].point[1]; 

     POINTFLOAT a; 
     a.x = prv.x - cur.x; 
     a.y = prv.y - cur.y; 

     a = normalize(a); 

     POINTFLOAT a_perp; 
     a_perp.x = a.y; 
     a_perp.y = -a.x; 

     float det = a.x * b.y - b.x * a.y; 
     if(det > 0) 
     { 
      a_perp.x = -a_perp.x; 
      a_perp.y = -a_perp.y; 

      b_perp.x = -b_perp.x; 
      b_perp.y = -b_perp.y; 
     } 

     // TODO: do inner miter calculation 

     // flip around normals and calculate round join points 
     a_perp.x = -a_perp.x; 
     a_perp.y = -a_perp.y; 

     b_perp.x = -b_perp.x; 
     b_perp.y = -b_perp.y; 

     size_t num_pts = 4; 

     std::vector< POINTFLOAT> round(1 + num_pts + 1); 
     POINTFLOAT nc; 
     nc.x = cur.x + (a_perp.x * w); 
     nc.y = cur.y + (a_perp.y * w); 

     round.front() = nc; 

     nc.x = cur.x + (b_perp.x * w); 
     nc.y = cur.y + (b_perp.y * w); 

     round.back() = nc; 

     for(size_t j = 1; j < num_pts+1; ++j) 
     { 
      float t = (float)j/(float)(num_pts+1); 
      if(det > 0) 
     { 
      POINTFLOAT nin; 
      nin = slerp2d(b_perp, a_perp, 1.0f-t); 
      nin.x *= w; 
      nin.y *= w; 

      nin.x += cur.x; 
      nin.y += cur.y; 

      round[j] = nin; 
     } 
      else 
     { 
      POINTFLOAT nin; 
      nin = slerp2d(a_perp, b_perp, t); 
      nin.x *= w; 
      nin.y *= w; 

      nin.x += cur.x; 
      nin.y += cur.y; 

      round[j] = nin; 
     } 
     } 

     for(size_t j = 0; j < round.size()-1; ++j) 
     { 

      OutlineVec.push_back(cur.x); 
      OutlineVec.push_back(cur.y); 


      if(det > 0) 
     { 
      OutlineVec.push_back(round[j + 1].x); 
      OutlineVec.push_back(round[j + 1].y); 
      OutlineVec.push_back(round[j].x); 
      OutlineVec.push_back(round[j].y); 
     } 
      else 
     { 

      OutlineVec.push_back(round[j].x); 
      OutlineVec.push_back(round[j].y); 

      OutlineVec.push_back(round[j + 1].x); 
      OutlineVec.push_back(round[j + 1].y); 
     } 
     } 
    } 

} 

POINTFLOAT multiply(const POINTFLOAT &a, float b) 
{ 
    POINTFLOAT result; 
    result.x = a.x * b; 
    result.y = a.y * b; 
    return result; 
} 

POINTFLOAT normalize(const POINTFLOAT &a) 
{ 
    return multiply(a, 1.0f/sqrt(a.x*a.x+a.y*a.y)); 
} 


POINTFLOAT slerp2d(const POINTFLOAT &v0, 
        const POINTFLOAT &v1, float t) 
{ 
    float dot = (v0.x * v1.x + v0.y * v1.y); 

    if(dot < -1.0f) dot = -1.0f; 
    if(dot > 1.0f) dot = 1.0f; 

    float theta_0 = acos(dot); 
    float theta = theta_0 * t; 

    POINTFLOAT v2; 
    v2.x = -v0.y; 
    v2.y = v0.x; 

    POINTFLOAT result; 
    result.x = v0.x * cos(theta) + v2.x * sin(theta); 
    result.y = v0.y * cos(theta) + v2.y * sin(theta); 

    return result; 
} 

我注意到,矢量绘图应用程序允许圆的开始和一个段结束的能力。我如何修改线条生成算法来舍入未连接线段的开始和结尾?

请参见下面的例子说明我的意思:

alt text http://img39.imageshack.us/img39/6029/capss.png

感谢

+0

您能否提供一些您希望未连接线段四舍五入的方式的视觉示例? – kriss 2010-07-16 23:25:41

+0

这不是我所做的吗?如果我画一条线,我希望它看起来像我的例子右边的那个,而不是左边的 – jmasterx 2010-07-16 23:33:07

回答

1

花了我一段时间了解slerp2d()是如何工作的,但我仍然可能会弄错它,但它让我感到你可以使用单位矢量,并且它垂直于末端,使用它们绘制两部分半球。

只要末端不符合,使用slerp2d(-b,b_perp,t);和slerp2d(-b,-b_perp,t); (b,b_perp,t)的开始(词条的顺序可能需要交换);和slerp2d(b,-b_perp,t);为了结束。

您可以再次避免计算round.back(),因为这仍然是P0(或者P1取决于确定值),而round.front()是您放在OutlineVec中的前一个P2或P3。计算内部尖角点可能对此有所帮助,因为它会删除其他点。

0

编辑
关于第二个想法济耶不会那么有用,因为它们会要求你添加额外的点,然后您需要区分哪些路径应绘制为直线,哪些应绘制为曲线。

从本质上讲,你需要具有以下原型功能:

void DrawRoundedRectangle(Rectangle rect, Angle angle); 

我的主要观点仍然认为,渲染的坐标为矩形的代码不需要修改,并且它是由渲染代码添加任何圆角。

我相信GDI +能够做到这一点。

如果我可能会问,你在开发什么平台,以及你在使用什么库? :)

原帖

的算法生成线可以保持大致相同。这是渲染代码需要将点连接为贝塞尔曲线而不是直线。

所以你基本上需要一个bezier渲染库。我在Windows上使用过GDI +。

+0

我有一个贝塞尔库,但是您能更具体地说明我应该如何使用贝塞尔来解决我的问题,谢谢 – jmasterx 2010-07-17 00:54:23

+0

使用OpenGL绘制组成我的多边形和轮廓的三角形 – jmasterx 2010-07-17 01:05:15

0

如果您使用的是X/Motif,那么在图形上下文中有一个这样的字段;不过,我忘记了它是什么,因为很久以前我最后一次使用它。

我在网上找不到任何有价值的东西。抱歉。 O'Reilly的书籍就这一点进行了很好的讨论。