rozina
rozina

Reputation: 4232

Drawing a straight line from points

I am trying to make a simple chart application and I have stumbled upon a problem. I have an array of points which represent a 45 degree line (y = x) of 100 points. I then try to draw the line by converting each point's value to its pixel value and then draw lines from one point to another using graphic.DrawLines().

The the basic code:

struct MyPoint
{
    double X { set; get; }
    double Y { set; get; }
}

List<MyPoint> Values;
List<System.Drawing.Point> Points;

for (int i = 0; i < Values.Count; i++)
    {
        // convert point value to pixel coordinate
        int x = (int)((Values[i].X - MinimumX) * (double)Control.Width / (MaximumX - MinimumX) + 0.5);
        int y = (int)((Values[i].Y - MinimumY) * (double)Control.Height / (MaximumY - MinimumY)) + 0.5;

        Points.Add(new System.Drawing.Point(x, y));
    }

    graphic.DrawLines(pen, Points.ToArray());

Note: MaximumX and MaximumY are 100 (number of points) and minimums are 0.

The thing is that this kind of line is not very straight :) So my question is, how would I draw a straight line through those points, so that it would look the same as

graphic.DrawLine(pen, 0, 0, Control.Width-1, Control.Height-1);

Thanks in advance!

Upvotes: 3

Views: 5986

Answers (4)

HTU
HTU

Reputation: 1034

here is my solution:

 System.Drawing.Pen myPen;
 myPen = new System.Drawing.Pen(System.Drawing.Color.Red);
 System.Drawing.Graphics formGraphics = this.CreateGraphics();
 formGraphics.DrawLine(myPen, 0, 0, 200, 200);
 myPen.Dispose();
 formGraphics.Dispose();

Upvotes: 0

John Alexiou
John Alexiou

Reputation: 29244

Since you are doing a graph, I have a basic framework for fitting values within a control like this example:

Form1

public struct MyPoint
{
    public double X { set; get; }
    public double Y { set; get; }

    public PointF ToPoint()
    {
        return new PointF((float)X, (float)Y);
    }
}

/// <summary>
/// For http://stackoverflow.com/questions/19459519/drawing-a-straight-line-from-points
/// </summary>
public partial class Form1 : Form
{
    List<MyPoint> points;
    public Form1()
    {
        InitializeComponent();

        // Initialize points to draw
        points=new List<MyPoint>(100);
        for (int i=0; i<=100; i++)
        {
            double θ=2*Math.PI*(i/100.0);
            double x=(1+θ/Math.PI)*Math.Cos(θ);
            double y=(1+θ/Math.PI)*Math.Sin(θ);
            points.Add(new MyPoint() { X=x, Y=y });
        }
    }

    private void pictureBox1_Paint(object sender, PaintEventArgs e)
    {
        // smooth graphics
        e.Graphics.SmoothingMode=SmoothingMode.AntiAlias;            
        // set margins inside the control client area in pixels
        var margin=new System.Drawing.Printing.Margins(16, 16, 16, 16);
        // set the domain of (x,y) values
        var range=new RectangleF(-3, -3, 6, 6);
        // scale graphics
        ScaleGraphics(e.Graphics, pictureBox1, range, margin);            
        // convert points to pixels
        PointF[] pixels=points.Select((v) => v.ToPoint()).ToArray();
        // draw arrow axes
        using (var pen=new Pen(Color.Black, 0))
        {
            pen.EndCap=System.Drawing.Drawing2D.LineCap.ArrowAnchor;
            e.Graphics.DrawLine(pen, range.Left, 0.0f, range.Right, 0.0f);
            e.Graphics.DrawLine(pen, 0.0f, range.Top, 0.0f, range.Bottom);
        }
        // draw bounding rectangle (on margin)
        using (var pen=new Pen(Color.LightGray, 0))
        {
            pen.DashStyle=DashStyle.Dash;
            e.Graphics.DrawRectangle(pen, Rectangle.Round(range));
        }
        // draw curve
        using (var pen = new Pen(Color.Blue, 0))
        {                
            //e.Graphics.DrawLines(pen, pixels);
            e.Graphics.DrawCurve(pen, pixels);
        }            
    }
    /// <summary>
    /// Scales the Graphics to fit a Control, given a domain of x,y values and side margins in pixels
    /// </summary>
    /// <param name="g">The Graphics object</param>
    /// <param name="control">The Control</param>
    /// <param name="domain">The value domain</param>
    /// <param name="margin">The margin</param>
    void ScaleGraphics(Graphics g, Control control, RectangleF domain, Margins margin)
    {
        // Find the drawable area in pixels (control-margins)
        int W=control.Width-margin.Left-margin.Right;
        int H=control.Height-margin.Bottom-margin.Top;
        // Ensure drawable area is at least 1 pixel wide
        W=Math.Max(1, W);
        H=Math.Max(1, H);
        // Find the origin (0,0) in pixels
        float OX=margin.Left-W*(domain.Left/domain.Width);
        float OY=margin.Top+H*(1+domain.Top/domain.Height);
        // Find the scale to fit the control
        float SX=W/domain.Width;
        float SY=H/domain.Height;
        // Transform the Graphics
        g.TranslateTransform(OX, OY);
        g.ScaleTransform(SX, -SY);
    }

    private void pictureBox1_SizeChanged(object sender, EventArgs e)
    {
        // redraw on resize
        pictureBox1.Refresh();
    }
}

Upvotes: 2

Nikita
Nikita

Reputation: 422

You probably need something like this:

private void DrawLine()
{
    int xInitial = 0, yInitial = 0, xFinal = Control.Width - 1, yFinal = Control.Height - 1;
    int dx = xFinal - xInitial, dy = yFinal - yInitial, steps, k, xf, yf;
    float xIncrement, yIncrement, x = xInitial, y = yInitial;

    if (Math.Abs(dx) > Math.Abs(dy))
        steps = Math.Abs(dx);
    else 
        steps = Math.Abs(dy);

    xIncrement = dx / (float)steps;
    yIncrement = dy / (float)steps;

    for (k = 0; k < steps; k++)
    {
        x += xIncrement;
        xf = (int)x;
        y += yIncrement;
        yf = (int)y;

        Points.Add(new System.Drawing.Point(xf, yf));
    }
    graphic.DrawLines(pen, Points.ToArray());
}

For more details see: Bresenham's line algorithm

If your points already calculated correctly by algorithm, than simple:

int x = (int) Values[i].X;
int y = (int) Values[i].Y;

Should be enough.

Upvotes: 1

user2880486
user2880486

Reputation: 1108

I think the problem is because your points are Integers. Precisions are lost.

Trying using

float x = (float)((Values[i] ...
List<System.Drawing.PointF> PointFs;

instead of

int x = (int)((Values[i] ...
List<System.Drawing.Point> Points;

Upvotes: 1

Related Questions