2012-08-04 96 views
2

我正在使用Juce庫來顯示圖形的項目。 到目前爲止,我一直在使用庫的API函數來生成線性和徑向漸變,但是這是該庫支持的唯一兩種類型的漸變。我現在需要生成一種不同類型的漸變,它遵循規則凸多邊形的形狀。這裏的關鍵詞是REGULAR,意思是一個多邊形,所有的邊具有相同的長度,並且所有的頂點都位於一個圓上。多邊形梯度

對於一個五邊形的情況下,這裏是一個圖片,以更好地顯示結果我想獲得: http://www.filterforge.com/wiki/index.php/Polygonal_Gradient

對於我的申請,我希望能夠與任意數量的指定多邊形漸變邊緣。 (五角形,六角形,八角形等)

由於API的限制,我能夠產生所需結果的唯一方法是逐個像素填充表面矩陣,數學計算R的值, G,B,每個像素的分量。

下面是代碼我迄今爲止:

void render_surface(unsigned char *surface_data, 
        int width, int height, int linestride, 
        int num_vertices, t_rgba *color1, t_rgba *color2) 
{ 
    const double center_x = 0.5 * width; 
    const double center_y = 0.5 * height; 
    const double radius = 0.5 * MIN(width, height); 
    int x, y; 

    for (y = height; --y >= 0;) { 

     uint32_t *line = (uint32_t *)data; 
     data += linestride; 

     const double dy = y - center_y; 

     for (x = width; --x >= 0;) { 

      const double dx = x - center_x; 

      double rho = hypot(dx, dy); 
      rho /= radius; // normalize radius 

      // constrain 
      rho = CLIP(rho, 0.0, 1.0); 

      // interpolate 
      double a = color2->alpha + (color1->alpha - color2->alpha) * rho; 
      double r = color2->red + (color1->red - color2->red ) * rho; 
      double g = color2->green + (color1->green - color2->green) * rho; 
      double b = color2->blue + (color1->blue - color2->blue) * rho; 

      // premultiply alpha 
      r *= a; 
      g *= a; 
      b *= a; 

#if LITTLE_ENDIAN 
      *line++ = ((unsigned int)((256.0 * (1.0 - DBL_EPSILON)) * a) << 24) // alpha 
        | ((unsigned int)((256.0 * (1.0 - DBL_EPSILON)) * r) << 16) // red 
        | ((unsigned int)((256.0 * (1.0 - DBL_EPSILON)) * g) << 8) // green 
        | (unsigned int)((256.0 * (1.0 - DBL_EPSILON)) * b);  // blue 
#else 
      *line++ = ((unsigned int)((256.0 * (1.0 - DBL_EPSILON)) * b) << 24) // blue 
        | ((unsigned int)((256.0 * (1.0 - DBL_EPSILON)) * g) << 16) // green 
        | ((unsigned int)((256.0 * (1.0 - DBL_EPSILON)) * r) << 8) // red 
        | (unsigned int)((256.0 * (1.0 - DBL_EPSILON)) * a);  // alpha 
#endif 
     } 
    } 
} 

以上代碼生成的徑向梯度,同一類型的梯度我可以產生利用一個API函數。不過,這似乎是解決問題的一個很好的起點。

surface_data - 是代表紅色,綠色,藍色和Alpha分量像素強度的8位值矩陣。

num_vertices - 是我們希望我們的多邊形漸變具有的頂點數(在單個圓上等間距)。

color1 - 漸變的起始顏色。

color2 - 漸變的結束顏色。

我想知道如何以相同的方式填充曲面,創建多邊形漸變而不是徑向。

感謝您的任何幫助。

  • 路易吉

再思考這個問題有點...... 如果我們考慮到我們的座標系中的多邊形的中心的原點,把它歸結爲找到一個方程使得對任意輸入點以笛卡爾座標表示,輸出是距離多邊形最近邊的距離。

我的直覺告訴我,一定有某種封閉形式的解決方案,因爲:

了一圈,

rho = sqrt(dx*dx + dy*dy); 

讓我們從圓的中心的徑向距離,這可能是視爲具有無限邊的多邊形。

對於方形,

fmax(fabs(dx), fabs(dy)); 

使我們從方形,這可以被視爲具有4個邊的多邊形的最近側的切比雪夫距離。

所以,我認爲這兩個公式的某種組合應該給中介案例,這將解決最初的問題。

我完全不考慮這些方面嗎?

  • 路易吉

回答

1

這大約是我怎麼想靠近它......

  • 將在原點 'O' 的多邊形的中心。
  • 對於正多邊形的給定段內的給定的點「P」,讓通過 「O」 &「P」的線是「線1」和
  • 讓線通過 的含有外邊緣多邊形段爲'Line2'
  • 找到這兩行的intesection點'IP'。

現在,P處的色彩分數由P與原點的距離相對於IP到原點的距離來定義。

enter image description here

編輯:我實現了上述算法,這是輸出...

enter image description here

EDIT2: 這裏的(德爾福)代碼

const 
    vertical: TFloat = 3.4e38; 

function Slope(const pt1, pt2: TFloatPoint): single; 
begin 
    if (pt1.X = pt2.X) then result := vertical 
    else result := (pt2.Y - pt1.Y)/(pt2.X - pt1.X); 
end; 
//--------------------------------------------------------------------------- 

procedure GetLine(const pt1, pt2: TFloatPoint; out m, b: TFloat); 
begin 
    m := Slope(pt1, pt2); 
    if m = vertical then 
    b := pt1.X else 
    b := pt1.Y - m * pt1.X; 
end; 
//--------------------------------------------------------------------------- 

function GradientColor(const clr1, clr2: TColor32; fraction: TFloat): TColor32; 
begin 
    if fraction <= 0 then result := clr1 
    else if fraction >= 1 then result := clr2 
    else 
    begin 
    TColor32Entry(result).B := 
     trunc(TColor32Entry(clr2).B * fraction + TColor32Entry(clr1).B * (1-fraction)); 
    TColor32Entry(result).G := 
     trunc(TColor32Entry(clr2).G * fraction + TColor32Entry(clr1).G * (1-fraction)); 
    TColor32Entry(result).R := 
     trunc(TColor32Entry(clr2).R * fraction + TColor32Entry(clr1).R * (1-fraction)); 
    TColor32Entry(result).A := 
     trunc(TColor32Entry(clr2).A * fraction + TColor32Entry(clr1).A * (1-fraction)); 
    end; 
end; 
//--------------------------------------------------------------------------- 

function PointInTriangle(const pt, tr1, tr2, tr3: TFloatPoint): boolean; 
begin 
    result := false; 
    if ((((tr1.Y <= pt.Y) and (pt.Y < tr3.Y)) or 
    ((tr3.Y <= pt.Y) and (pt.Y < tr1.Y))) and 
    (pt.X < (tr3.X - tr1.X) * (pt.Y - tr1.Y)/
    (tr3.Y - tr1.Y) + tr1.X)) then result := not result; 
    if ((((tr2.Y <= pt.Y) and (pt.Y < tr1.Y)) or 
    ((tr1.Y <= pt.Y) and (pt.Y < tr2.Y))) and 
    (pt.X < (tr1.X - tr2.X) * (pt.Y - tr2.Y)/
    (tr1.Y - tr2.Y) + tr2.X)) then result := not result; 
    if ((((tr3.Y <= pt.Y) and (pt.Y < tr2.Y)) or 
    ((tr2.Y <= pt.Y) and (pt.Y < tr3.Y))) and 
    (pt.X < (tr2.X - tr3.X) * (pt.Y - tr3.Y)/
    (tr2.Y - tr3.Y) + tr3.X)) then result := not result; 
end; 
//--------------------------------------------------------------------------- 

function GetSegmentIndex(vertex: TFloatPoint; vertices: TArrayOfFloatPoint): integer; 
var 
    i, highI: integer; 
    prev: TFloatPoint; 
const 
    origin: TFloatPoint = (X: 0; Y: 0); 
begin 
    highI := high(vertices); 
    prev := vertices[highI]; 
    result := -1; 
    for i := 0 to highI do 
    begin 
    if PointInTriangle(vertex, origin, prev, vertices[i]) then 
    begin 
     result := i; 
     break; 
    end; 
    prev := vertices[i]; 
    end; 
end; 
//--------------------------------------------------------------------------- 

procedure RegularPolygonFill(bmp: TBitmap32; const origin: TPoint; 
    radius: TFloat; vertexCount: integer; InnerColor, OuterColor: TColor32); 
var 
    i,j,d,q: integer; 
    dist1,dist2: TFloat; 
    vert, intersetPt: TFloatPoint; 
    verts: TArrayOfFloatPoint; 
    edgeMs, edgeBs: TArrayOfFloat; 
    angle, angleDiff, m, b: TFloat; 
    sinAngle, cosAngle: extended; 
const 
    orig: TFloatPoint = (X: 0; Y: 0); 
begin 
    if vertexCount < 3 then exit; 
    setlength(verts, vertexCount); 
    setlength(edgeMs, vertexCount); //edge slopes (ie y = M*x +b) 
    setlength(edgeBs, vertexCount); //edge offsets (ie y = m*x +B) 
    angleDiff := pi *2/vertexCount; 
    angle := angleDiff; 
    vert.X := radius; //vert used here as prev vertex 
    vert.Y := 0; 
    for i := 0 to vertexCount -1 do 
    begin 
    SinCos(angle, sinAngle, cosAngle); 
    verts[i].X := cosAngle * radius; 
    verts[i].Y := sinAngle * radius; 
    GetLine(vert, verts[i], edgeMs[i], edgeBs[i]); 
    angle := angle + angleDiff; 
    vert := verts[i]; 
    end; 

    d := floor(radius); 
    for i := -d to d do 
    for j := -d to d do 
    begin 
     vert := FloatPoint(i,j); 
     GetLine(orig, vert, m, b); 
     q := GetSegmentIndex(vert, verts); 
     if q < 0 then continue; 
     //simultaneous equations to find intersection ... 
     //y = m * x + b; y = edgeMs[q]* x + edgeBs[q]; 
     //edgeMs[q]* x + edgeBs[q] = m * x + b; 
     //(edgeMs[q] - m) * x = b - edgeBs[q] 
     //x = (b - edgeBs[q])/(edgeMs[q] - m) 
     if m = vertical then 
     begin 
     intersetPt.X := b; 
     intersetPt.Y := edgeMs[q]* intersetPt.X + edgeBs[q]; 
     end 
     else if edgeMs[q] = vertical then 
     begin 
     intersetPt.X := edgeBs[q]; 
     intersetPt.Y := m* intersetPt.X + b; 
     end else 
     begin 
     intersetPt.X := (b - edgeBs[q])/(edgeMs[q] - m); 
     intersetPt.Y := m * intersetPt.X + b; 
     end; 

     //get distances from origin of vert and intersetPt ... 
     dist1 := sqrt(vert.X*vert.X + vert.Y*vert.Y); 
     dist2 := sqrt(intersetPt.X*intersetPt.X + intersetPt.Y*intersetPt.Y); 

     bmp.Pixel[i + origin.X, j + origin.Y] := 
     GradientColor(InnerColor, OuterColor, dist1/dist2); 
    end; 
end; 
+0

是的,我沒有試圖直接實施你的解決方案,但至少在理論上它應該工作。但是它涉及相當多的計算。我希望得到某種封閉的形式方程,更簡單,更簡潔。等式的輸入將是笛卡爾座標中的一個點,輸出是距最近一側的距離。 – 2012-08-05 12:50:57

+0

對於邊數爲偶數的多邊形,只需計算多邊形一半的像素,然後在中線上反射顏色,就可以顯着加快這一速度。同樣,對於具有4個邊的倍數的多邊形,只需計算多邊形的四分之一像素,然後在水平和垂直中線上鏡像像素顏色,就可以加快速度。 – 2012-08-05 12:57:36

+0

是的,它看起來完全正確。謝謝。你介意分享你的實現嗎?我會非常感興趣的看到一些實際的代碼。 (語言無所謂) – 2012-08-05 13:22:55