Короче, вот готовый пример, как работать с матрицами трансформации. Это действительно проще показать, чем объяснить
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
namespace DrawPlot
{
public partial class MainForm : Form
{
// Добавил сюда, чтобы в InitializeComponent не лезть
PictureBox picDisplay;
// Макс размер сетки по наибольшему измерению окна
// для сохранения пропорции (либо по ширине, либо по высоте)
const float MaxGrid = 10.0f;
// Это задаёт логический размер окна
SizeF viewPort;
// Шаги сетки
const float GridStep = 0.1f;
// Каждые 10 линий сетки делаем линию поярче
const int BoldLineStep = 10;
// Цвета
Pen pen_LargeGrid = new Pen(Color.FromArgb(0,64,0));
Pen pen_SmallGrid = new Pen(Color.FromArgb(0,32,0));
Pen pen_Axii = new Pen(Color.FromArgb(0,128,0));
SolidBrush plotBrush = new SolidBrush(Color.Red);
// Ширина 1 пиксела в логических размерах
float Pixel_Width;
public MainForm()
{
InitializeComponent();
picDisplay = new PictureBox() { Parent = this, Dock = DockStyle.Fill, BackColor = Color.Black };
picDisplay.Paint += this.PicDisplay_Paint;
picDisplay.Resize += this.PicDisplay_Resize;
// Стрелочка 10х4
AdjustableArrowCap cap = new AdjustableArrowCap(4.0f,10.0f);
pen_Axii.CustomEndCap = cap;
CalculateDimensions();
} // MainForm Ctor
private void PicDisplay_Resize(object sender, EventArgs e)
{
CalculateDimensions();
picDisplay.Invalidate();
} // PicDisplay_Resize
private void CalculateDimensions()
{
var rect = picDisplay.ClientRectangle;
// Устанавливаем размеры в зависимости от того, что больше, длина или ширина
// для сохранения квадратной сетки
if (rect.Width > rect.Height)
viewPort = new SizeF(MaxGrid, MaxGrid * (rect.Height / (float)rect.Width));
else
viewPort = new SizeF(MaxGrid * (rect.Width / (float)rect.Height), MaxGrid);
// Считаем ширину 1 пикселя
Pixel_Width = viewPort.Width / rect.Width;
// Назначаем эту ширину используемым перьям
foreach (var pen in new Pen[] { pen_Axii, pen_LargeGrid, pen_SmallGrid, pen_LargeGrid })
pen.Width = Pixel_Width;
} // CalculateDimensions
private void PicDisplay_Paint(object sender, PaintEventArgs e)
{
Graphics g = e.Graphics;
// Масштабируем
g.ScaleTransform(picDisplay.Width / viewPort.Width, (picDisplay.Height / viewPort.Height));
// Двигаем начало координат в середину
float halfWidth = viewPort.Width / 2;
float halfHeight = viewPort.Height / 2;
g.TranslateTransform(halfWidth, halfHeight);
// Переворачиваем Y
g.ScaleTransform(1.0f, -1.0f);
// Рисуем сетку
int step = 0;
for(var f = 0f; f < MaxGrid / 2 ; f += GridStep)
{
var currentPen = pen_SmallGrid;
if (0 == step % BoldLineStep) currentPen = pen_LargeGrid;
// Горизонтальные
g.DrawLine(currentPen, -halfWidth, f, halfWidth, f);
g.DrawLine(currentPen, -halfWidth, -f, halfWidth, -f);
// Вертикальные
g.DrawLine(currentPen, f, -halfHeight, f, halfHeight);
g.DrawLine(currentPen, -f, -halfHeight, -f, halfHeight);
step++;
} // for
// Оси координат
g.DrawLine(pen_Axii, -halfWidth, 0, halfWidth, 0);
g.DrawLine(pen_Axii, 0, -halfHeight, 0, halfHeight);
// график
float y;
for (var x = -halfWidth ; x < halfWidth ; x+= Pixel_Width)
{
y = ((float)Math.Pow(x, 3) - 1) / (x + 5);
// Обязательно проверка на NaN и Infinity
if (!float.IsNaN(y) && !float.IsInfinity(y))
g.FillRectangle(plotBrush, new RectangleF(x, y, Pixel_Width, Pixel_Width));
} // for
} // PicDisplay_Paint
} // class MainForm` 1+m
} // DrawPlot namespace