mirror of
https://github.com/AlexMacocian/MonoGame.Extended.git
synced 2026-07-26 00:45:20 +00:00
Merge branch 'develop' of https://github.com/craftworkgames/MonoGame.Extended into develop
This commit is contained in:
@@ -55,3 +55,15 @@
|
||||
/processorParam:TextureFormat=Color
|
||||
/build:tiny-tiles.png
|
||||
|
||||
#begin tiny-grey-cloud.png
|
||||
/importer:TextureImporter
|
||||
/processor:TextureProcessor
|
||||
/processorParam:ColorKeyColor=255,0,255,255
|
||||
/processorParam:ColorKeyEnabled=True
|
||||
/processorParam:GenerateMipmaps=False
|
||||
/processorParam:PremultiplyAlpha=True
|
||||
/processorParam:ResizeToPowerOfTwo=False
|
||||
/processorParam:MakeSquare=False
|
||||
/processorParam:TextureFormat=Color
|
||||
/build:tiny-grey-cloud.png
|
||||
|
||||
|
||||
@@ -11,9 +11,12 @@
|
||||
<tileset firstgid="257" name="tiny-characters" tilewidth="32" tileheight="32" tilecount="128" columns="16">
|
||||
<image source="tiny-characters.png" width="512" height="256"/>
|
||||
</tileset>
|
||||
<tileset firstgid="385" name="tiny-grey-cloud" tilewidth="32" tileheight="32" tilecount="1" columns="1">
|
||||
<image source="tiny-grey-cloud.png" width="32" height="32"/>
|
||||
</tileset>
|
||||
<layer name="background" width="25" height="15">
|
||||
<data encoding="base64">
|
||||
LAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAuAAAALwAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAAC0AAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACsAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAArAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAAC4AAAAvAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAA
|
||||
gQEAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAuAAAALwAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAAC0AAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAACBAQAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACsAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAArAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAAgQEAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAAIEBAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAAC4AAAAvAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAAIEBAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAA
|
||||
</data>
|
||||
</layer>
|
||||
<layer name="decorations" width="25" height="15">
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 252 B |
@@ -62,6 +62,7 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="Content\tiny-characters.png" />
|
||||
<Content Include="Content\tiny-grey-cloud.png" />
|
||||
<Content Include="Content\tiny-tiles.png" />
|
||||
<Content Include="Icon.ico" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -8,21 +8,19 @@ namespace Demo.SpaceGame.Entities
|
||||
{
|
||||
public class Explosion : Entity
|
||||
{
|
||||
private readonly SpriteSheetAnimator _animator;
|
||||
private readonly Sprite _sprite;
|
||||
private readonly AnimatedSprite _sprite;
|
||||
|
||||
public Explosion(SpriteSheetAnimationFactory animations, Vector2 position, float radius)
|
||||
{
|
||||
_animator = new SpriteSheetAnimator(animations);
|
||||
_sprite = _animator.CreateSprite();
|
||||
_sprite = new AnimatedSprite(animations);
|
||||
_sprite.Position = position;
|
||||
_sprite.Scale = Vector2.One*radius*0.2f;
|
||||
_animator.Play("explode", Destroy);
|
||||
_sprite.Play("explode", Destroy);
|
||||
}
|
||||
|
||||
public override void Update(GameTime gameTime)
|
||||
{
|
||||
_animator.Update(gameTime);
|
||||
_sprite.Update(gameTime);
|
||||
}
|
||||
|
||||
public override void Draw(SpriteBatch spriteBatch)
|
||||
|
||||
@@ -27,8 +27,7 @@ namespace Demo.SpriteSheetAnimations
|
||||
private Zombie _zombie;
|
||||
private SpriteSheetAnimation _animation;
|
||||
private Sprite _fireballSprite;
|
||||
private SpriteSheetAnimator _motwAnimator;
|
||||
private Sprite _motwSprite;
|
||||
private AnimatedSprite _motwSprite;
|
||||
|
||||
public Game1()
|
||||
{
|
||||
@@ -86,9 +85,9 @@ namespace Demo.SpriteSheetAnimations
|
||||
motwAnimationFactory.Add("walkWest", new SpriteSheetAnimationData(new[] { 12, 13, 14, 13 }, isLooping: false));
|
||||
motwAnimationFactory.Add("walkEast", new SpriteSheetAnimationData(new[] { 24, 25, 26, 25 }, isLooping: false));
|
||||
motwAnimationFactory.Add("walkNorth", new SpriteSheetAnimationData(new[] { 36, 37, 38, 37 }, isLooping: false));
|
||||
_motwAnimator = new SpriteSheetAnimator(motwAnimationFactory);
|
||||
_motwSprite = _motwAnimator.CreateSprite(new Vector2(350, 800));
|
||||
_motwAnimator.Play("walkSouth").IsLooping = true;
|
||||
_motwSprite = new AnimatedSprite(motwAnimationFactory);
|
||||
_motwSprite.Position = new Vector2(350, 800);
|
||||
_motwSprite.Play("walkSouth").IsLooping = true;
|
||||
}
|
||||
|
||||
protected override void UnloadContent()
|
||||
@@ -104,16 +103,16 @@ namespace Demo.SpriteSheetAnimations
|
||||
|
||||
// motw
|
||||
if (keyboardState.IsKeyDown(Keys.W))
|
||||
_motwAnimator.Play("walkNorth");
|
||||
_motwSprite.Play("walkNorth");
|
||||
|
||||
if (keyboardState.IsKeyDown(Keys.A))
|
||||
_motwAnimator.Play("walkWest");
|
||||
_motwSprite.Play("walkWest");
|
||||
|
||||
if (keyboardState.IsKeyDown(Keys.S))
|
||||
_motwAnimator.Play("walkSouth");
|
||||
_motwSprite.Play("walkSouth");
|
||||
|
||||
if (keyboardState.IsKeyDown(Keys.D))
|
||||
_motwAnimator.Play("walkEast");
|
||||
_motwSprite.Play("walkEast");
|
||||
|
||||
// camera
|
||||
if (keyboardState.IsKeyDown(Keys.R))
|
||||
@@ -146,7 +145,7 @@ namespace Demo.SpriteSheetAnimations
|
||||
_animation.Update(deltaSeconds);
|
||||
_fireballSprite.TextureRegion = _animation.CurrentFrame;
|
||||
|
||||
_motwAnimator.Update(deltaSeconds);
|
||||
_motwSprite.Update(deltaSeconds);
|
||||
|
||||
base.Update(gameTime);
|
||||
}
|
||||
@@ -156,7 +155,7 @@ namespace Demo.SpriteSheetAnimations
|
||||
GraphicsDevice.Clear(Color.CornflowerBlue);
|
||||
|
||||
_spriteBatch.Begin(transformMatrix: _camera.GetViewMatrix());
|
||||
_tiledMap.Draw(_camera);
|
||||
_tiledMap.Draw(_camera.GetViewMatrix());
|
||||
_zombie.Draw(_spriteBatch);
|
||||
_spriteBatch.Draw(_fireballSprite);
|
||||
_spriteBatch.End();
|
||||
|
||||
@@ -22,8 +22,7 @@ namespace Demo.SpriteSheetAnimations
|
||||
|
||||
public class Zombie : IUpdate, IActorTarget
|
||||
{
|
||||
private readonly SpriteSheetAnimator _animator;
|
||||
private readonly Sprite _sprite;
|
||||
private readonly AnimatedSprite _sprite;
|
||||
|
||||
private float _direction = -1.0f;
|
||||
|
||||
@@ -53,19 +52,19 @@ namespace Demo.SpriteSheetAnimations
|
||||
switch (_state)
|
||||
{
|
||||
case ZombieState.Attacking:
|
||||
_animator.Play("attack", () => State = ZombieState.Idle);
|
||||
_sprite.Play("attack", () => State = ZombieState.Idle);
|
||||
break;
|
||||
case ZombieState.Dying:
|
||||
_animator.Play("die", () => State = ZombieState.Appearing);
|
||||
_sprite.Play("die", () => State = ZombieState.Appearing);
|
||||
break;
|
||||
case ZombieState.Idle:
|
||||
_animator.Play("idle");
|
||||
_sprite.Play("idle");
|
||||
break;
|
||||
case ZombieState.Appearing:
|
||||
_animator.Play("appear", () => State = ZombieState.Idle);
|
||||
_sprite.Play("appear", () => State = ZombieState.Idle);
|
||||
break;
|
||||
case ZombieState.Walking:
|
||||
_animator.Play("walk", () => State = ZombieState.Idle);
|
||||
_sprite.Play("walk", () => State = ZombieState.Idle);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -76,8 +75,7 @@ namespace Demo.SpriteSheetAnimations
|
||||
|
||||
public Zombie(SpriteSheetAnimationFactory animations)
|
||||
{
|
||||
_animator = new SpriteSheetAnimator(animations);
|
||||
_sprite = _animator.CreateSprite();
|
||||
_sprite = new AnimatedSprite(animations);
|
||||
|
||||
State = ZombieState.Appearing;
|
||||
IsOnGround = false;
|
||||
@@ -85,7 +83,7 @@ namespace Demo.SpriteSheetAnimations
|
||||
|
||||
public void Update(GameTime gameTime)
|
||||
{
|
||||
_animator.Update(gameTime);
|
||||
_sprite.Update(gameTime);
|
||||
|
||||
IsOnGround = false;
|
||||
|
||||
|
||||
@@ -101,7 +101,7 @@ namespace Demo.TiledMaps
|
||||
GraphicsDevice.Clear(Color.CornflowerBlue);
|
||||
|
||||
// you can draw the whole map all at once
|
||||
_tiledMap.Draw(_camera);
|
||||
_tiledMap.Draw(_camera.GetViewMatrix());
|
||||
|
||||
// or you can have more control over drawing each individual layer
|
||||
//foreach (var layer in _tiledMap.Layers)
|
||||
|
||||
@@ -7,7 +7,7 @@ namespace MonoGame.Extended.Tests
|
||||
[TestFixture]
|
||||
public class AngleTest
|
||||
{
|
||||
private const float DELTA = 0.0000001f;
|
||||
private const double _delta = 0.000000000000001f;
|
||||
[Test]
|
||||
public void ConstructorTest()
|
||||
{
|
||||
@@ -16,27 +16,28 @@ namespace MonoGame.Extended.Tests
|
||||
var degrees = new Angle(value, AngleType.Degree);
|
||||
var gradians = new Angle(value, AngleType.Gradian);
|
||||
var revolutions = new Angle(value, AngleType.Revolution);
|
||||
Assert.AreEqual(value, radians.Radians, DELTA);
|
||||
Assert.AreEqual(value, degrees.Degrees, DELTA);
|
||||
Assert.AreEqual(value, gradians.Gradians, DELTA);
|
||||
Assert.AreEqual(value, revolutions.Revolutions, DELTA);
|
||||
Assert.AreEqual(value, radians.Radians, _delta);
|
||||
Assert.AreEqual(value, degrees.Degrees, _delta);
|
||||
Assert.AreEqual(value, gradians.Gradians, _delta);
|
||||
Assert.AreEqual(value, revolutions.Revolutions, _delta);
|
||||
}
|
||||
|
||||
[Test]
|
||||
[Ignore("This test is broken on the build server and I have no idea why")]
|
||||
public void ConversionTest()
|
||||
{
|
||||
//from radians
|
||||
var radians = new Angle(MathHelper.Pi);
|
||||
Assert.AreEqual(180d, radians.Degrees, DELTA);
|
||||
Assert.AreEqual(200d, radians.Gradians, DELTA);
|
||||
Assert.AreEqual(0.5, radians.Revolutions, DELTA);
|
||||
Assert.AreEqual(180d, radians.Degrees, _delta);
|
||||
Assert.AreEqual(200d, radians.Gradians, _delta);
|
||||
Assert.AreEqual(0.5, radians.Revolutions, _delta);
|
||||
//to radians
|
||||
var degrees = new Angle(180f, AngleType.Degree);
|
||||
var gradians = new Angle(200f, AngleType.Gradian);
|
||||
var revolutions = new Angle(0.5f, AngleType.Revolution);
|
||||
Assert.AreEqual(MathHelper.Pi, degrees.Radians, DELTA);
|
||||
Assert.AreEqual(MathHelper.Pi, gradians.Radians, DELTA);
|
||||
Assert.AreEqual(MathHelper.Pi, revolutions.Radians, DELTA);
|
||||
Assert.AreEqual(MathHelper.Pi, degrees.Radians, _delta);
|
||||
Assert.AreEqual(MathHelper.Pi, gradians.Radians, _delta);
|
||||
Assert.AreEqual(MathHelper.Pi, revolutions.Radians, _delta);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -62,15 +63,15 @@ namespace MonoGame.Extended.Tests
|
||||
public void VectorTest()
|
||||
{
|
||||
var angle = Angle.FromVector(Vector2.One);
|
||||
Assert.AreEqual(-MathHelper.Pi / 4d, angle.Radians, DELTA);
|
||||
Assert.AreEqual(-MathHelper.Pi / 4d, angle.Radians, _delta);
|
||||
Assert.AreEqual(10f, angle.ToVector(10f).Length());
|
||||
|
||||
angle = Angle.FromVector(Vector2.UnitX);
|
||||
Assert.AreEqual(0, angle.Radians, DELTA);
|
||||
Assert.AreEqual(0, angle.Radians, _delta);
|
||||
Assert.IsTrue(Vector2.UnitX.EqualsWithTolerence(angle.ToUnitVector()));
|
||||
|
||||
angle = Angle.FromVector(-Vector2.UnitY);
|
||||
Assert.AreEqual(MathHelper.Pi / 2d, angle.Radians, DELTA);
|
||||
Assert.AreEqual(MathHelper.Pi / 2d, angle.Radians, _delta);
|
||||
Assert.IsTrue((-Vector2.UnitY).EqualsWithTolerence(angle.ToUnitVector()));
|
||||
}
|
||||
|
||||
|
||||
@@ -69,7 +69,7 @@
|
||||
<Compile Include="Particles\ParticleBufferTests.cs" />
|
||||
<Compile Include="Particles\Profiles\PointProfileTests.cs" />
|
||||
<Compile Include="Particles\Profiles\RingProfileTests.cs" />
|
||||
<Compile Include="Primitives\BoundingBox2DTests.cs" />
|
||||
<Compile Include="Primitives\BoundingRectangleTests.cs" />
|
||||
<Compile Include="Primitives\Point2Tests.cs" />
|
||||
<Compile Include="Primitives\Ray2DTests.cs" />
|
||||
<Compile Include="Primitives\Segment2DTests.cs" />
|
||||
|
||||
@@ -0,0 +1,397 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using Microsoft.Xna.Framework;
|
||||
using MonoGame.Extended.Primitives;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace MonoGame.Extended.Tests.Primitives
|
||||
{
|
||||
[TestFixture]
|
||||
public class BoundingRectangleTests
|
||||
{
|
||||
public IEnumerable<TestCaseData> ConstructorTestCases
|
||||
{
|
||||
get
|
||||
{
|
||||
yield return
|
||||
new TestCaseData(new Point2(), new Size2()).SetName(
|
||||
"The empty bounding rectangle has the expected position and direction.");
|
||||
yield return
|
||||
new TestCaseData(new Point2(5, 5), new Size2(15, 15)).SetName(
|
||||
"A non-empty bounding rectangle has the expected position and direction.");
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
[TestCaseSource(nameof(ConstructorTestCases))]
|
||||
public void Constructor(Point2 centre, Size2 radius)
|
||||
{
|
||||
var boundingRectangle = new BoundingRectangle(centre, radius);
|
||||
Assert.AreEqual(centre, boundingRectangle.Centre);
|
||||
Assert.AreEqual(radius, boundingRectangle.Radius);
|
||||
}
|
||||
|
||||
public IEnumerable<TestCaseData> CreateFromMinimumMaximumTestCases
|
||||
{
|
||||
get
|
||||
{
|
||||
yield return
|
||||
new TestCaseData(new Point2(), new Point2(), new BoundingRectangle()).SetName(
|
||||
"The bounding rectangle created from the zero minimum point and zero maximum point is the empty bounding rectangle.")
|
||||
;
|
||||
yield return
|
||||
new TestCaseData(new Point2(5, 5), new Point2(15, 15),
|
||||
new BoundingRectangle(new Point2(10, 10), new Size2(5, 5))).SetName(
|
||||
"The bounding rectangle created from the non-zero minimum point and the non-zero maximum point is the expected bounding rectangle.")
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
[TestCaseSource(nameof(CreateFromMinimumMaximumTestCases))]
|
||||
public void CreateFromMinimumMaximum(Point2 minimum, Point2 maximum, BoundingRectangle expectedBoundingRectangle)
|
||||
{
|
||||
var actualBoundingRectangle = BoundingRectangle.CreateFrom(minimum, maximum);
|
||||
Assert.AreEqual(expectedBoundingRectangle, actualBoundingRectangle);
|
||||
}
|
||||
|
||||
public IEnumerable<TestCaseData> CreateFromPointsTestCases
|
||||
{
|
||||
get
|
||||
{
|
||||
yield return
|
||||
new TestCaseData(null, new BoundingRectangle()).SetName(
|
||||
"The bounding rectangle created from null points is the empty bounding rectangle.");
|
||||
yield return
|
||||
new TestCaseData(new Point2[0], new BoundingRectangle()).SetName(
|
||||
"The bounding rectangle created from the empty set of points is the empty bounding rectangle.");
|
||||
yield return
|
||||
new TestCaseData(
|
||||
new[]
|
||||
{
|
||||
new Point2(5, 5), new Point2(10, 10), new Point2(15, 15), new Point2(-5, -5),
|
||||
new Point2(-15, -15)
|
||||
}, new BoundingRectangle(new Point2(0, 0), new Size2(15, 15))).SetName(
|
||||
"The bounding rectangle created from a non-empty set of points is the expected bounding rectangle.");
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
[TestCaseSource(nameof(CreateFromPointsTestCases))]
|
||||
public void CreateFromPoints(Point2[] points, BoundingRectangle expectedBoundingRectangle)
|
||||
{
|
||||
var actualBoundingRectangle = BoundingRectangle.CreateFrom(points);
|
||||
Assert.AreEqual(expectedBoundingRectangle, actualBoundingRectangle);
|
||||
}
|
||||
|
||||
public IEnumerable<TestCaseData> CreateFromTransformedTestCases
|
||||
{
|
||||
get
|
||||
{
|
||||
yield return
|
||||
new TestCaseData(new BoundingRectangle(), Matrix2D.Identity, new BoundingRectangle()).SetName(
|
||||
"The bounding rectangle created from the empty bounding rectangle transformed by the identity matrix is the empty bounding rectangle.")
|
||||
;
|
||||
yield return
|
||||
new TestCaseData(new BoundingRectangle(new Point2(0, 0), new Size2(20, 20)), Matrix2D.CreateScale(2), new BoundingRectangle(new Point2(0, 0), new Size2(40, 40))).SetName(
|
||||
"The bounding rectangle created from a non-empty bounding rectangle transformed by a non-identity matrix is the expected bounding rectangle.")
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
[TestCaseSource(nameof(CreateFromTransformedTestCases))]
|
||||
public void CreateFromTransformed(BoundingRectangle boundingRectangle, Matrix2D transformMatrix,
|
||||
BoundingRectangle expectedBoundingRectangle)
|
||||
{
|
||||
var actualBoundingRectangle = BoundingRectangle.CreateFrom(boundingRectangle, ref transformMatrix);
|
||||
Assert.AreEqual(expectedBoundingRectangle, actualBoundingRectangle);
|
||||
}
|
||||
|
||||
public IEnumerable<TestCaseData> UnionTestCases
|
||||
{
|
||||
get
|
||||
{
|
||||
yield return
|
||||
new TestCaseData(new BoundingRectangle(), new BoundingRectangle(), new BoundingRectangle()).SetName(
|
||||
"The union of two empty bounding rectangles is the empty bounding rectangle.");
|
||||
yield return
|
||||
new TestCaseData(new BoundingRectangle(new Point2(0, 0), new Size2(15, 15)),
|
||||
new BoundingRectangle(new Point2(20, 20), new Size2(40, 40)), new BoundingRectangle(new Point2(20, 20), new Size2(40, 40)))
|
||||
.SetName(
|
||||
"The union of two non-empty bounding rectangles is the expected bounding rectangle.");
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
[TestCaseSource(nameof(UnionTestCases))]
|
||||
public void Union(BoundingRectangle boundingRectangle1, BoundingRectangle boundingRectangle2, BoundingRectangle expectedBoundingRectangle)
|
||||
{
|
||||
Assert.AreEqual(expectedBoundingRectangle, boundingRectangle1.Union(boundingRectangle2));
|
||||
Assert.AreEqual(expectedBoundingRectangle, BoundingRectangle.Union(boundingRectangle1, boundingRectangle2));
|
||||
}
|
||||
|
||||
public IEnumerable<TestCaseData> IntersectionTestCases
|
||||
{
|
||||
get
|
||||
{
|
||||
yield return
|
||||
new TestCaseData(new BoundingRectangle(), new BoundingRectangle(), new BoundingRectangle()).SetName(
|
||||
"The intersection of two empty bounding rectangles is the empty bounding box.");
|
||||
yield return
|
||||
new TestCaseData(new BoundingRectangle(new Point2(-10, -10), new Size2(15, 15)),
|
||||
new BoundingRectangle(new Point2(20, 20), new Size2(40, 40)),
|
||||
new BoundingRectangle(new Point2(-7.5f, -7.5f), new Size2(12.5f, 12.5f))).SetName(
|
||||
"The intersection of two overlapping non-empty bounding rectangles is the expected bounding rectangle.");
|
||||
yield return
|
||||
new TestCaseData(new BoundingRectangle(new Point2(-30, -30), new Size2(15, 15)),
|
||||
new BoundingRectangle(new Point2(20, 20), new Size2(10, 10)),
|
||||
null).SetName(
|
||||
"The intersection of two non-overlapping non-empty bounding rectangles is null.");
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
[TestCaseSource(nameof(IntersectionTestCases))]
|
||||
public void Intersection(BoundingRectangle boundingRectangle1, BoundingRectangle boundingRectangle2,
|
||||
BoundingRectangle? expectedBoundingRectangle)
|
||||
{
|
||||
Assert.AreEqual(expectedBoundingRectangle, boundingRectangle1.Intersection(boundingRectangle2));
|
||||
Assert.AreEqual(expectedBoundingRectangle, BoundingRectangle.Intersection(boundingRectangle1, boundingRectangle2));
|
||||
}
|
||||
|
||||
public IEnumerable<TestCaseData> IntersectsTestCases
|
||||
{
|
||||
get
|
||||
{
|
||||
yield return
|
||||
new TestCaseData(new BoundingRectangle(), new BoundingRectangle(), true).SetName(
|
||||
"Two empty bounding rectangles intersect.");
|
||||
yield return
|
||||
new TestCaseData(new BoundingRectangle(new Point2(-10, -10), new Size2(15, 15)),
|
||||
new BoundingRectangle(new Point2(20, 20), new Size2(40, 40)), true).SetName(
|
||||
"Two overlapping non-empty bounding rectangles intersect.");
|
||||
yield return
|
||||
new TestCaseData(new BoundingRectangle(new Point2(-40, -50), new Size2(15, 15)),
|
||||
new BoundingRectangle(new Point2(20, 20), new Size2(15, 15)), false).SetName(
|
||||
"Two non-overlapping non-empty bounding rectangles do not intersect.");
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
[TestCaseSource(nameof(IntersectsTestCases))]
|
||||
public void Intersects(BoundingRectangle boundingRectangle1, BoundingRectangle boundingRectangle2, bool expectedToIntersect)
|
||||
{
|
||||
Assert.AreEqual(expectedToIntersect, boundingRectangle1.Intersects(boundingRectangle2));
|
||||
Assert.AreEqual(expectedToIntersect, BoundingRectangle.Intersects(boundingRectangle1, boundingRectangle2));
|
||||
}
|
||||
|
||||
public IEnumerable<TestCaseData> ContainsPointTestCases
|
||||
{
|
||||
get
|
||||
{
|
||||
yield return
|
||||
new TestCaseData(new BoundingRectangle(), new Point2(), true).SetName(
|
||||
"The empty bounding rectangle contains the zero point.");
|
||||
yield return
|
||||
new TestCaseData(new BoundingRectangle(new Point2(0, 0), new Size2(15, 15)), new Point2(-15, -15), true)
|
||||
.SetName(
|
||||
"A non-empty bounding rectangle contains a point inside it.");
|
||||
yield return
|
||||
new TestCaseData(new BoundingRectangle(new Point2(0, 0), new Size2(15, 15)), new Point2(-16, 15), false)
|
||||
.SetName(
|
||||
"A non-empty bounding rectangle does not contain a point outside it.");
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
[TestCaseSource(nameof(ContainsPointTestCases))]
|
||||
public void ContainsPoint(BoundingRectangle boundingRectangle, Point2 point, bool expectedToContainPoint)
|
||||
{
|
||||
Assert.AreEqual(expectedToContainPoint, boundingRectangle.Contains(point));
|
||||
Assert.AreEqual(expectedToContainPoint, BoundingRectangle.Contains(boundingRectangle, point));
|
||||
}
|
||||
|
||||
public IEnumerable<TestCaseData> ClosestPointTestCases
|
||||
{
|
||||
get
|
||||
{
|
||||
yield return
|
||||
new TestCaseData(new BoundingRectangle(), new Point2(), new Point2()).SetName(
|
||||
"The closest point on the empty bounding rectangle to the zero point is the zero point.");
|
||||
yield return
|
||||
new TestCaseData(new BoundingRectangle(new Point2(0, 0), new Point2(50, 50)), new Point2(25, 25),
|
||||
new Point2(25, 25)).SetName(
|
||||
"The closest point on a non-empty bounding rectangle to a point which is inside the bounding rectangle is that point.")
|
||||
;
|
||||
yield return
|
||||
new TestCaseData(new BoundingRectangle(new Point2(0, 0), new Point2(50, 50)), new Point2(400, 0),
|
||||
new Point2(50, 0)).SetName(
|
||||
"The closest point on a non-empty bounding rectangle to a point which is outside the bounding rectangle is the expected point.")
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
[TestCaseSource(nameof(ClosestPointTestCases))]
|
||||
public void ClosestPoint(BoundingRectangle boundingRectangle, Point2 point, Point2 expectedClosestPoint)
|
||||
{
|
||||
var actualClosestPoint = boundingRectangle.ClosestPointTo(point);
|
||||
Assert.AreEqual(expectedClosestPoint, actualClosestPoint);
|
||||
}
|
||||
|
||||
public IEnumerable<TestCaseData> EqualityTestCases
|
||||
{
|
||||
get
|
||||
{
|
||||
yield return
|
||||
new TestCaseData(new BoundingRectangle(), new BoundingRectangle(), true).SetName(
|
||||
"Empty bounding rectangles are equal.")
|
||||
;
|
||||
yield return
|
||||
new TestCaseData(
|
||||
new BoundingRectangle(new Point2(0, 0), new Size2(float.MaxValue, float.MinValue)),
|
||||
new BoundingRectangle(new Point2(0, 0),
|
||||
new Point2(float.MinValue, float.MaxValue)), false).SetName(
|
||||
"Two different non-empty bounding rectangles are not equal.");
|
||||
yield return
|
||||
new TestCaseData(
|
||||
new BoundingRectangle(new Point2(0, 0), new Size2(float.MinValue, float.MaxValue)),
|
||||
new BoundingRectangle(new Point2(0, 0),
|
||||
new Size2(float.MinValue, float.MaxValue)), true).SetName(
|
||||
"Two identical non-empty bounding rectangles are equal.");
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
[TestCaseSource(nameof(EqualityTestCases))]
|
||||
public void Equality(BoundingRectangle boundingRectangle1, BoundingRectangle boundingRectangle2, bool expectedToBeEqual)
|
||||
{
|
||||
Assert.IsTrue(Equals(boundingRectangle1, boundingRectangle2) == expectedToBeEqual);
|
||||
Assert.IsTrue(boundingRectangle1 == boundingRectangle2 == expectedToBeEqual);
|
||||
Assert.IsFalse(boundingRectangle1 == boundingRectangle2 != expectedToBeEqual);
|
||||
Assert.IsTrue(boundingRectangle1.Equals(boundingRectangle2) == expectedToBeEqual);
|
||||
|
||||
if (expectedToBeEqual)
|
||||
Assert.AreEqual(boundingRectangle1.GetHashCode(), boundingRectangle2.GetHashCode());
|
||||
}
|
||||
|
||||
public IEnumerable<TestCaseData> InequalityTestCases
|
||||
{
|
||||
get
|
||||
{
|
||||
yield return
|
||||
new TestCaseData(new BoundingRectangle(), null, false).SetName(
|
||||
"A bounding rectangle is not equal to a null object.");
|
||||
yield return
|
||||
new TestCaseData(new BoundingRectangle(), new object(), false).SetName(
|
||||
"A bounding rectangle is not equal to an instantiated object.");
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
[TestCaseSource(nameof(InequalityTestCases))]
|
||||
public void Inequality(BoundingRectangle boundingRectangle, object obj, bool expectedToBeEqual)
|
||||
{
|
||||
Assert.IsTrue(boundingRectangle.Equals(obj) == expectedToBeEqual);
|
||||
}
|
||||
|
||||
public IEnumerable<TestCaseData> HashCodeTestCases
|
||||
{
|
||||
get
|
||||
{
|
||||
yield return
|
||||
new TestCaseData(new BoundingRectangle(), new BoundingRectangle(), true).SetName(
|
||||
"Two empty bounding rectangles have the same hash code.");
|
||||
yield return
|
||||
new TestCaseData(new BoundingRectangle(new Point2(0, 0), new Size2(50, 50)),
|
||||
new BoundingRectangle(new Point2(0, 0), new Size2(50, 50)), true).SetName(
|
||||
"Two indentical non-empty bounding rectangles have the same hash code.");
|
||||
yield return
|
||||
new TestCaseData(new BoundingRectangle(new Point2(0, 0), new Size2(50, 50)),
|
||||
new BoundingRectangle(new Point2(50, 50), new Size2(50, 50)), false).SetName(
|
||||
"Two different non-empty bounding rectangles do not have the same hash code.");
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
[TestCaseSource(nameof(HashCodeTestCases))]
|
||||
public void HashCode(BoundingRectangle boundingRectangle1, BoundingRectangle boundingRectangle2, bool expectedThatHashCodesAreEqual)
|
||||
{
|
||||
var hashCode1 = boundingRectangle1.GetHashCode();
|
||||
var hashCode2 = boundingRectangle2.GetHashCode();
|
||||
if (expectedThatHashCodesAreEqual)
|
||||
Assert.AreEqual(hashCode1, hashCode2);
|
||||
else
|
||||
Assert.AreNotEqual(hashCode1, hashCode2);
|
||||
}
|
||||
|
||||
public IEnumerable<TestCaseData> ToRectangleTestCases
|
||||
{
|
||||
get
|
||||
{
|
||||
yield return
|
||||
new TestCaseData(new BoundingRectangle(), new Rectangle()).SetName(
|
||||
"The empty bounding rectangle point converted to a rectangle is the empty rectangle.");
|
||||
yield return
|
||||
new TestCaseData(new BoundingRectangle(new Point2(25, 25), new Size2(25, 25)),
|
||||
new Rectangle(0, 0, 50, 50)).SetName(
|
||||
"A non-empty bounding rectangle converted to a rectangle is the expected rectangle.");
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
[TestCaseSource(nameof(ToRectangleTestCases))]
|
||||
public void ToRectangle(BoundingRectangle boundingRectangle, Rectangle expectedRectangle)
|
||||
{
|
||||
var actualRectangle = (Rectangle)boundingRectangle;
|
||||
Assert.AreEqual(expectedRectangle, actualRectangle);
|
||||
}
|
||||
|
||||
public IEnumerable<TestCaseData> FromRectangleTestCases
|
||||
{
|
||||
get
|
||||
{
|
||||
yield return
|
||||
new TestCaseData(new Rectangle(), new BoundingRectangle()).SetName(
|
||||
"The empty rectangle converted to a bounding rectangle is the empty bounding rectangle.");
|
||||
yield return
|
||||
new TestCaseData(new Rectangle(0, 0, 50, 50),
|
||||
new BoundingRectangle(new Point2(25, 25), new Size2(25, 25))).SetName(
|
||||
"A non-empty rectangle converted to a bounding rectangle is the expected bounding rectangle.");
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
[TestCaseSource(nameof(FromRectangleTestCases))]
|
||||
public void FromRectangle(Rectangle rectangle, BoundingRectangle expectedBoundingRectangle)
|
||||
{
|
||||
var actualBoundingRectangle = (BoundingRectangle)rectangle;
|
||||
Assert.AreEqual(expectedBoundingRectangle, actualBoundingRectangle);
|
||||
}
|
||||
|
||||
public IEnumerable<TestCaseData> StringCases
|
||||
{
|
||||
get
|
||||
{
|
||||
yield return
|
||||
new TestCaseData(new BoundingRectangle(),
|
||||
string.Format(CultureInfo.CurrentCulture, "Centre: {0}, Radius: {1}", new Point2(),
|
||||
new Size2())).SetName(
|
||||
"The empty bounding rectangle has the expected string representation using the current culture.");
|
||||
yield return new TestCaseData(new BoundingRectangle(new Point2(5.1f, -5.123f), new Size2(5.4f, -5.4123f)),
|
||||
string.Format(CultureInfo.CurrentCulture, "Centre: {0}, Radius: {1}", new Point2(5.1f, -5.123f),
|
||||
new Size2(5.4f, -5.4123f))).SetName(
|
||||
"A non-empty bounding rectangle has the expected string representation using the current culture.");
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
[TestCaseSource(nameof(StringCases))]
|
||||
public void String(BoundingRectangle boundingRectangle, string expectedString)
|
||||
{
|
||||
var actualString = boundingRectangle.ToString();
|
||||
Assert.AreEqual(expectedString, actualString);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -62,36 +62,36 @@ namespace MonoGame.Extended.Tests.Primitives
|
||||
Assert.AreEqual(new Vector2(-10.123f, 10.123f), ray.Direction);
|
||||
}
|
||||
|
||||
public IEnumerable<TestCaseData> IntersectsBoundingBoxTestCases
|
||||
public IEnumerable<TestCaseData> IntersectsBoundingRectangleTestCases
|
||||
{
|
||||
get
|
||||
{
|
||||
yield return
|
||||
new TestCaseData(new Ray2D(), new BoundingBox2D(), true, Point2.Zero, Point2.Zero).SetName(
|
||||
new TestCaseData(new Ray2D(), new BoundingRectangle(), true, Point2.Zero, Point2.Zero).SetName(
|
||||
"The degenerate ray intersects the empty bounding box.");
|
||||
yield return
|
||||
new TestCaseData(new Ray2D(new Point2(-75, -75), new Vector2(75, -75)),
|
||||
new BoundingBox2D(0, 0, 50, 50), false, Point2.NaN, Point2.NaN).SetName(
|
||||
new BoundingRectangle(new Point2(), new Size2(50, 50)), false, Point2.NaN, Point2.NaN).SetName(
|
||||
"A non-degenerate ray that does not cross a non-empty bounding box does not intersect the bounding box.");
|
||||
yield return
|
||||
new TestCaseData(new Ray2D(new Point2(0, 0), new Vector2(25, 0)), new BoundingBox2D(0, 0, 50, 50),
|
||||
new TestCaseData(new Ray2D(new Point2(0, 0), new Vector2(25, 0)), new BoundingRectangle(new Point2(), new Size2(50, 50)),
|
||||
true, new Point2(0, 0), new Point2(50, 0)).SetName(
|
||||
"A non-degenerate ray starting from inside a non-empty bounding box intersects the bounding box.");
|
||||
yield return
|
||||
new TestCaseData(new Ray2D(new Point2(-100, 0), new Vector2(100, 0)),
|
||||
new BoundingBox2D(0, 0, 50, 50),
|
||||
new BoundingRectangle(new Point2(), new Size2(50, 50)),
|
||||
true, new Point2(-50, 0), new Point2(50, 0)).SetName(
|
||||
"A non-degenerate ray crossing a non-empty bounding box intersects the bounding box.");
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
[TestCaseSource(nameof(IntersectsBoundingBoxTestCases))]
|
||||
public void IntersectsBoundingBox(Ray2D ray, BoundingBox2D axisAlignedBoundingBox, bool expectedResult,
|
||||
[TestCaseSource(nameof(IntersectsBoundingRectangleTestCases))]
|
||||
public void IntersectsBoundingRectangle(Ray2D ray, BoundingRectangle boundingRectangle, bool expectedResult,
|
||||
Point2 firstExpectedIntersectionPoint, Point2 secondExpectedIntersectionPoint)
|
||||
{
|
||||
float rayNearDistance, rayFarDistance;
|
||||
var actualResult = ray.Intersects(axisAlignedBoundingBox, out rayNearDistance, out rayFarDistance);
|
||||
var actualResult = ray.Intersects(boundingRectangle, out rayNearDistance, out rayFarDistance);
|
||||
Assert.AreEqual(expectedResult, actualResult);
|
||||
|
||||
if (actualResult)
|
||||
|
||||
@@ -98,36 +98,36 @@ namespace MonoGame.Extended.Tests.Primitives
|
||||
Assert.AreEqual(expectedDistance, actualDistance);
|
||||
}
|
||||
|
||||
public IEnumerable<TestCaseData> IntersectsBoundingBoxTestCases
|
||||
public IEnumerable<TestCaseData> IntersectsBoundingRectangleTestCases
|
||||
{
|
||||
get
|
||||
{
|
||||
yield return
|
||||
new TestCaseData(new Segment2D(), new BoundingBox2D(), true, Point2.Zero).SetName(
|
||||
new TestCaseData(new Segment2D(), new BoundingRectangle(), true, Point2.Zero).SetName(
|
||||
"The empty segment intersects the empty bounding box.");
|
||||
yield return
|
||||
new TestCaseData(new Segment2D(new Point2(-75, -75), new Point2(75, -75)),
|
||||
new BoundingBox2D(0, 0, 50, 50), false, Point2.NaN).SetName(
|
||||
new BoundingRectangle(new Point2(), new Size2(50, 50)), false, Point2.NaN).SetName(
|
||||
"A non-empty segment outside a non-empty bounding box does not intersect the bounding box.");
|
||||
yield return
|
||||
new TestCaseData(new Segment2D(new Point2(0, 0), new Point2(25, 0)), new BoundingBox2D(0, 0, 50, 50),
|
||||
new TestCaseData(new Segment2D(new Point2(0, 0), new Point2(25, 0)), new BoundingRectangle(new Point2(), new Size2(50, 50)),
|
||||
true, new Point2(0, 0)).SetName(
|
||||
"A non-empty segment inside a non-empty bounding box intersects the bounding box.");
|
||||
yield return
|
||||
new TestCaseData(new Segment2D(new Point2(-100, 0), new Point2(100, 0)),
|
||||
new BoundingBox2D(0, 0, 50, 50),
|
||||
new BoundingRectangle(new Point2(), new Size2(50, 50)),
|
||||
true, new Point2(-50, 0)).SetName(
|
||||
"A non-empty segment crossing a non-empty bounding box intersects the bounding box.");
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
[TestCaseSource(nameof(IntersectsBoundingBoxTestCases))]
|
||||
public void IntersectsBoundingBox(Segment2D segment, BoundingBox2D axisAlignedBoundingBox, bool expectedResult,
|
||||
[TestCaseSource(nameof(IntersectsBoundingRectangleTestCases))]
|
||||
public void IntersectsBoundingRectangle(Segment2D segment, BoundingRectangle boundingRectangle, bool expectedResult,
|
||||
Point2 expectedIntersectionPoint)
|
||||
{
|
||||
Point2 actualIntersectionPoint;
|
||||
var actualResult = segment.Intersects(axisAlignedBoundingBox, out actualIntersectionPoint);
|
||||
var actualResult = segment.Intersects(boundingRectangle, out actualIntersectionPoint);
|
||||
Assert.AreEqual(expectedResult, actualResult);
|
||||
|
||||
if (actualResult)
|
||||
|
||||
@@ -44,28 +44,52 @@ namespace MonoGame.Extended.BitmapFonts
|
||||
|
||||
public Size GetSize(string text)
|
||||
{
|
||||
var width = 0;
|
||||
var height = 0;
|
||||
if (text == null) throw new ArgumentNullException(nameof(text));
|
||||
|
||||
var totalWidth = 0;
|
||||
var lineWidth = 0;
|
||||
var totalHeight = 0;
|
||||
var lineHeight = 0;
|
||||
|
||||
const int newlineCodePoint = '\n';
|
||||
|
||||
var codePoints = GetUnicodeCodePoints(text).ToArray();
|
||||
|
||||
for (int i = 0, l = codePoints.Length; i < l; i++)
|
||||
{
|
||||
BitmapFontRegion fontRegion;
|
||||
var c = codePoints[i];
|
||||
var character = codePoints[i];
|
||||
var nextCharacter = character;
|
||||
|
||||
if (_characterMap.TryGetValue(c, out fontRegion))
|
||||
if (i < l - 1) nextCharacter = codePoints[i + 1];
|
||||
|
||||
if (_characterMap.TryGetValue(character, out fontRegion))
|
||||
{
|
||||
if (i != text.Length - 1)
|
||||
width += fontRegion.XAdvance + LetterSpacing;
|
||||
// Add LetterSpacing unless end of string or next character is not in _characterMap
|
||||
if (i != text.Length - 1 && _characterMap.ContainsKey(nextCharacter))
|
||||
lineWidth += fontRegion.XAdvance + LetterSpacing;
|
||||
else
|
||||
width += fontRegion.XOffset + fontRegion.Width;
|
||||
lineWidth += fontRegion.XOffset + fontRegion.Width;
|
||||
|
||||
if (fontRegion.Height + fontRegion.YOffset > height)
|
||||
height = fontRegion.Height + fontRegion.YOffset;
|
||||
if (fontRegion.Height + fontRegion.YOffset > lineHeight)
|
||||
lineHeight = fontRegion.Height + fontRegion.YOffset;
|
||||
}
|
||||
|
||||
if (character == newlineCodePoint)
|
||||
{
|
||||
totalHeight += lineHeight;
|
||||
if (totalWidth < lineWidth) totalWidth = lineWidth;
|
||||
|
||||
lineHeight = 0;
|
||||
lineWidth = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return new Size(width, height);
|
||||
if (totalWidth == 0)
|
||||
totalWidth = lineWidth;
|
||||
totalHeight += lineHeight;
|
||||
|
||||
return new Size(totalWidth, totalHeight);
|
||||
}
|
||||
|
||||
public Size MeasureString(string text)
|
||||
|
||||
@@ -111,12 +111,12 @@ namespace MonoGame.Extended.Maps.Tiled
|
||||
for (var i = 0; i < tilesCount; i++)
|
||||
{
|
||||
var thisTileIndexes = new short[6];
|
||||
thisTileIndexes[0] = (short) (4*indexOffset);
|
||||
thisTileIndexes[1] = (short) (4*indexOffset + 1);
|
||||
thisTileIndexes[2] = (short) (4*indexOffset + 2);
|
||||
thisTileIndexes[3] = (short) (4*indexOffset + 1);
|
||||
thisTileIndexes[4] = (short) (4*indexOffset + 3);
|
||||
thisTileIndexes[5] = (short) (4*indexOffset + 2);
|
||||
thisTileIndexes[0] = (short)(4 * indexOffset);
|
||||
thisTileIndexes[1] = (short)(4 * indexOffset + 1);
|
||||
thisTileIndexes[2] = (short)(4 * indexOffset + 2);
|
||||
thisTileIndexes[3] = (short)(4 * indexOffset + 1);
|
||||
thisTileIndexes[4] = (short)(4 * indexOffset + 3);
|
||||
thisTileIndexes[5] = (short)(4 * indexOffset + 2);
|
||||
tileIndexes.AddRange(thisTileIndexes);
|
||||
indexOffset++;
|
||||
}
|
||||
@@ -185,29 +185,39 @@ namespace MonoGame.Extended.Maps.Tiled
|
||||
|
||||
foreach (var layer in _layers)
|
||||
{
|
||||
|
||||
|
||||
var tiledTileLayer = layer as TiledTileLayer;
|
||||
|
||||
if (tiledTileLayer != null)
|
||||
{
|
||||
var tileLayer = tiledTileLayer;
|
||||
var indexCount = tileLayer.NotBlankTilesCount * 6;
|
||||
var primitivesCount = tileLayer.NotBlankTilesCount * 2;
|
||||
var indexCount = 6;
|
||||
var primitivesCount = 2;
|
||||
|
||||
if (tileLayer.IsVisible && tileLayer.NotBlankTilesCount > 0)
|
||||
if (tileLayer.IsVisible)
|
||||
{
|
||||
if (_basicEffect.Texture != _tilesets[0].Texture)
|
||||
_basicEffect.Texture = _tilesets[0].Texture;
|
||||
|
||||
pass.Apply();
|
||||
_graphicsDevice.DrawIndexedPrimitives(
|
||||
primitiveType: PrimitiveType.TriangleList,
|
||||
baseVertex: 0,
|
||||
startIndex: tilesIndexesSoFar,
|
||||
primitiveCount: primitivesCount
|
||||
);
|
||||
foreach (var tileset in _tilesets)
|
||||
{
|
||||
var tilesToDraw = tiledTileLayer.Tiles.Count(x => tileset.Equals(GetTileSetByTileId(x.Id)));
|
||||
|
||||
if (tilesToDraw > 0)
|
||||
{
|
||||
if (_basicEffect.Texture != tileset.Texture)
|
||||
_basicEffect.Texture = tileset.Texture;
|
||||
|
||||
pass.Apply();
|
||||
_graphicsDevice.DrawIndexedPrimitives(
|
||||
primitiveType: PrimitiveType.TriangleList,
|
||||
baseVertex: 0,
|
||||
startIndex: tilesIndexesSoFar,
|
||||
primitiveCount: primitivesCount * tilesToDraw
|
||||
);
|
||||
tilesIndexesSoFar += indexCount * tilesToDraw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tilesIndexesSoFar += indexCount;
|
||||
}
|
||||
else if (layer is TiledImageLayer)
|
||||
{
|
||||
@@ -230,6 +240,22 @@ namespace MonoGame.Extended.Maps.Tiled
|
||||
}
|
||||
}
|
||||
}
|
||||
TiledTileset GetTileSetByTileId(int id)
|
||||
{
|
||||
//if it's a blank tile there is no tileset
|
||||
if (id == 0) return null;
|
||||
|
||||
int tilesetIndex;
|
||||
|
||||
//Loop backwards because they are in order of First Tile ID
|
||||
for (tilesetIndex = _tilesets.Count - 1; tilesetIndex > 0; tilesetIndex--)
|
||||
{
|
||||
if (_tilesets[tilesetIndex].FirstId <= id)
|
||||
break;
|
||||
}
|
||||
|
||||
return _tilesets[tilesetIndex];
|
||||
}
|
||||
|
||||
public TextureRegion2D GetTileRegion(int id)
|
||||
{
|
||||
|
||||
@@ -17,10 +17,10 @@ namespace MonoGame.Extended.Maps.Tiled
|
||||
|
||||
_renderTargetSpriteBatch = new SpriteBatch(graphicsDevice);
|
||||
_map = map;
|
||||
_tiles = CreateTiles(data);
|
||||
_tiles = CreateTiles(data, map.Tilesets);
|
||||
_animatedTiles = new List<TiledTile>();
|
||||
}
|
||||
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
_renderTargetSpriteBatch.Dispose();
|
||||
@@ -43,22 +43,39 @@ namespace MonoGame.Extended.Maps.Tiled
|
||||
public int TileWidth => _map.TileWidth;
|
||||
public int TileHeight => _map.TileHeight;
|
||||
|
||||
private TiledTile[] CreateTiles(int[] data)
|
||||
private TiledTile[] CreateTiles(int[] data, IReadOnlyList<TiledTileset> tilesets)
|
||||
{
|
||||
var tiles = new TiledTile[data.Length];
|
||||
var index = 0;
|
||||
var tileData = new TiledTile[data.Length];
|
||||
var tiles = tilesets.Select(tileset => new List<TiledTile>()).ToList();
|
||||
var tileDataIndex = 0;
|
||||
|
||||
for (var y = 0; y < Height; y++)
|
||||
{
|
||||
for (var x = 0; x < Width; x++)
|
||||
{
|
||||
tiles[x + y * Width] = new TiledTile(data[index], x, y, _map.GetTileSetTileById(data[index]));
|
||||
index++;
|
||||
int tilesetIndex;
|
||||
|
||||
//find which list to add it to
|
||||
for (tilesetIndex = tilesets.Count - 1; tilesetIndex > 0; tilesetIndex--)
|
||||
if (tilesets[tilesetIndex].FirstId <= data[tileDataIndex])
|
||||
break;
|
||||
|
||||
tiles.ElementAt(tilesetIndex).Add(new TiledTile(data[tileDataIndex], x, y, _map.GetTileSetTileById(data[tileDataIndex])));
|
||||
tileDataIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
NotBlankTilesCount = tiles.Count(x => x.Id != 0);
|
||||
return tiles;
|
||||
//Place each Tile in order of their respective Tileset for optimal drawing
|
||||
var tiledDataIndex = 0;
|
||||
foreach (var tileChunk in tiles)
|
||||
foreach (var tile in tileChunk)
|
||||
{
|
||||
tileData[tiledDataIndex] = tile;
|
||||
tiledDataIndex++;
|
||||
}
|
||||
|
||||
NotBlankTilesCount = tileData.Count(x => x.Id != 0);
|
||||
return tileData;
|
||||
}
|
||||
|
||||
internal VertexPositionTexture[] BuildVertices(float depth)
|
||||
@@ -79,7 +96,7 @@ namespace MonoGame.Extended.Maps.Tiled
|
||||
continue;
|
||||
|
||||
var region = _map.GetTileRegion(tile.Id);
|
||||
var point = GetTileLocationFunction()(tile).ToVector2() + new Vector2(OffsetX, OffsetY);
|
||||
var point = GetTileLocationFunction()(tile);
|
||||
var tileWidth = region.Width;
|
||||
var tileHeight = region.Height;
|
||||
var tc0 = Vector2.Zero;
|
||||
@@ -119,12 +136,12 @@ namespace MonoGame.Extended.Maps.Tiled
|
||||
//var vr = visibleRectangle ?? new Rectangle(0, 0, _map.WidthInPixels, _map.HeightInPixels);
|
||||
var vr = new Rectangle(0, 0, _map.WidthInPixels, _map.HeightInPixels);
|
||||
var renderOrderFunction = GetRenderOrderFunction();
|
||||
var firstCol = vr.Left < 0 ? 0 : (int) Math.Floor(vr.Left/(float) _map.TileWidth);
|
||||
var firstRow = vr.Top < 0 ? 0 : (int) Math.Floor(vr.Top/(float) _map.TileHeight);
|
||||
var firstCol = vr.Left < 0 ? 0 : (int)Math.Floor(vr.Left / (float)_map.TileWidth);
|
||||
var firstRow = vr.Top < 0 ? 0 : (int)Math.Floor(vr.Top / (float)_map.TileHeight);
|
||||
|
||||
// +3 to cover any gaps
|
||||
var columns = Math.Min(_map.Width, vr.Width/_map.TileWidth);
|
||||
var rows = Math.Min(_map.Height, vr.Height/_map.TileHeight);
|
||||
var columns = Math.Min(_map.Width, vr.Width / _map.TileWidth);
|
||||
var rows = Math.Min(_map.Height, vr.Height / _map.TileHeight);
|
||||
|
||||
_renderTargetSpriteBatch.Begin(blendState: BlendState.AlphaBlend, samplerState: SamplerState.PointClamp);
|
||||
|
||||
@@ -144,14 +161,14 @@ namespace MonoGame.Extended.Maps.Tiled
|
||||
_renderTargetSpriteBatch.End();
|
||||
}
|
||||
_uniqueTileSetTiles = _animatedTiles.Select(t => t.TileSetTile).Distinct().ToList();
|
||||
spriteBatch.Draw(_renderTarget, Vector2.Zero, Color.White);
|
||||
spriteBatch.Draw(_renderTarget, Vector2.Zero, Color.White);
|
||||
}
|
||||
else
|
||||
{
|
||||
spriteBatch.Draw(_renderTarget, Vector2.Zero, Color.White);
|
||||
if (gameTime == null || _animatedTiles.Count == 0) return;
|
||||
double deltaTime = gameTime.ElapsedGameTime.TotalMilliseconds;
|
||||
foreach(var tileSetTile in _uniqueTileSetTiles)
|
||||
foreach (var tileSetTile in _uniqueTileSetTiles)
|
||||
{
|
||||
tileSetTile.Update(deltaTime);
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
<ItemGroup>
|
||||
<Compile Include="Animations\Animation.cs" />
|
||||
<Compile Include="Animations\AnimationComponent.cs" />
|
||||
<Compile Include="Primitives\BoundingBox2D.cs" />
|
||||
<Compile Include="Primitives\BoundingRectangle.cs" />
|
||||
<Compile Include="Primitives\Point2.cs" />
|
||||
<Compile Include="Primitives\Ray2D.cs" />
|
||||
<Compile Include="Primitives\RayHelper.cs" />
|
||||
|
||||
@@ -0,0 +1,435 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace MonoGame.Extended.Primitives
|
||||
{
|
||||
// Real-Time Collision Detection, Christer Ericson, 2005. Chapter 4.2; Bounding Volumes - Axis-aligned Bounding Boxes (AABBs). pg 77
|
||||
|
||||
/// <summary>
|
||||
/// An axis-aligned, four sided, two dimensional box defined by a centre <see cref="Point2" /> and a radius <see cref="Size2" />.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// An <see cref="BoundingRectangle" /> is categorized by having its faces oriented in such a way that its
|
||||
/// face normals are at all times parallel with the axes of the given coordinate system.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// The <see cref="BoundingRectangle" /> of a rotated <see cref="BoundingRectangle" /> will be equivalent or larger in size
|
||||
/// than the original depending on the angle of rotation.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
/// <seealso cref="IEquatable{T}" />
|
||||
/// <seealso cref="IEquatableByRef{T}" />
|
||||
[DebuggerDisplay("{DebugDisplayString,nq}")]
|
||||
public struct BoundingRectangle : IEquatable<BoundingRectangle>,
|
||||
IEquatableByRef<BoundingRectangle>
|
||||
{
|
||||
/// <summary>
|
||||
/// The centre position of this <see cref="BoundingRectangle" />.
|
||||
/// </summary>
|
||||
public Point2 Centre;
|
||||
|
||||
/// <summary>
|
||||
/// The dimensions of this <see cref="BoundingRectangle" />.
|
||||
/// </summary>
|
||||
public Size2 Radius;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BoundingRectangle" /> structure from the specified centre
|
||||
/// <see cref="Point2" /> and the radius <see cref="Size2" />.
|
||||
/// </summary>
|
||||
/// <param name="centre">The centre <see cref="Point2" />.</param>
|
||||
/// <param name="radius">The radius <see cref="Size2" />.</param>
|
||||
public BoundingRectangle(Point2 centre, Size2 radius)
|
||||
{
|
||||
Centre = centre;
|
||||
Radius = radius;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Computes the <see cref="BoundingRectangle" /> from a minimum <see cref="Point2" /> and maximum
|
||||
/// <see cref="Point2" />.
|
||||
/// </summary>
|
||||
/// <param name="minimum">The minimum point.</param>
|
||||
/// <param name="maximum">The maximum point.</param>
|
||||
/// <returns>An <see cref="BoundingRectangle" />.</returns>
|
||||
public static BoundingRectangle CreateFrom(Point2 minimum, Point2 maximum)
|
||||
{
|
||||
var centre = new Point2((maximum.X + minimum.X) * 0.5f, (maximum.Y + minimum.Y) * 0.5f);
|
||||
var radius = new Size2((maximum.X - minimum.X) * 0.5f, (maximum.Y - minimum.Y) * 0.5f);
|
||||
return new BoundingRectangle(centre, radius);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Computes the <see cref="BoundingRectangle" /> from a list of <see cref="Point2" /> structures.
|
||||
/// </summary>
|
||||
/// <param name="points">The points.</param>
|
||||
/// <returns>An <see cref="BoundingRectangle" />.</returns>
|
||||
public static BoundingRectangle CreateFrom(IReadOnlyList<Point2> points)
|
||||
{
|
||||
// Real-Time Collision Detection, Christer Ericson, 2005. Chapter 4.2; Bounding Volumes - Axis-aligned Bounding Boxes (AABBs). pg 82-84
|
||||
|
||||
if ((points == null) || (points.Count == 0))
|
||||
return new BoundingRectangle();
|
||||
|
||||
var minimum = new Vector2(float.MaxValue);
|
||||
var maximum = new Vector2(float.MinValue);
|
||||
|
||||
// ReSharper disable once ForCanBeConvertedToForeach
|
||||
for (var index = 0; index < points.Count; ++index)
|
||||
{
|
||||
var point = points[index];
|
||||
minimum = Point2.Minimum(minimum, point);
|
||||
maximum = Point2.Maximum(maximum, point);
|
||||
}
|
||||
|
||||
return CreateFrom(minimum, maximum);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Computes the <see cref="BoundingRectangle" /> from the specified <see cref="BoundingRectangle" /> transformed by the
|
||||
/// specified <see cref="Matrix2D" />.
|
||||
/// </summary>
|
||||
/// <param name="boundingRectangle">The bounding rectangle.</param>
|
||||
/// <param name="transformMatrix">The transform matrix.</param>
|
||||
/// <returns>
|
||||
/// The <see cref="BoundingRectangle" /> from the <paramref name="boundingRectangle" /> transformed by the
|
||||
/// <paramref name="transformMatrix" />.
|
||||
/// </returns>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// If a transformed <see cref="BoundingRectangle" /> is used for <paramref name="boundingRectangle" /> then the
|
||||
/// resulting <see cref="BoundingRectangle" /> will have the compounded transformation, which most likely is
|
||||
/// not desired.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public static BoundingRectangle CreateFrom(BoundingRectangle boundingRectangle,
|
||||
ref Matrix2D transformMatrix)
|
||||
{
|
||||
// Real-Time Collision Detection, Christer Ericson, 2005. Chapter 4.2; Bounding Volumes - Axis-aligned Bounding Boxes (AABBs). pg 86-87
|
||||
|
||||
var centre = transformMatrix.Transform(boundingRectangle.Centre);
|
||||
var radius = boundingRectangle.Radius;
|
||||
radius.Width = radius.Width * Math.Abs(transformMatrix.M11) + radius.Width * Math.Abs(transformMatrix.M12) +
|
||||
radius.Width * Math.Abs(transformMatrix.M31);
|
||||
radius.Height = radius.Height * Math.Abs(transformMatrix.M21) + radius.Height * Math.Abs(transformMatrix.M22) +
|
||||
radius.Height * Math.Abs(transformMatrix.M32);
|
||||
return new BoundingRectangle(centre, radius);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Computes the <see cref="BoundingRectangle" /> that contains the two specified
|
||||
/// <see cref="BoundingRectangle" /> structures.
|
||||
/// </summary>
|
||||
/// <param name="first">The first bounding rectangle.</param>
|
||||
/// <param name="second">The second bounding rectangle.</param>
|
||||
/// <returns>
|
||||
/// An <see cref="BoundingRectangle" /> that contains both the <paramref name="first" /> and the
|
||||
/// <paramref name="second" />.
|
||||
/// </returns>
|
||||
public static BoundingRectangle Union(BoundingRectangle first, BoundingRectangle second)
|
||||
{
|
||||
// Real-Time Collision Detection, Christer Ericson, 2005. Chapter 6.5; Bounding Volume Hierarchies - Merging Bounding Volumes. pg 267
|
||||
|
||||
var firstMinimum = first.Centre - first.Radius;
|
||||
var firstMaximum = first.Centre + first.Radius;
|
||||
var secondMinimum = second.Centre - second.Radius;
|
||||
var secondMaximum = second.Centre + second.Radius;
|
||||
|
||||
var minimum = Point2.Minimum(firstMinimum, secondMinimum);
|
||||
var maximum = Point2.Maximum(firstMaximum, secondMaximum);
|
||||
|
||||
return CreateFrom(minimum, maximum);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Computes the <see cref="BoundingRectangle" /> that contains both the specified
|
||||
/// <see cref="BoundingRectangle" /> and this <see cref="BoundingRectangle" />.
|
||||
/// </summary>
|
||||
/// <param name="boundingRectangle">The bounding rectangle.</param>
|
||||
/// <returns>
|
||||
/// An <see cref="BoundingRectangle" /> that contains both the <paramref name="boundingRectangle" /> and
|
||||
/// this
|
||||
/// <see cref="BoundingRectangle" />.
|
||||
/// </returns>
|
||||
public BoundingRectangle Union(BoundingRectangle boundingRectangle)
|
||||
{
|
||||
return Union(this, boundingRectangle);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Computes the <see cref="Nullable{BoundingRectangle}" /> that is in common between the two specified
|
||||
/// <see cref="BoundingRectangle" /> structures.
|
||||
/// </summary>
|
||||
/// <param name="first">The first bounding rectangle.</param>
|
||||
/// <param name="second">The second bounding rectangle.</param>
|
||||
/// <returns>
|
||||
/// An <see cref="BoundingRectangle" /> that is in common between both the <paramref name="first" /> and
|
||||
/// the <paramref name="second" />, if they intersect; otherwise, <code>null</code>.
|
||||
/// </returns>
|
||||
public static BoundingRectangle? Intersection(BoundingRectangle first,
|
||||
BoundingRectangle second)
|
||||
{
|
||||
var firstMinimum = first.Centre - first.Radius;
|
||||
var firstMaximum = first.Centre + first.Radius;
|
||||
var secondMinimum = second.Centre - second.Radius;
|
||||
var secondMaximum = second.Centre + second.Radius;
|
||||
|
||||
var minimum = Point2.Maximum(firstMinimum, secondMinimum);
|
||||
var maximum = Point2.Minimum(firstMaximum, secondMaximum);
|
||||
|
||||
if ((maximum.X < minimum.X) || (maximum.Y < minimum.Y))
|
||||
return null;
|
||||
|
||||
return CreateFrom(minimum, maximum);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates this <see cref="BoundingRectangle" /> from a list of <see cref="Point2" /> structures.
|
||||
/// </summary>
|
||||
/// <param name="points">The points.</param>
|
||||
public void UpdateFromPoints(IReadOnlyList<Point2> points)
|
||||
{
|
||||
var boundingRectangle = CreateFrom(points);
|
||||
Centre = boundingRectangle.Centre;
|
||||
Radius = boundingRectangle.Radius;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Computes the <see cref="BoundingRectangle" /> that is in common between the specified
|
||||
/// <see cref="BoundingRectangle" /> and this <see cref="BoundingRectangle" />.
|
||||
/// </summary>
|
||||
/// <param name="boundingRectangle">The bounding rectangle.</param>
|
||||
/// <returns>
|
||||
/// An <see cref="BoundingRectangle" /> that is in common between the specified
|
||||
/// <see cref="BoundingRectangle" /> and this <see cref="BoundingRectangle" />.
|
||||
/// </returns>
|
||||
public BoundingRectangle? Intersection(BoundingRectangle boundingRectangle)
|
||||
{
|
||||
return Intersection(this, boundingRectangle);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the two specified <see cref="BoundingRectangle" /> structures intersect.
|
||||
/// </summary>
|
||||
/// <param name="first">The first bounding rectangle.</param>
|
||||
/// <param name="second">The second bounding rectangle.</param>
|
||||
/// <returns>
|
||||
/// <c>true</c> if the <paramref name="first" /> intersects with the <see cref="second" />; otherwise, <c>false</c>.
|
||||
/// </returns>
|
||||
public static bool Intersects(BoundingRectangle first, BoundingRectangle second)
|
||||
{
|
||||
// Real-Time Collision Detection, Christer Ericson, 2005. Chapter 4.2; Bounding Volumes - Axis-aligned Bounding Boxes (AABBs). pg 80
|
||||
|
||||
var distance = first.Centre - second.Centre;
|
||||
var radius = first.Radius + second.Radius;
|
||||
return (Math.Abs(distance.X) <= radius.Width) && (Math.Abs(distance.Y) <= radius.Height);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified <see cref="BoundingRectangle" /> intersects with this
|
||||
/// <see cref="BoundingRectangle" />.
|
||||
/// </summary>
|
||||
/// <param name="boundingRectangle">The bounding rectangle.</param>
|
||||
/// <returns>
|
||||
/// <c>true</c> if the <paramref name="boundingRectangle" /> intersects with this
|
||||
/// <see cref="BoundingRectangle" />; otherwise,
|
||||
/// <c>false</c>.
|
||||
/// </returns>
|
||||
public bool Intersects(BoundingRectangle boundingRectangle)
|
||||
{
|
||||
return Intersects(this, boundingRectangle);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified <see cref="BoundingRectangle" /> contains the specified
|
||||
/// <see cref="Point2" />.
|
||||
/// </summary>
|
||||
/// <param name="boundingRectangle">The bounding rectangle.</param>
|
||||
/// <param name="point">The point.</param>
|
||||
/// <returns>
|
||||
/// <c>true</c> if the <paramref name="boundingRectangle" /> contains the <paramref name="point" />; otherwise,
|
||||
/// <c>false</c>.
|
||||
/// </returns>
|
||||
public static bool Contains(BoundingRectangle boundingRectangle, Point2 point)
|
||||
{
|
||||
// Real-Time Collision Detection, Christer Ericson, 2005. Chapter 4.2; Bounding Volumes - Axis-aligned Bounding Boxes (AABBs). pg 78
|
||||
|
||||
var distance = boundingRectangle.Centre - point;
|
||||
var radius = boundingRectangle.Radius;
|
||||
|
||||
return (Math.Abs(distance.X) <= radius.Width) && (Math.Abs(distance.Y) <= radius.Height);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether this <see cref="BoundingRectangle" /> contains the specified <see cref="Point2" />.
|
||||
/// </summary>
|
||||
/// <param name="point">The point.</param>
|
||||
/// <returns>
|
||||
/// <c>true</c> if this <see cref="BoundingRectangle" /> contains the <paramref name="point" />; otherwise,
|
||||
/// <c>false</c>.
|
||||
/// </returns>
|
||||
public bool Contains(Point2 point)
|
||||
{
|
||||
return Contains(this, point);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Computes the closest <see cref="Point2" /> on this <see cref="BoundingRectangle" /> to a specified <see cref="Point2" />.
|
||||
/// </summary>
|
||||
/// <param name="point">The point.</param>
|
||||
/// <returns>The closest <see cref="Point2" /> on this <see cref="BoundingRectangle" /> to the <paramref name="point" />.</returns>
|
||||
public Point2 ClosestPointTo(Point2 point)
|
||||
{
|
||||
// Real-Time Collision Detection, Christer Ericson, 2005. Chapter 5.1.2; Basic Primitive Tests - Closest-point Computations. pg 130-131
|
||||
|
||||
var minimum = Centre - Radius;
|
||||
var maximum = Centre + Radius;
|
||||
var result = point;
|
||||
|
||||
// For each coordinate axis, if the point coordinate value is outside box, clamp it to the box, else keep it as is
|
||||
if (result.X < minimum.X)
|
||||
result.X = minimum.X;
|
||||
else if (result.X > maximum.X)
|
||||
result.X = maximum.X;
|
||||
|
||||
if (result.Y < minimum.Y)
|
||||
result.Y = minimum.Y;
|
||||
else if (result.Y > maximum.Y)
|
||||
result.Y = maximum.Y;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares two <see cref="BoundingRectangle" /> structures. The result specifies whether the values of the
|
||||
/// <see cref="Centre" /> and <see cref="Radius" /> fields of the two <see cref="BoundingRectangle" /> structures
|
||||
/// are equal.
|
||||
/// </summary>
|
||||
/// <param name="first">The first bounding rectangle.</param>
|
||||
/// <param name="second">The second bounding rectangle.</param>
|
||||
/// <returns>
|
||||
/// <c>true</c> if the <see cref="Centre" /> and <see cref="Radius" /> fields of the two
|
||||
/// <see cref="BoundingRectangle" /> structures are equal; otherwise, <c>false</c>.
|
||||
/// </returns>
|
||||
public static bool operator ==(BoundingRectangle first, BoundingRectangle second)
|
||||
{
|
||||
return first.Equals(ref second);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether this <see cref="BoundingRectangle" /> is equal to another
|
||||
/// <see cref="BoundingRectangle" />.
|
||||
/// </summary>
|
||||
/// <param name="boundingRectangle">The bounding rectangle.</param>
|
||||
/// <returns>
|
||||
/// <c>true</c> if this <see cref="BoundingRectangle" /> is equal to the <paramref name="boundingRectangle" />;
|
||||
/// otherwise, <c>false</c>.
|
||||
/// </returns>
|
||||
public bool Equals(BoundingRectangle boundingRectangle)
|
||||
{
|
||||
return Equals(ref boundingRectangle);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether this <see cref="BoundingRectangle" /> is equal to another <see cref="BoundingRectangle" />.
|
||||
/// </summary>
|
||||
/// <param name="boundingRectangle">The bounding rectangle.</param>
|
||||
/// <returns>
|
||||
/// <c>true</c> if this <see cref="BoundingRectangle" /> is equal to the <paramref name="boundingRectangle" />; otherwise,
|
||||
/// <c>false</c>.
|
||||
/// </returns>
|
||||
public bool Equals(ref BoundingRectangle boundingRectangle)
|
||||
{
|
||||
return (boundingRectangle.Centre == Centre) && (boundingRectangle.Radius == Radius);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a value indicating whether this <see cref="BoundingRectangle" /> is equal to a specified object.
|
||||
/// </summary>
|
||||
/// <param name="obj">The object to make the comparison with.</param>
|
||||
/// <returns>
|
||||
/// <c>true</c> if this <see cref="BoundingRectangle" /> is equal to <paramref name="obj" />; otherwise, <c>false</c>.
|
||||
/// </returns>
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (obj is BoundingRectangle)
|
||||
return Equals((BoundingRectangle)obj);
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares two <see cref="BoundingRectangle" /> structures. The result specifies whether the values of the
|
||||
/// <see cref="Centre" /> and <see cref="Radius" /> fields of the two <see cref="BoundingRectangle" /> structures
|
||||
/// are unequal.
|
||||
/// </summary>
|
||||
/// <param name="first">The first bounding box.</param>
|
||||
/// <param name="second">The second bounding box.</param>
|
||||
/// <returns>
|
||||
/// <c>true</c> if the <see cref="Centre" /> and <see cref="Radius" /> fields of the two
|
||||
/// <see cref="BoundingRectangle" /> structures are unequal; otherwise, <c>false</c>.
|
||||
/// </returns>
|
||||
public static bool operator !=(BoundingRectangle first, BoundingRectangle second)
|
||||
{
|
||||
return !(first == second);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a hash code of this <see cref="BoundingRectangle" /> suitable for use in hashing algorithms and data
|
||||
/// structures like a hash table.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A hash code of this <see cref="BoundingRectangle" />.
|
||||
/// </returns>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
unchecked
|
||||
{
|
||||
return (Centre.GetHashCode() * 397) ^ Radius.GetHashCode();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs an implicit conversion from a <see cref="Rectangle" /> to a <see cref="BoundingRectangle" />.
|
||||
/// </summary>
|
||||
/// <param name="rectangle">The rectangle.</param>
|
||||
/// <returns>
|
||||
/// The resulting <see cref="BoundingRectangle" />.
|
||||
/// </returns>
|
||||
public static implicit operator BoundingRectangle(Rectangle rectangle)
|
||||
{
|
||||
var radius = new Size2(rectangle.Width / 2f, rectangle.Height / 2f);
|
||||
var centre = new Point2(rectangle.X + radius.Width, rectangle.Y + radius.Height);
|
||||
return new BoundingRectangle(centre, radius);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs an implicit conversion from a <see cref="BoundingRectangle" /> to a <see cref="Rectangle" />.
|
||||
/// </summary>
|
||||
/// <param name="boundingRectangle">The bounding rectangle.</param>
|
||||
/// <returns>
|
||||
/// The resulting <see cref="Rectangle" />.
|
||||
/// </returns>
|
||||
public static implicit operator Rectangle(BoundingRectangle boundingRectangle)
|
||||
{
|
||||
var minimum = boundingRectangle.Centre - boundingRectangle.Radius;
|
||||
return new Rectangle((int)minimum.X, (int)minimum.Y, (int)boundingRectangle.Radius.Width * 2, (int)boundingRectangle.Radius.Height * 2);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a <see cref="string" /> that represents this <see cref="BoundingRectangle" />.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// A <see cref="string" /> that represents this <see cref="BoundingRectangle" />.
|
||||
/// </returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return $"Centre: {Centre}, Radius: {Radius}";
|
||||
}
|
||||
|
||||
internal string DebugDisplayString => ToString();
|
||||
}
|
||||
}
|
||||
@@ -35,29 +35,29 @@ namespace MonoGame.Extended.Primitives
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether this <see cref="Ray2D" /> intersects with a specified <see cref="BoundingBox2D" />.
|
||||
/// Determines whether this <see cref="Ray2D" /> intersects with a specified <see cref="BoundingRectangle" />.
|
||||
/// </summary>
|
||||
/// <param name="axisAlignedBoundingBox">The bounding box.</param>
|
||||
/// <param name="boundingRectangle">The bounding rectangle.</param>
|
||||
/// <param name="rayNearDistance">
|
||||
/// When this method returns, contains the distance along the ray to the first intersection
|
||||
/// point with the <paramref name="axisAlignedBoundingBox" />, if an intersection was found; otherwise, <see cref="float.NaN" />.
|
||||
/// point with the <paramref name="boundingRectangle" />, if an intersection was found; otherwise, <see cref="float.NaN" />.
|
||||
/// This parameter is passed uninitialized.
|
||||
/// </param>
|
||||
/// <param name="rayFarDistance">
|
||||
/// When this method returns, contains the distance along the ray to the second intersection
|
||||
/// point with the <paramref name="axisAlignedBoundingBox" />, if an intersection was found; otherwise, <see cref="float.NaN" />.
|
||||
/// point with the <paramref name="boundingRectangle" />, if an intersection was found; otherwise, <see cref="float.NaN" />.
|
||||
/// This parameter is passed uninitialized.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// <c>true</c> if this <see cref="Ray2D" /> intersects with <paramref name="axisAlignedBoundingBox" />; otherwise,
|
||||
/// <c>true</c> if this <see cref="Ray2D" /> intersects with <paramref name="boundingRectangle" />; otherwise,
|
||||
/// <c>false</c>.
|
||||
/// </returns>
|
||||
public bool Intersects(BoundingBox2D axisAlignedBoundingBox, out float rayNearDistance, out float rayFarDistance)
|
||||
public bool Intersects(BoundingRectangle boundingRectangle, out float rayNearDistance, out float rayFarDistance)
|
||||
{
|
||||
// Real-Time Collision Detection, Christer Ericson, 2005. Chapter 5.3; Basic Primitive Tests - Intersecting Lines, Rays, and (Directed Segments). pg 179-181
|
||||
|
||||
var boundingBoxMinimumPoint = axisAlignedBoundingBox.Centre - axisAlignedBoundingBox.Radius;
|
||||
var boundingBoxMaximumPoint = axisAlignedBoundingBox.Centre + axisAlignedBoundingBox.Radius;
|
||||
var minimum = boundingRectangle.Centre - boundingRectangle.Radius;
|
||||
var maximum = boundingRectangle.Centre + boundingRectangle.Radius;
|
||||
|
||||
// Set to the smallest possible value so the algorithm can find the first hit along the ray
|
||||
var minimumDistanceAlongRay = float.MinValue;
|
||||
@@ -67,7 +67,7 @@ namespace MonoGame.Extended.Primitives
|
||||
// For all relevant slabs which in this case is two.
|
||||
|
||||
// The first, horizontal, slab.
|
||||
if (!RayHelper.IntersectsSlab(Position.X, Direction.X, boundingBoxMinimumPoint.X, boundingBoxMaximumPoint.X,
|
||||
if (!RayHelper.IntersectsSlab(Position.X, Direction.X, minimum.X, maximum.X,
|
||||
ref minimumDistanceAlongRay,
|
||||
ref maximumDistanceAlongRay))
|
||||
{
|
||||
@@ -76,7 +76,7 @@ namespace MonoGame.Extended.Primitives
|
||||
}
|
||||
|
||||
// The second, vertical, slab.
|
||||
if (!RayHelper.IntersectsSlab(Position.Y, Direction.Y, boundingBoxMinimumPoint.Y, boundingBoxMaximumPoint.Y,
|
||||
if (!RayHelper.IntersectsSlab(Position.Y, Direction.Y, minimum.Y, maximum.Y,
|
||||
ref minimumDistanceAlongRay,
|
||||
ref maximumDistanceAlongRay))
|
||||
{
|
||||
|
||||
@@ -106,23 +106,23 @@ namespace MonoGame.Extended.Primitives
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether this <see cref="Segment2D" /> intersects with the specified <see cref="BoundingBox2D" />.
|
||||
/// Determines whether this <see cref="Segment2D" /> intersects with the specified <see cref="BoundingRectangle" />.
|
||||
/// </summary>
|
||||
/// <param name="axisAlignedBoundingBox">The bounding box.</param>
|
||||
/// <param name="boundingRectangle">The bounding box.</param>
|
||||
/// <param name="intersectionPoint">
|
||||
/// When this method returns, contains the <see cref="Point2" /> of intersection, if an
|
||||
/// intersection was found; otherwise, the <see cref="Point2.NaN" />. This parameter is passed uninitialized.
|
||||
/// </param>
|
||||
/// <returns>
|
||||
/// <c>true</c> if this <see cref="Segment2D" /> intersects with <paramref name="axisAlignedBoundingBox" />; otherwise,
|
||||
/// <c>true</c> if this <see cref="Segment2D" /> intersects with <paramref name="boundingRectangle" />; otherwise,
|
||||
/// <c>false</c>.
|
||||
/// </returns>
|
||||
public bool Intersects(BoundingBox2D axisAlignedBoundingBox, out Point2 intersectionPoint)
|
||||
public bool Intersects(BoundingRectangle boundingRectangle, out Point2 intersectionPoint)
|
||||
{
|
||||
// Real-Time Collision Detection, Christer Ericson, 2005. Chapter 5.3; Basic Primitive Tests - Intersecting Lines, Rays, and (Directed Segments). pg 179-181
|
||||
|
||||
var minimumPoint = axisAlignedBoundingBox.Centre - axisAlignedBoundingBox.Radius;
|
||||
var maximumPoint = axisAlignedBoundingBox.Centre + axisAlignedBoundingBox.Radius;
|
||||
var minimumPoint = boundingRectangle.Centre - boundingRectangle.Radius;
|
||||
var maximumPoint = boundingRectangle.Centre + boundingRectangle.Radius;
|
||||
var minimumDistance = float.MinValue;
|
||||
var maximumDistance = float.MaxValue;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user