2012-03-30 30 views
3

我使用DrawStorage()創建存儲(圖A)。爲了填充存儲我使用FillWater()。我想按照圖片C的0-100%(空滿)的水平填充存儲空間,但是當前輸出是從圖片B的FillWater()生成的。如何填充存儲以使其看起來像圖片C ?難的是如何填補存儲,這樣它看起來像3D(圖C)如何用C繪製3D管的下半部分#

很抱歉,如果我的英語不是很好。我希望你們是專家的幫助,謝謝。

output

protected override void OnPaint(PaintEventArgs pe) 
    { 
     base.OnPaint(pe); 
     Graphics g = pe.Graphics; 
     g.SmoothingMode = SmoothingMode.AntiAlias; 

     this.DrawBar(g, this.ForeColor); 
    } 


    private void DrawBar(Graphics g, Color foreColor) 
    { 
     bool outLine = this._outLineColor != Color.Transparent; 
     Rectangle bound = this.ClientRectangle; 
     bound.Inflate(-20, -20); 

     DrawStorage(g, bound, new Size(4, 10), Color.FromArgb(this.Alpha, foreColor), this.OutLineColor, outLine); 

     if (this.Value > this.Minimum && this.Value <= this.Maximum) 
     { 
      float barValue = bound.Height * ((this.Value - this.Minimum)/(this.Maximum - this.Minimum)); 
      RectangleF valueBound = RectangleF.FromLTRB(bound.Left, bound.Bottom - barValue, bound.Right, bound.Bottom); 

      FillWater(g, valueBound, new Size(4, 10), Color.FromArgb(this.Alpha, this.BarColor), this.OutLineColor, outLine); 

      if (this._showValue && valueBound.Height > 20) 
      { 
       g.SmoothingMode = SmoothingMode.AntiAlias; 
       StringFormat format = new StringFormat(); 
       format.Alignment = StringAlignment.Center; 
       format.LineAlignment = StringAlignment.Center; 
       g.DrawString(this._value.ToString("F2"), this.Font, Brushes.Black, valueBound, format); 
       format.Dispose(); 
      } 
     } 
    } 


    public static void DrawStorage(Graphics g, RectangleF front, SizeF depth, Color fillColor, Color borderColor, bool outLine) 
    { 
     if (front.Width <= 0 || front.Height <= 0) 
      return; 

     // Make Back Side Area 
     RectangleF aback = front; 

     // Make Depth 
     aback.X += depth.Width; 
     aback.Y -= depth.Height; 

     // Create Top and Bottom Plane. 
     RectangleF leftPlane; 
     RectangleF rightPlane; 

     // Create Graphics Object 
     GraphicsPath gp = new GraphicsPath(); 

     rightPlane = new RectangleF(front.Width, front.Y, front.X, front.Height); 
     leftPlane = new RectangleF(front.X, front.Y, front.X, front.Height); 

     // Brush 
     SolidBrush brush = new SolidBrush(fillColor); 

     // Border Pen 
     Pen borderPen = new Pen(borderColor); 

     /*************** 
     * LEFT * 
     * ************/ 
     // Make GP On Bottom 
     gp.AddEllipse(leftPlane); 

     // Get Bottom color 
     brush.Color = GetSideColor(fillColor, WallSide.Left); 

     // Fill Bottom Plane 
     g.FillPath(brush, gp); 

     // Shadow of the Body 
     FillCylinderShadow(g, front, gp, false); 

     // Check Draw Border 
     if (outLine) 
      g.DrawPath(borderPen, gp); 

     gp.Reset(); 
     gp.AddArc(rightPlane, 270, 180); 
     gp.AddArc(leftPlane, 90, -180); 
     gp.CloseFigure(); 

     /*************** 
     *  Body * 
     * ************/ 
     // Color For Body is real Fill Color. 
     brush.Color = fillColor; 

     // Fill Body 
     g.FillPath(brush, gp); 

     // Shadow of the Body 
     FillCylinderShadow(g, front, gp, true); 

     // Check Draw Border 
     if (outLine) 
      g.DrawPath(borderPen, gp); 

     /*************** 
     *  RIGHT  * 
     * ************/ 
     gp.Reset(); 
     gp.AddEllipse(rightPlane); 

     // Get Bottom color 
     brush.Color = GetSideColor(fillColor, WallSide.Back); 

     // Fill Top Plane 
     g.FillPath(brush, gp); 

     // Shadow of the Body 
     FillCylinderShadow(g, front, gp, true); 

     //Check Draw Border 
     if (outLine) 
      g.DrawPath(borderPen, gp); 

     // Dispose 
     gp.Dispose(); 
     brush.Dispose(); 
     borderPen.Dispose(); 
    } 


    public static void FillWater(Graphics g, RectangleF front, SizeF depth, Color fillColor, Color borderColor, bool outLine) 
    { 
     if (front.Width <= 0 || front.Height <= 0) 
      return; 

     // Make Back Side Area 
     RectangleF aback = front; 

     // Make Depth 
     aback.X += depth.Width; 
     aback.Y -= depth.Height; 

     // Create Top and Bottom Plane. 
     RectangleF leftPlane; 
     RectangleF rightPlane; 

     // Create Graphics Object 
     GraphicsPath gp = new GraphicsPath(); 

     rightPlane = new RectangleF(front.Width, front.Y, front.X, front.Height); 
     leftPlane = new RectangleF(front.X, front.Y, front.X, front.Height); 

     // Brush 
     SolidBrush brush = new SolidBrush(fillColor); 

     // Border Pen 
     Pen borderPen = new Pen(borderColor); 

     /*************** 
     * LEFT * 
     * ************/ 
     // Make GP On Bottom 
     gp.AddEllipse(leftPlane); 

     // Get Bottom color 
     brush.Color = GetSideColor(fillColor, WallSide.Left); 

     // Fill Bottom Plane 
     g.FillPath(brush, gp); 

     // Shadow of the Body 
     FillCylinderShadow(g, front, gp, false); 

     // Check Draw Border 
     if (outLine) 
      g.DrawPath(borderPen, gp); 

     gp.Reset(); 
     gp.AddArc(rightPlane, 270, 180); 
     gp.AddArc(leftPlane, 90, -180); 
     gp.CloseFigure(); 

     /*************** 
     *  Body * 
     * ************/ 
     // Color For Body is real Fill Color. 
     brush.Color = fillColor; 

     // Fill Body 
     g.FillPath(brush, gp); 

     // Shadow of the Body 
     FillCylinderShadow(g, front, gp, true); 

     // Check Draw Border 
     if (outLine) 
      g.DrawPath(borderPen, gp); 

     /*************** 
     *  RIGHT  * 
     * ************/ 
     gp.Reset(); 
     gp.AddEllipse(rightPlane); 

     // Get Bottom color 
     brush.Color = GetSideColor(fillColor, WallSide.Back); 

     // Fill Top Plane 
     g.FillPath(brush, gp); 

     // Shadow of the Body 
     FillCylinderShadow(g, front, gp, true); 

     //Check Draw Border 
     if (outLine) 
      g.DrawPath(borderPen, gp); 

     // Dispose 
     gp.Dispose(); 
     brush.Dispose(); 
     borderPen.Dispose(); 
    } 
+0

你總是可以採取簡單的方法和旋轉缸90度,並填補這種方式。 – Robaticus 2012-03-30 16:45:18

回答

1

我在創造使用繪製順序和剪裁3D效果的一些有限的成功。下面的代碼通常有效,但在填充接近0%或100%時有一些問題...我認爲可以修復。

3D cylinder

/// <summary> 
/// Calculate X coordinate on an ellipse 
/// </summary> 
/// <param name="width">Ellipse width</param> 
/// <param name="height">Ellipse height</param> 
/// <param name="y">Y ranging from 0 to height</param> 
/// <returns>X relative to the center of the ellipse</returns> 
/// 
static float EllipseCalculateX(float width, float height, float y) 
{ 
    if (y < 0 || y > height) 
    { 
     return 0; 
    } 

    y = y - (height/2f); 
    var a = width/2f; 
    var b = height/2f; 

    var x = (a * Math.Sqrt((b * b) - (y * y)))/b; 

    return (float)x; 
} 

protected override void OnPaint(PaintEventArgs e) 
{ 
    var g = e.Graphics; 

    var percent_full = PercentFull; 


    // length, width, and depth of the storage in pixels 
    // 
    var storage_length = 140f; 
    var storage_height = 80f;    
    var storage_depth = 15f; 


    var start = new PointF(80, 50); 

    var cylinder = new RectangleF(start.X, start.Y, storage_length, storage_height); 
    var left_cap = new RectangleF(cylinder.Left - (storage_depth/2f), cylinder.Top, storage_depth, storage_height); 
    var right_cap = new RectangleF(cylinder.Right - (storage_depth/2f), cylinder.Top, storage_depth, storage_height); 


    // relative x,y of the fill level on the "near" (front) side of the storage 
    // 
    var fill_near_y = storage_height * (percent_full/100f); 
    var fill_near_x = EllipseCalculateX(storage_depth, storage_height, fill_near_y); 

    // relative x,y of the fill level on the "far" (back) side of the storage 
    // y is offset slightly for the 3D effect 
    // 
    var fill_far_y = storage_height * (percent_full/100f) + (storage_depth/2f); 
    var fill_far_x = EllipseCalculateX(storage_depth, storage_height, fill_far_y); 


    // absolute x,y of the fill level on the left side (near and far) 
    // 
    var fill_left_far = new PointF(cylinder.Left - fill_far_x, cylinder.Bottom - fill_far_y); 
    var fill_left_near = new PointF(cylinder.Left + fill_near_x, cylinder.Bottom - fill_near_y); 

    // absolute x,y of the fill level on the right side (near and far) 
    // 
    var fill_right_far = new PointF(cylinder.Right - fill_far_x, cylinder.Bottom - fill_far_y); 
    var fill_right_near = new PointF(cylinder.Right + fill_near_x, cylinder.Bottom - fill_near_y); 

    // calculate the slope between the near and far levels 
    // 
    var slope = (fill_left_far.Y - fill_left_near.Y)/(fill_left_far.X - fill_left_near.X + 0.001f); 

    // build a clip path to be used in filling the left cap; its top is angled to match the 3D effect 
    // the first two points in the path have to be extended outside of the cap ellipse, or the fill will look wrong above 50% fill 
    // 
    var left_clip = new GraphicsPath(); 
    left_clip.AddPolygon(new PointF[] { 
     new PointF(left_cap.Left, fill_left_far.Y - (slope * (fill_left_far.X - left_cap.Left))), 
     new PointF(left_cap.Right, fill_left_near.Y + (slope * (left_cap.Right - fill_left_near.X))), 
     new PointF(left_cap.Right, left_cap.Bottom), 
     new PointF(left_cap.Left, left_cap.Bottom), 
    }); 

    // same for right cap 
    // 
    var right_clip = new GraphicsPath(); 
    right_clip.AddPolygon(new PointF[] { 
     new PointF(right_cap.Left, fill_right_far.Y - (slope * (fill_right_far.X - right_cap.Left))), 
     new PointF(right_cap.Right, fill_right_near.Y + (slope * (right_cap.Right - fill_right_near.X))), 
     new PointF(right_cap.Right, left_cap.Bottom), 
     new PointF(right_cap.Left, right_cap.Bottom), 
    }); 

    var outline = new Pen(Color.Black, 2f) { LineJoin = System.Drawing.Drawing2D.LineJoin.Bevel }; 

    // outline the top and bottom of the storage 
    // 
    g.DrawLine(outline, cylinder.Left, cylinder.Top, cylinder.Right, cylinder.Top); 
    g.DrawLine(outline, cylinder.Left, cylinder.Bottom, cylinder.Right, cylinder.Bottom); 


    // outline the right cap 
    // 
    g.DrawEllipse(outline, right_cap); 


    // outline and fill the right side 
    // 
    g.SetClip(right_clip); 
    g.DrawEllipse(outline, right_cap); 
    g.FillEllipse(Brushes.Orange, right_cap); 
    g.ResetClip(); 


    // fill in the center area 
    // 
    g.SetClip(RectangleF.FromLTRB(cylinder.Left, fill_left_near.Y, cylinder.Right, cylinder.Bottom)); 
    g.FillRectangle(Brushes.Orange, cylinder); 
    g.ResetClip(); 


    // fill left side 
    // 
    g.SetClip(left_clip); 
    g.FillEllipse(Brushes.Yellow, left_cap); 
    g.ResetClip(); 


    // outline and fill the surface 
    // 
    var surface = new[] { fill_left_near, fill_left_far, fill_right_far, fill_right_near, fill_left_near }; 
    g.DrawPolygon(outline, surface); 
    g.FillPolygon(Brushes.Yellow, surface); 


    // outline the left cap 
    // 
    g.DrawEllipse(outline, left_cap); 
} 
+0

這很好,謝謝你的幫助:) – 2012-03-31 11:06:00

0

庵以及實際的3D圖形實際上是在2D形狀的拼貼,這就是爲什麼你可以「看到」管例如兩端內。你可以通過這種方式達到目標。如果您在管道兩端的x%處繪製和絃(排列出右端,它應該與左邊的角度相同) 然後將它們合併起來,以便您有一個代表水面的平面,應該給你足夠的觀點來概述管道的填充部分和一些填充命令後的填充命令應該完成這項工作。

+0

我可以在圓柱體左側添加圓弧,問題是如何計算短軸(水的寬度),如果市長軸(水位)已知?變化不是線性的,因爲橢圓形表面。 – 2012-03-30 20:04:33

+0

您有一個投影角度,以使3D效果從管道底部畫出一條線(垂直半徑下半部分),然後在您的投影角度左右拖動,直到它截取橢圓的邊界。然後填寫,我想。很難說,我已經完成了管道和內容的三維模型,然後進行投影。在某些方面很硬,但會讓你遇到的問題變得微不足道。 – 2012-03-31 19:21:19