Thursday, October 16, 2008

Simple Kinetics in silverlight

Simple kinetics (mechanical) in silverlight, some exploding balls:



XAML:

<UserControl x:Class="SimpleKinetics.Page"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="500" Height="350">
<Grid x:Name="LayoutRoot" Background="White">
<Canvas x:Name="Container">
<Button x:Name="ButtonAdd" Canvas.Top="10" Canvas.Left="8" ClickMode="Release" Content="New balls" Click="ButtonAdd_Click" />
<Rectangle x:Name="Controls" Canvas.Top="0" Canvas.Left="0" Width="75" Height="300">
</Rectangle>
</Canvas>
</Grid>
</UserControl>


code behind:

public partial class Page : UserControl
{
List<Vector> vectors = new List<Vector>();
const int RADIUS = 5;
Random rand = new Random();

public Page()
{
InitializeComponent();

DispatcherTimer timer = new DispatcherTimer();

timer.Interval = new TimeSpan(0, 0, 0, 0, 30);

timer.Tick += new EventHandler(OnTick);

timer.Start();
}

private void OnTick(object sender, EventArgs e)
{
List<Vector> deads = new List<Vector>();
foreach (Vector vector in vectors)
{
vector.Transition();
if (vector.IsDead)
deads.Add(vector);
}

deads.ForEach(dead =>
{
vectors.Remove(dead);
Container.Children.Remove(dead.ellipse);
});
}

private void ButtonAdd_Click(object sender, RoutedEventArgs e)
{
for (int explosions = 0; explosions < rand.Next(10)+1; explosions++)
GenerateExplode();
}

private void GenerateExplode()
{
Point point = new Point(rand.Next((int)Width), rand.Next((int)Height));
for (int i = 0; i < 100; i++)
{
Ellipse ellipse = new Ellipse();
ellipse.Width = RADIUS;
ellipse.Height = RADIUS;
Vector vector = new Vector(rand.Next(360), (rand.Next(30) / 10D) + 0.5D, ellipse);
vectors.Add(vector);

// make it a random colour
ellipse.Fill = new SolidColorBrush(
Color.FromArgb(
255,
GetRandomColour(),
GetRandomColour(),
GetRandomColour()));

// add it to the interface
Container.Children.Add(ellipse);

// put it in a random location
vector.Point = point;
}
}

private byte GetRandomColour()
{
return (byte)rand.Next(byte.MaxValue);
}

private Point Offset(Point inpoint, double offset)
{
Point point = new Point(inpoint.X + offset, inpoint.Y + offset);

return point;
}

private static Point GetPoint(DependencyObject sobj)
{
Point point = new Point((double)sobj.GetValue(Canvas.LeftProperty), (double)sobj.GetValue(Canvas.TopProperty));

return point;
}
}


which depends on my Vector class:

internal class Vector
{
public double direction;
public double magnitude;
public Ellipse ellipse;

public Vector(double direction, double magnitude, Ellipse ellipse)
{
this.direction = direction;
this.magnitude = magnitude;
this.ellipse = ellipse;
}

public Point Point
{
get
{
Point point = new Point((double)ellipse.GetValue(Canvas.LeftProperty), (double)ellipse.GetValue(Canvas.TopProperty));

return point;
}
set
{
ellipse.SetValue(Canvas.LeftProperty, value.X);
ellipse.SetValue(Canvas.TopProperty, value.Y);
}
}

internal void Transition()
{
Point newPoint = CalculateNextPosition();

Point = newPoint;

double fade = ((magnitude * magnitude) / magnitude) / 800d;
ellipse.Fill.Opacity -= fade;
}

public bool IsDead
{
get
{
return !(ellipse.Fill.Opacity > 0);
}
}

private double DegreesToRadians(double angle)
{
return ((angle*Math.PI) / 180f);
}

private double RadiansToDegrees(double angle)
{
return ((angle*180) / Math.PI);
}

private Point CalculateNextPosition()
{
double vx = magnitude * Math.Cos(DegreesToRadians(direction));
double vy = magnitude * Math.Sin(DegreesToRadians(direction));

Point current = this.Point;
double x = current.X + vx;
double y = current.Y + vy;

CheckCollision(vx, vy, x, y);

Point newPoint = new Point(x, y);

return newPoint;
}

private void CheckCollision(double vx, double vy, double x, double y)
{
const int maxX = 500;
const int maxY = 500;

if (x < 0 x > maxX)
vx *= -1;
if (y < 0 y > maxY)
vy *= -1;

magnitude = Math.Sqrt((vx * vx) + (vy * vy));
direction = RadiansToDegrees(Math.Atan2(vy, vx));
}
}

No comments: