2012-03-30 12 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

答えて

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

それは素晴らしいです、助けてくれてありがとう:) –

0

うーんだけでなく、実際にあなたの3D描​​画が実際にあなたは、インスタンスのためのパイプの両端の内側に「見る」ことができる理由ですが、2D形状のコラージュ、です。あなたはおそらくそこにこのような心を得ることができます。コードの両端をx%で描画すると(右端をソートすると、左端と同じ角度になる) 次に、水面を表す平面を持つようにそれらを結合します。パイプの塗りつぶされた部分を概説するのに十分なポイントを与えてください。

+0

円柱の左側に円弧を付けることができますが、市長(水位)が分かっていれば短軸(水の幅)を計算する方法は問題ですか?楕円形のサーフェスなので、変更は線形ではありません。 –

+0

3Dエフェクトにパイプの下端(垂直半径下半分)から線を引いてから、投射角度が楕円の境界を横切るまで左右に描きます。それから、私は思う。難しいと言えば、私はパイプとコンテンツの3Dモデルを完成し、それを投影していたでしょう。いくつかの点でHarddeerが、あなたが実行している問題を簡単にしています。 –

関連する問題