using System;
using Chernobyl.Mathematics.Geometry;
using Chernobyl.Mathematics.Vectors;
using Chernobyl.Values;
namespace Chernobyl.Mathematics.Movement
{
///
/// An that does not move outside a boundary
/// specified by .
///
public class CagedTransform2D : MatrixTransform
{
///
/// Initializes a new instance of the class.
///
/// The that this
/// is not allowed to move outside of.
/// Thrown if
/// is null.
public CagedTransform2D(Rectangle cage)
{
BorderDistance = DefaultBorderDistance;
Cage = cage;
}
///
/// The that this is
/// not allowed to move outside of.
///
public Rectangle Cage
{
get { return _cage; }
private set
{
if(value == null)
throw new ArgumentNullException("value", "CagedTransform2D.Cage " +
"cannot be null. Please specify a valid Rectangle.");
_cage = value;
_cage.TransformDirtied += CageTransformDirtied;
}
}
///
/// The default value of the property which
/// is ".0001f".
///
public const float DefaultBorderDistance = .0001f;
///
/// The distance this places between itself and
/// the edges of the . The default value of this
/// property is given by .
///
/// Thrown if client code
/// attempts to set a value on this property that is less than or equal
/// to 0.
public float BorderDistance
{
get { return _borderDistance; }
set
{
if(value <= 0)
throw new ArgumentOutOfRangeException("value", value,
"The CagedTransform2D.BorderDistance cannot be zero.");
_borderDistance = value;
IsUpdateNeeded = true;
}
}
///
/// This method is invoked during the update of the
/// right before the
/// is actually updated. This
/// method ensures the is enclosed
/// within the .
///
/// The value of the .
/// It is stored in a so that the calculation can be
/// avoided if necessary. Don't call unless
/// required.
protected override void PreUpdate(Values.LazyValue world)
{
base.PreUpdate(world);
// Found out where we are in the world.
Vector2 worldPosition = world.Value.Translation.Xy;
// Now calculate how far outside of the cage we have moved (if
// any).
float leftDistance = Cage.LeftSide - worldPosition.X; // Positive value if outside the cage.
float rightDistance = Cage.RightSide - worldPosition.X; // Negative value if outside the cage.
float bottomDistance = Cage.Bottom - worldPosition.Y; // Positive value if outside the cage.
float topDistance = Cage.Top - worldPosition.Y; // Negative value if outside the cage.
// Move the local position so that we are back in the cage, if
// necessary.
Matrix4 local = LocalMatrix;
Vector2 localPosition = local.Translation.Xy;
if (leftDistance > 0)
localPosition.X += (leftDistance + BorderDistance);
else if (rightDistance < 0)
localPosition.X += (rightDistance - BorderDistance);
if (bottomDistance > 0)
localPosition.Y += (bottomDistance + BorderDistance);
else if (topDistance < 0)
localPosition.Y += (topDistance - BorderDistance);
local.Row3.X = localPosition.X;
local.Row3.Y = localPosition.Y;
SetTransformation(ref local);
}
///
/// An event handler that is invoked when the instance's
/// event has been invoked.
/// This method ensures that, if the moves, this
/// will stay inside it.
///
/// The instance that generated the event.
/// The instance containing the
/// event data.
void CageTransformDirtied(object sender, EventArgs e)
{
IsUpdateNeeded = true;
}
///
/// The backing field to .
///
Rectangle _cage;
///
/// The backing field to .
///
float _borderDistance;
}
}