2010-07-16 123 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的書籍就這一點進行了很好的討論。