1、抛物线本来就是一个二次函数
2、图形用GDI+写的。随手写着玩的东西,并不涉及很深的图形算法。
3、当采样点越多矩齿现象就越小,图像越精致。GDI+中本身也带有消除矩齿的函数,不过这里并不很需要。
4、使用计时器来写的,这个小东西不需要多线程。在图像绘制过程中我不允许鼠标按键再响应绘制新图。
本不想发C#代码在C论坛里。不过没有代码算法交流起来很不方便,这里把程序的主要部分发上来吧。其实有C++底子的话,C#代码并不难看懂。
程序代码:
namespace Beyondyf
{
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
}
private void MainForm_Load(object sender, EventArgs e)
{
ox = 100;
oy = 500;
bgColor = Color.Black;
brushArrow = new SolidBrush(Color.Red);
brushBall = new SolidBrush(Color.Pink);
brushLine = new SolidBrush(Color.Yellow);
penLine = new Pen(brushLine);
map = new Bitmap(this.ClientSize.Width, this.ClientSize.Height);
gMap = Graphics.FromImage(map);
gMap.Clear(bgColor);
DrawBall(gMap, ox, oy);
gShow = this.CreateGraphics();
timer = new Timer();
timer.Tick += new EventHandler(DrawPath);
timer.Interval = 20;
setting = false;
}
private void MainForm_Paint(object sender, PaintEventArgs e)
{
gShow.DrawImage(map, 0, 0);
}
private void MainForm_MouseDown(object sender, MouseEventArgs e)
{
if (setting) return;
int dx = e.X - ox;
int dy = e.Y - oy;
double length = Math.Sqrt(dx * dx + dy * dy);
double angle = length < 1 ? 0 : Math.Asin(dy / length);
if (dx < 0) angle = Math.PI - angle;
if (length < 1) velocity = 0;
else if (length > 300) velocity = 300;
else velocity = length;
initAngle = -angle;
gMap.Clear(bgColor);
DrawArrow(gMap, ox, oy, velocity, angle);
DrawBall(gMap, ox, oy);
gShow.DrawImage(map, 0, 0);
}
private void MainForm_MouseMove(object sender, MouseEventArgs e)
{
if (!setting && (e.Button & System.Windows.Forms.MouseButtons.Left) != 0)
{
int dx = e.X - ox;
int dy = e.Y - oy;
double length = Math.Sqrt(dx * dx + dy * dy);
double angle = length < 1 ? 0 : Math.Asin(dy / length);
if (dx < 0) angle = Math.PI - angle;
if (length < 1) velocity = 0;
else if (length > 300) velocity = 300;
else velocity = length;
initAngle = -angle;
gMap.Clear(bgColor);
DrawArrow(gMap, ox, oy, velocity, angle);
DrawBall(gMap, ox, oy);
gShow.DrawImage(map, 0, 0);
}
}
private void MainForm_MouseUp(object sender, MouseEventArgs e)
{
if (!setting)
{
setting = true;
prb = new Parabola(velocity, initAngle, 1, 1000);
timer.Start();
}
}
private void DrawBall(Graphics g, float x, float y)
{
g.FillEllipse(brushBall, x - 10, y - 10, 20, 20);
}
private void DrawArrow(Graphics g, float x, float y, double length, double angle)
{
Point[] ps = new Point[3];
ps[0].X = (int)(8 * Math.Cos(angle - Math.PI / 2) + x + 0.5);
ps[0].Y = (int)(8 * Math.Sin(angle - Math.PI / 2) + y + 0.5);
ps[1].X = (int)(8 * Math.Cos(angle + Math.PI / 2) + x + 0.5);
ps[1].Y = (int)(8 * Math.Sin(angle + Math.PI / 2) + y + 0.5);
ps[2].X = (int)(length * Math.Cos(angle) + x + 0.5);
ps[2].Y = (int)(length * Math.Sin(angle) + y + 0.5);
g.FillPolygon(brushArrow, ps);
}
private void DrawLine(Graphics g, Parabola p, int count)
{
for (int i = 1; i <= count; i++)
g.DrawLine(penLine, p.X(i - 1, rate) + ox, oy - p.Y(i - 1, rate), p.X(i, rate) + ox, oy - p.Y(i, rate));
}
private void DrawPath(object sender, EventArgs e)
{
gMap.Clear(bgColor);
DrawLine(gMap, prb, count);
DrawBall(gMap, prb.X(count, rate) + ox, oy - prb.Y(count, rate));
gShow.DrawImage(map, 0, 0);
if (prb.Y(count, rate) < oy - 700)
{
timer.Stop();
count = 0;
setting = false;
}
else count++;
}
private Timer timer;
private Parabola prb;
private bool setting;
private double velocity;
private double initAngle;
private int count;
private const double rate = 10;
private Bitmap map;
private Graphics gMap;
private Graphics gShow;
private int ox;
private int oy;
private Color bgColor;
private Brush brushArrow;
private Brush brushBall;
private Brush brushLine;
private Pen penLine;
}
public class Parabola
{
public Parabola(double velocity, double angle, double timeStep, int stepCount)
{
x = new double[stepCount + 1];
y = new double[stepCount + 1];
double vx = velocity * Math.Cos(angle) * timeStep;
double vy = velocity * Math.Sin(angle) * timeStep;
double a = -9.8 / 2 * timeStep * timeStep;
for (int i = 0; i <= stepCount; i++)
{
x[i] = vx * i;
y[i] = vy * i + a * i * i;
}
}
public float X(int index, double rate)
{
if (index < 0 && index >= x.Length) return 0;
return (float)(x[index] / rate);
}
public float Y(int index, double rate)
{
if (index < 0 && index >= y.Length) return 0;
return (float)(y[index] / rate);
}
private double[] x;
private double[] y;
}
}