I've written a program that calculates the line of best fit (intercept/slope) given several input values from the user. I've plotted each of the individual values, however unsure of the code to plot the line given the slope and y-intercept.
This is the slope:
double m = ( aXY.Sum() -
((levels.Sum() * scores.Sum()) / 5)) / (newaX.Sum() - ((powLevels) / 5));
The Intercept
double b = meanY - (m * meanX);
Plotting of points
for (int i = 0; i < levels.GetLength(0); i++)
{
chart1.Series["Series1"].Points
.AddXY(levels.GetValue(i), scores.ToArray().GetValue(i));
}
Any ideas? I am by no means an expert and getting this far took a fair bit of experimenting..
Assuming your data are plotted as a scattergraph using the ChartType.Points the simplest way to add a line is to add one extra Series with ChartType.Line and set two points there.
There are other ways to create a line on a Chart, like drawing it or creating a LineAnnotation, but they are much more complicated!
Following this example to the letter here is an implementation:
Note that after creating the series for the line of best fit the thing you were looking for are just the last two lines..:
private void button1_Click(object sender, EventArgs e)
{
// create TWO series!
chart1.Series.Clear();
chart1.Series.Add("Data");
chart1.Series.Add("Line of best fit");
chart1.Series[0].ChartType = SeriesChartType.Point;
chart1.Series[1].ChartType = SeriesChartType.Line;
List<int> levels = new List<int>() { 8, 2, 11, 6, 5, 4, 12, 9, 6, 1};
List<int> scores = new List<int>() { 3, 10, 3, 6, 8, 12, 1, 4, 9, 14};
double minX = levels.ToList().Min();
double maxX = levels.ToList().Max();
double meanX = 1f * levels.Sum() / levels.Count;
double meanY = 1f * scores.Sum() / scores.Count;
double st = 0;
double sb = 0;
for (int i = 0; i < levels.Count; i++ )
{
st += (levels[i] - meanX) * (scores[i] - meanY);
sb += (levels[i] - meanX) * (levels[i] - meanX);
}
double slope = st / sb;
double y0 = meanY - slope * meanX; // y-intercept or y-crossing
for (int i = 0; i < levels.Count; i++)
{
chart1.Series[0].Points.AddXY(levels[i], scores[i]);
}
// this is the part that creates the line of best fit:
chart1.Series[1].Points.AddXY(minX, y0 + minX * slope);
chart1.Series[1].Points.AddXY(maxX, y0 + maxX * slope);
}
If you want to you can add the first line point right at the y-axis:
chart1.Series[1].Points.AddXY(0, y0 );
In this case you may want to set the minimum x-values shown in the chart to prevent it from including -1, maybe like this:
chart1.ChartAreas[0].AxisX.Minimum = minX - 1;
Related
I am creating One Cross Platform Application in Xamarin Forms and try to draw lines from 10 to -10 using below code. But the problem is lines are drawn from 10 to 0 only. Why this is happening I don't have any Idea.
int margin = 20;
int steps = 20;
float start = margin;
float end = width - margin;
float dHeigth = heigth - (margin * 4);
float hStep = dHeigth / Convert.ToSingle(steps);
float textMargin = 30;
// draw the line
for (int i = 10; i >= -10; i--)
{
float xpoint = i * hStep + margin;
if (i.IsOdd())
{
canvas.DrawLine(start + textMargin, xpoint, end, xpoint, LineWhitePaint);
}
else
{
decimal dText = 0;
canvas.DrawLine(start + textMargin, xpoint, end, xpoint, LineGreyPaint);
if (i < 0)
dText = i;
else
dText = (10 - i);
string txt = dText.ToString();
canvas.DrawText(txt, start + margin, xpoint + 15, TextStyleFillPaintX);
}
}
I am attaching screen shot of that
For the positive lines, you are drawing 10 - i, which yields 0 for the first iteration, 2 for the third and so on. Regarding this, you can see, that you are beginning to draw the lines from the middle of the canvas. The tenth iteration will draw the topmost line (the one with the 10). Further lines are drawn, but not on the screen.
You can see this, too, when you are writing xPoint to the debug output. As i gets negative, xPoint will, too. To fix this, you'll have to offset xPoint to always draw on screen
float xpoint = i * hStep + margin + steps / 2 * hStep;
Alternatively, you could loop from 20 to 0 and change how the text is generated.
for (int i = 20; i >= 0; i--)
{
var xPoint = i * hStep + margin;
// ...
var displayedText = GetDisplayedText(i, steps);
// ...
}
string GetDisplayedText(int i, int steps)
{
var displayedValue = i > steps / 2
? steps - i
: -i - steps / 2; // I think this should be the formula
return displayedValue.ToString();
}
Remarks: It would even better to encapsulate the concept of the lines, to separate their calculation from draawing them. You could create a factory that generates the correct line based on the index and the number of steps and then only iterate over the Line objects, and draw them by passing the canvas. This would make your code way cleaner and neater.
UPDATE
Since we have been able to clarify the requirements, I will give another shot.
First of all, I'd define methods to transform graph coordinates to canvas coordinates
private SKPoint ToCanvasCoordinates(SKPoint graphCoordinates)
{
var x = Margin + TextMargin + (_canvas.Width - 2*Margin - TextMargin)*graphCoordinates.X;
var y = (MaxY - graphCoordinates.Y)*(_canvas.Height - 2 * Margin)/(MaxY - MinY) + Margin;
return new SKPoint(x,y);
}
private SKPoint GetLegendCoordinates(int i)
{
var x = Margin;
var y = (MaxY - graphCoordinates.Y)*(_canvas.Height - 2 * Margin)/(MaxY - MinY) + Margin + 15;
return new SKPoint(x,y);
}
_canvas is a private member field in this case, Margin, MaxY and MinY are properties. I've assumed the min of x being 0 and the max bein 1.
Now you can draw your lines like
for(int i = -1; i <= 10; i++)
{
var lineStart = ToCanvasCoordinates(new SKPoint(0, i));
var lineEnd = ToCanvasCoordinates(new SKPoint(1, i));
canvas.DrawLine(lineStart, lineEnd, LineGreyPaint);
var textPosition = GetLegendCoordinates(i);
canvas.DrawText(i.ToString(), textPosition, TextStyleFillPaintX);
}
Furthermore, if you'd like to draw a line between two of the grid lines, you can use the following methods
private void DrawDataLine(SKPoint start, SKPoint end, SKPaint paint)
{
var startTransformed = ToCanvasCoordinates(start);
var endTransformed = ToCanvasCoordinates(end);
_canvas.DrawLine(startTransformed, endTransformed, paint);
}
private void DrawData(SKPaint paint)
{
for(int i=1; i<_data.Length; i++)
{
DrawDataLine(new SKPoint(data[i-1].X, data[i-1].Y), new SKPoint(data[i].X, data[i].Y)); // given that the objects in _data have the properties X and Y
}
}
I have a chart control that I use to show sound pressure lines.
So the X axis is
31.5 63 125 250 500 1000 2000 4000 and 8000.
I set the chart logarithmic on and the log base to 10.
But I'm not able to show all these labels on the axis, it shows 31.5 315 and 3150 only.
Tried to put interval to 1 but no luck.
Can anyone help me?
To make CustomLabels show up on your axis you need to create them with at least these three properties:
Text
FromPosition
ToPosition
Here is an example:
private void button4_Click(object sender, EventArgs e)
{
Series S2 = chart1.Series.Add("Series2");
ChartArea CA = chart1.ChartAreas[0];
CA.AxisY.IsLogarithmic = true;
List<double> fr = new List<double>();
for (int i = 3; i < 18; i++ )
{
fr.Add(Math.Pow(2, 1f * i / 2));
}
for (int i = 1; i < fr.Count; i+=2)
{
CustomLabel cl = new CustomLabel();
cl.FromPosition = fr[i - 1];
cl.ToPosition = fr[i + 1];
cl.Text = fr[i] + " Hz";
CA.AxisY.CustomLabels.Add(cl);
}
for (int i = 1; i < 60; i++)
{
chart1.Series[0].Points.AddXY(i, Math.Pow(2, i));
chart1.Series[1].Points.AddXY(i, i * i);
}
}
Note that for best precision you should use FromPositions and ToPositions that don't fall on the Labels but right between. So I skip every other step in the list of frequency steps for the displayed Labels and use them instead for their FromPositions and ToPositions.
Here is a peace of code which draws 1/2/3/4 (depends on remarks) charts:
private void button1_Click(object sender, EventArgs e)
{
List<int> queue = new List<int>();
queue.Add(1); queue.Add(2); queue.Add(3); queue.Add(4);
chart1.ChartAreas.Add(queue[0].ToString());
chart1.ChartAreas.Add(queue[1].ToString());
chart1.ChartAreas.Add(queue[2].ToString());
chart1.ChartAreas.Add(queue[3].ToString());
chart1.Series.Add("test1");
chart1.Series.Add("test2");
chart1.Series.Add("test3");
chart1.Series.Add("test4");
chart1.Series["test1"].ChartArea = "1";
chart1.Series["test2"].ChartArea = "2";
chart1.Series["test3"].ChartArea = "3";
chart1.Series["test4"].ChartArea = "4";
Random rdn = new Random();
for (int i = 0; i < 50; i++)
{
chart1.Series["test1"].Points.AddXY(rdn.Next(0, 10), rdn.Next(0, 10));
chart1.Series["test2"].Points.AddXY(rdn.Next(0, 10), rdn.Next(0, 10));
chart1.Series["test3"].Points.AddXY(rdn.Next(0, 10), rdn.Next(0, 10));
chart1.Series["test4"].Points.AddXY(rdn.Next(0, 10), rdn.Next(0, 10));
}
chart1.Series["test1"].ChartType = SeriesChartType.FastLine;
chart1.Series["test2"].ChartType = SeriesChartType.FastLine;
chart1.Series["test3"].ChartType = SeriesChartType.FastLine;
chart1.Series["test4"].ChartType = SeriesChartType.FastLine;
}
If I draw two or three charts it appears horizontally something like:
............
............
or
............
............
............
When I add fourth chartarea it starts create second "column"
............ ............
............ ............
What to do to force layout with one column ? I have found "Position" property but couldn't find the way how to use it correctly :(
I think all the alignment properties actually are more about data aligning than about the areas themselves..
Looks like the default Position = Auto will win with its own ideas about how to use the space best, until you switch it off; so I believe that you have to set the Positions of the ChartAreas in code. Here is an example to play with:
float dist = 1f;
float h = 23f;
float w = 80f;
CA1.Position = new ElementPosition(dist, dist * 2 + h * 0, w, h);
CA2.Position = new ElementPosition(dist, dist * 3 + h * 1, w, h);
CA3.Position = new ElementPosition(dist, dist * 4 + h * 2, w, h);
CA4.Position = new ElementPosition(dist, dist * 5 + h * 3, w, h);
The four numbers of an ElementPosition are floats that hold percentages (!) of the total chart area. I have allowed a little distance and set the ChartAreas to 23% height and 80% width.
The good news is that these numbers will hold during resize..
Here is a screenshot (without data):
Isn't is strange that these things are so very hard to find out? (This is my 3rd try at it..)
I have stored numerical data in lists with its coordinates (xValues, yValues), and if I want to compare (add, subtract, divide...) that set of data to another I have to be aware of that I can't compare if the xValues don't match (because there is nothing to compare with). So I need to interpolate linearly between the "missing" xValues, that actually exist in the other set and generate new points. Please check this picture:
The cyan squares on the red line represent the stored points (xValues2), and (generally) they won't match the other's set xValues (xValues1). The two squares on the green line are examples of the desired generated points. With them I can work with this two graphs without problem.
For linear interpolation It's pretty straightforward: If I have two points (x0,y0) and (x1,y1) and I want to add a new point between them given a "x2":
y2=y0+(x2-x0)*(y1-y0)/(x1-x0)
To make this work I think I have to implement something like this:
Create new lists (xValuesNew, yValuesNew).
Make a union between xValues1 and xValues2 (xValuesNew).
Check what are the differences between the original xValues1 and the xValuesNew.
For each new value found generate the "y" using the formula written above.
Put that 4 steps in a method and use it again but now with the set2.
I've been on this all day, trying to find an easy solution, maybe using Linq or lambda expressions but I'm not used to work with them and my lack of knowledge on that topics ishuge. Note that this operation will be made pretty often so I have to make it not too heavy. I've thought that it will be a good idea to generate a new list instead inserting points in the middle of the original for that reason.
Please if someone could guide me a little bit or tell me if there is a math library actually doing this would be great. Thank you.
EDIT: Sorry if I haven't explained me properly.
Here I have an example (done in Excel):
Note that I can't directly add together Series1 and Series2 (+) or any other operation because the X spacing in them is different. So what I want is to generate a new point in the Series1 when is needed.
For that I would like to simple use a linear interpolation. Say that I have P1(0,40) and P2(0,60) in series1, but in series2 I have a point (1,10). I need to generate a point P3 between P1 and P2 with (1,50) coordinates.
I was trying to do this with SkipWhile and comparing the next X value of both series, if XValue of series1 is lower, then add that XValue and corresponding YValue in the newSeries. Else use the XValue2 for generating an Y and add it to the newSeries. Here is one of my attempts (doesn't work):
List<double> series1X = new List<double> { 0, 2, 4, 6, 8 };
List<double> series1Y = new List<double> { 120, 100, 110, 105, 70 };
List<double> series2X = new List<double> { 0, 1, 7, 8,9 };
List<double> newSeries1X = new List<double>();
List<double> newSeries1Y = new List<double>();
double lastX1 = series1X[series1X.Count()-1];
int i = 0;
while (next1X <= lastX1)
{
next2X = series2X.SkipWhile(p => p <= next1X).First();
Console.WriteLine(next2X.ToString());
if (next1X <= next2X)
{
newSeries1X.Add(series1X[i]);
newSeries1Y.Add(series1Y[i]);
}
if (next2X < next1X)
{
while (next2X < next1X)
{
newSeries1X.Add(next2X);
newY = series1Y[i] + (next2X - series1X[i]) * (series1Y[i + 1] - series1Y[i]) / (series1X[i + 1] - series1X[i]);
newSeries1Y.Add(newY);
next2X = series2X.SkipWhile(p => p <= next2X).First();
}
}
next1X = series1X.SkipWhile(p => p <= next2X).First();
Console.WriteLine(next1X.ToString());
i++;
}
It would be AWESOME to do this with your Zip method. But I have no idea how to write that condition in the predicate.
First off, I'd probably use an appropriate 'point' class that contains both the x and y coordinates instead of two separate lists for each coordinate. Then you can use the Zip method to quickly iterate through them:
IEnumerable<PointF> points0 = ...
IEnumerable<PointF> points0 = ...
float x2 = ...
IEnumerable<PointF> newPoints = point0.Zip(points1,
(p0, p1) => new PointF(p0.X, p0.Y + (x2-p0.X) * (p1.Y-p0.Y) / (p1.X-p0.X)));
This makes it easy to calculate a new set of points from your input data. If you just care about a single y-value, you can still do this with your current data, it will just look weird:
IEnumerable<double> y2values =
xValues1.Zip(yValues1, (x, y) => new { x, y }).Zip(
xValues2.Zip(yValues2, (x, y) => new { x, y }),
(p0, p1) => p0.y + (x2-p0.x) * (p1.y-p0.y) / (p1.x-p0.x));
I appologize if in the process of coding this answer I somehow mangled your math.
Update
Now that I have a better grasp on what you're trying to do, I don't think any Linq method will work out quite right. Here what I've come up with using indexes:
List<double> series1X = new List<double> { 0, 2, 4, 6, 8 };
List<double> series1Y = new List<double> { 120, 100, 110, 105, 70 };
List<double> series2X = new List<double> { 0, 1, 7, 8, 9 };
// in the worst case there are n + m new points
List<double> newSeries1X = new List<double>(series1X.Count + series2X.Count);
List<double> newSeries1Y = new List<double>(series1X.Count + series2X.Count);
int i = 0, j = 0;
for ( ; i < series1X.Count && j < series2X.Count; )
{
if (series1X[i] <= series2X[j])
{
newSeries1X.Add(series1X[i]);
newSeries1Y.Add(series1Y[i]);
if (series1X[i] == series2X[j])
{
j++;
}
i++;
}
else
{
int k = (i == 0) ? i : i - 1;
// interpolate
double y0 = series1Y[k];
double y1 = series1Y[k + 1];
double x0 = series1X[k];
double x1 = series1X[k + 1];
double y = y0 + (y1 - y0) * (series2X[j] - x0) / (x1 - x0);
newSeries1X.Add(series2X[j]);
newSeries1Y.Add(y);
j++;
}
}
for ( ; i < series1X.Count; i++)
{
newSeries1X.Add(series1X[i]);
newSeries1Y.Add(series1Y[i]);
}
for ( ; j < series2X.Count; j++)
{
// interpolate
double y0 = series1Y[i - 2];
double y1 = series1Y[i - 1];
double x0 = series1X[i - 2];
double x1 = series1X[i - 1];
double y = y0 + (y1 - y0) * (series2X[j] - x0) / (x1 - x0);
newSeries1X.Add(series2X[j]);
newSeries1Y.Add(y);
}
Output is
newSeries1X = { 0, 1, 2, 4, 6, 7, 8, 0 }
newSeries1Y = { 120, 110, 100, 110, 105, 87.5, 70, 52.5 }
This solution handles cases where the first series2X[0] < series1X[0] and when series2X[n] > series1X[m] by linearly 'projecting' the data outward from the first / last pair of points.
Here's another solution using enumerators (mostly), but it's not nearly as elegant as I'd hoped it would be. It could probably be improved a bit:
bool hasS1 = true, hasS2 = true, preinterp = true;
double x0 = 0, y0 = 0, x1 = 0, y1 = 0, x = 0, y = 0;
using(var s1xEnumerator = series1X.GetEnumerator())
using(var s1yEnumerator = series1Y.GetEnumerator())
using(var s2xEnumerator = series2X.GetEnumerator())
{
hasS1 = s1xEnumerator.MoveNext();
hasS2 = s2xEnumerator.MoveNext();
s1yEnumerator.MoveNext();
while(hasS1 && hasS2)
{
x1 = s1xEnumerator.Current;
y1 = s1yEnumerator.Current;
x = s2xEnumerator.Current;
if (x1 <= x)
{
newSeries1X.Add(x1);
newSeries1Y.Add(y1);
hasS1 = s1xEnumerator.MoveNext();
s1yEnumerator.MoveNext();
preinterp = false;
if (hasS1)
{
x0 = x1;
y0 = y1;
}
if (x1 == x)
{
hasS2 = s2xEnumerator.MoveNext();
}
}
else
{
// we have to look ahead to get the next interval to interpolate before x0
if (preinterp)
{
x0 = x1;
y0 = y1;
x1 = series1X[1]; // can't peek with enumerator
y1 = series1Y[1];
preinterp = false;
}
y = y0 + (y1 - y0) * (x - x0) / (x1 - x0);
newSeries1X.Add(x);
newSeries1Y.Add(y);
hasS2 = s2xEnumerator.MoveNext();
}
}
while(hasS1)
{
newSeries1X.Add(s1xEnumerator.Current);
newSeries1Y.Add(s1yEnumerator.Current);
hasS1 = s1xEnumerator.MoveNext();
s1yEnumerator.MoveNext();
}
while(hasS2)
{
x = s2xEnumerator.Current;
y = y0 + (y1 - y0) * (x - x0) / (x1 - x0);
newSeries1X.Add(x);
newSeries1Y.Add(y);
hasS2 = s2xEnumerator.MoveNext();
}
}
For working with two series with different spacing I first need to generate points in the first set, then in the second (with the same method) and finally sum point to point.
Here is the code for that method:
using OxyPlot.Series;
using OxyPlot;
namespace Algorithm1
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
List<DataPoint> S1 = new List<DataPoint> ();
List<DataPoint> S2 = new List<DataPoint>();
List<DataPoint> NS1 = new List<DataPoint>();
S1.Add(new DataPoint(4, 10));
S1.Add(new DataPoint(6, 20));
S1.Add(new DataPoint(8, 15));
S1.Add(new DataPoint(9, 70));
S1.Add(new DataPoint(10, 5));
S2.Add(new DataPoint(1, 0));
S2.Add(new DataPoint(2, 0));
S2.Add(new DataPoint(3, 0));
S2.Add(new DataPoint(6, 0));
S2.Add(new DataPoint(7, 0));
S2.Add(new DataPoint(8.1, 0));
S2.Add(new DataPoint(8.2, 0));
S2.Add(new DataPoint(8.3, 0));
S2.Add(new DataPoint(8.4, 0));
S2.Add(new DataPoint(9, 0));
S2.Add(new DataPoint(9.75, 0));
S2.Add(new DataPoint(11, 0));
S2.Add(new DataPoint(12, 0));
S2.Add(new DataPoint(16, 0));
NS1 = GetMiddlePoints(S1, S2);
foreach (DataPoint pointin NS1)
{
MessageBox.Show( point.X.ToString()+" : "+ point.Y.ToString());
}
}
#region GetMiddlePoints
private List<DataPoint> GetMiddlePoints(List<DataPoint> S1, List<DataPoint> S2)
{
List<DataPoint> NS1 = new List<DataPoint>();
int i = 0;
int j = S2.TakeWhile(p => p.X < S1[0].X).Count();
int PointsInS1 = S1.Count;
int PointsInS2 = S2.Count;
DataPoint newPoint = new DataPoint();
while (i < PointsInS1 )
{
if (j < PointsInS2 )
{
if (S1[i].X < S2[j].X)
{
newPoint = S1[i];
NS1.Add(newPoint );
i++;
}
else if (S1[i].X == S2[j].X)
{
newPoint = S1[i];
NS1.Add(newPoint );
i++;
j++;
}
else if (S1[i].X > S2[j].X)
{
newPoint .X = S2[j].X;
newPoint .Y = S1[i-1].Y + (S2[j].X - S1[i-1].X) * (S1[i].Y - S1[i-1].Y) / (S1[i].X - S1[i-1].X);
NS1.Add(newPoint );
j++;
}
}
if (j == PointsInS2 )
{
newPoint = S1[i];
NS1.Add(newPoint );
i++;
}
}
return NS1;
}
#endregion
}
}
I am scratching my head to figure out a way to scale a signal on a 2D graphic pane. The story is: I connect my application to a microcontroller and on fixed intervals I read a data value (A voltage point). Now I want to draw this on my graphic pane. Example:
So up in the picture you see at time 0, the voltage is also 0 and this goes on and after 6 data points I will clear the pane and redo the whole stuff.
The question is, how can I translate this voltage into pixel values, having in mind I want the middle of the graphic pane to be my signals 0, just like a normal cartesian graph. Can someone please help me to figure out the scaling algorithm in this case?
Seems like simple math: just add the width/2 to all X coordinates which you are passing into drawing functions. Suppose you have an array of 6 points you can do the following:
var g = this.CreateGraphics();
var points = new Point[6]{new Point(0, 0), new Point(10, 10), new Point(30, 0), new Point(40,20), new Point(50, 0), new Point(60,30)};
for (int i = 0; i < points.Length-1; i++)
{
g.DrawLine(Pens.Black, points[i].X + Width / 2, Height / 2 - points[i].Y, points[i + 1].X + Width / 2, Height / 2 - points[i + 1].Y);
}
Alternatively you can invoke TranslateTransform function to move all further drawing to some amount by X and Y axes. Example:
var g = this.CreateGraphics();
var points = new Point[6]{new Point(0, 0), new Point(10, 10), new Point(30, 0), new Point(40,20), new Point(50, 0), new Point(60,30)};
g.TranslateTransform(Width / 2, 0, System.Drawing.Drawing2D.MatrixOrder.Append);
for (int i = 0; i < points.Length-1; i++)
{
g.DrawLine(Pens.Black, points[i].X, Height / 2 - points[i].Y, points[i + 1].X, Height / 2 - points[i + 1].Y);
}
Maybe this will be useful (remember that scale and translate functions are changing points in array):
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
var points = new PointF[6] { new PointF(0, 0), new PointF(30, 3), new PointF(90, 0), new PointF(190, 3.1f), new PointF(270, -0.5f), new PointF(360, 3.5f) };
float maxX = (from p in points select p).Max(t => t.X);
float maxY = (from p in points select p).Max(t => t.Y);
float xSizeToFit = pictureBox1.Width;
float ySizeToFit = pictureBox1.Height/2;
float scaleX = xSizeToFit / maxX;
float scaleY = ySizeToFit / maxY;
// scale to fit to given size
ScalePoints(points, scaleX, scaleY);
// translate to center
TranslatePoints(points, this.pictureBox1.Width / 2 - 0.5f * xSizeToFit, this.pictureBox1.Height / 2 + 0.5f * ySizeToFit);
DrawAxis(e.Graphics, this.pictureBox1.Size);
e.Graphics.DrawLines(Pens.Black, points);
}
private void TranslatePoints(PointF[] points, float transX, float transY)
{
for (int i = 0; i < points.Length; i++)
{
points[i].X += transX;
points[i].Y = transY - points[i].Y;
}
}
private void ScalePoints(PointF[] points, float scaleX, float scaleY)
{
for (int i = 0; i < points.Length; i++)
{
points[i].X *= scaleX;
points[i].Y *= scaleY;
}
}
public void DrawAxis(Graphics g, Size size)
{
//x
g.DrawLine(Pens.Black, 0, size.Height / 2, size.Width, size.Height / 2);
//y
g.DrawLine(Pens.Black, size.Width / 2, size.Height, size.Width / 2, 0);
}
private void pictureBox1_Resize(object sender, EventArgs e)
{
pictureBox1.Invalidate();
}