2012-03-30 4 views
3

Ich erstellen Speicher (Bild A) mit DrawStorage(). Um den Speicher zu füllen benutze ich FillWater(). Ich wollte Speicher mit Wasser entsprechend dem Level von 0-100% (leer-voll) wie Bild C füllen, aber die aktuelle Ausgabe wird von FillWater() wie das Bild B erzeugt. Wie man den Speicher füllt, um wie das Bild auszusehen C? Die Schwierigkeit besteht darin, den Speicher so zu füllen, dass er wie 3D aussieht (Bild C)Wie zeichne untere Hälfte einer 3D-Röhre mit C#

Sorry, wenn mein Englisch nicht gut ist. Ich hoffe auf Hilfe von Ihnen, die Experten sind, Danke.

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

Man kann immer den einfachen Weg nehmen und einen Zylinder 90 Grad drehen und es auf diese Weise füllen. – Robaticus

Antwort

1

Ich hatte einen begrenzten Erfolg beim Erstellen des 3D-Effekts mit Zeichenreihenfolge und Clipping. Der Code unten funktioniert im Allgemeinen, aber hat einige Probleme, wenn die Füllung nahe 0% oder 100% ist ... Ich denke, dass das behoben werden kann.

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

Es ist toll, Danke für die Hilfe :) –

0

Um auch tatsächlich Ihre 3D-Zeichnung in der Tat ist eine Collage von 2D-Formen, weshalb Sie ist „sehen“ kann innerhalb der beiden Enden des Rohres zum Beispiel. Sie können wahrscheinlich auf diese Weise dorthin kommen. Wenn Sie den Akkord bei x% an beiden Enden des Rohrs zeichnen (das rechte Ende aussortieren, sollte es den gleichen Winkel haben wie das linke) Dann verbinden Sie sie, so dass Sie eine Ebene haben, die die Oberfläche des Wassers darstellt sollte Ihnen genügend Punkte geben, um den gefüllten Teil der Pipe zu umreißen und ein paar Fill-Befehle, sobald Sie die gefüllten sind, sollten die Aufgabe erledigen.

+0

Ich kann den Bogen auf der linken Seite des Zylinders hinzufügen, das Problem ist, wie Nebenachse (Breite des Wassers) zu berechnen, wenn Bürgermeister Achse (Wasserstand) bekannt ist? Änderungen sind nicht linear, weil ellipsenförmige Oberfläche. –

+0

Sie haben einen Projektionswinkel, um dem 3D-Effekt eine Linie vom unteren Rand des Rohrs (vertikaler Radius untere Hälfte) zu ziehen. Ziehen Sie dann bei Ihrem Projektionswinkel nach links und rechts, bis er die Grenze der Ellipse schneidet. Dann fülle, denke ich. Schwer zu sagen, ich hätte ein 3D-Modell der Pfeife und des Inhalts erstellt und dann projiziert. Harddeer in gewisser Weise, aber macht die Probleme, die Sie laufen, in trivial. –