diff --git a/Assets/Scripts/ServerShared/ItemData.cs b/Assets/Scripts/ServerShared/ItemData.cs index 9bb1521ba..a3085135a 100644 --- a/Assets/Scripts/ServerShared/ItemData.cs +++ b/Assets/Scripts/ServerShared/ItemData.cs @@ -217,6 +217,55 @@ public class Shape position = int2.zero; return false; } + + // Set every cell on the line from a to b to true according to Bresenham's Line Algorithm + public void SetLine(float2 a, float2 b) + { + if (a.Equals(b)) + { + return; + } + + // If line gradient is steep, swap x and y + bool steep = Math.Abs(b.y - a.y) > Math.Abs(b.x - a.x); + if (steep) + { + a = new float2(a.y, a.x); + b = new float2(b.y, b.x); + } + + // If b is closer to the origin than a, swap a and b. + if (a.x > b.x) + { + var temp = a; + a = b; + b = temp; + } + + float dx = b.x - a.x; + float dy = b.y - a.y; + float derr = Math.Abs(dy / dx); // dx != 0 + + int y = (int) Math.Round(a.y); + int xStart = (int) Math.Round(a.x); + float error = (a.y - y) + (xStart - a.x) * derr; + + for (int x = xStart; x <= (int) Math.Round(b.x); x++) + { + if (x >= 0 && x < Width && y >= 0 && y < Height) + { + Cells[steep ? y : x, steep ? x : y] = true; + } + error += derr; + if (error >= 0.5f) + { + y += Math.Sign(dy); + error -= 1f; + } + } + + } + } [MessagePackObject, JsonObject(MemberSerialization.OptIn), JsonConverter(typeof(JsonKnownTypesConverter))] diff --git a/Assets/Scripts/Tests/BresenhamTest.cs b/Assets/Scripts/Tests/BresenhamTest.cs new file mode 100644 index 000000000..a8d9f7216 --- /dev/null +++ b/Assets/Scripts/Tests/BresenhamTest.cs @@ -0,0 +1,80 @@ +using System; +using System.Collections.Generic; +using NUnit.Framework; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Unity.Mathematics; + + + +class BresenhamTest +{ + [Test] + public void LowPositiveGradientTest() + { + Shape shape = new Shape(7, 7); + shape.SetLine(new float2(1, 1f), new float2(6f, 3f)); + + var coords = new[] { (1, 1), (2, 1), (3, 2), (4, 2), (5, 3), (6, 3)}; + foreach(var coord in coords) + { + Assert.True(shape.Cells[coord.Item1, coord.Item2], $"{coord.Item1}, {coord.Item2}"); + } + } + + [Test] + public void LowNegativeGradientTest() + { + Shape shape = new Shape(5, 5); + shape.SetLine(new float2(-2f, 4f), new float2(3f, 2f)); + + var coords = new[] { (0, 3), (1, 3), (2, 2), (3, 2)}; + foreach (var coord in coords) + { + Assert.True(shape.Cells[coord.Item1, coord.Item2], $"{coord.Item1}, {coord.Item2}"); + } + } + + [Test] + public void HighPositiveGradientTest() + { + Shape shape = new Shape(4, 4); + shape.SetLine(new float2(1f, 3f), new float2(0f, 0f)); + + var coords = new[] { (0, 0), (0, 1), (1, 2), (1, 3) }; + foreach (var coord in coords) + { + Assert.True(shape.Cells[coord.Item1, coord.Item2], $"{coord.Item1}, {coord.Item2}"); + } + } + + [Test] + public void FloatingPointYTest() + { + Shape shape = new Shape(7, 7); + shape.SetLine(new float2(0.6f, 1.4f), new float2(6f, 2.6f)); + + var coords = new[] { (1, 1), (2, 2), (3, 2), (4, 2), (5, 2), (6, 3) }; + foreach (var coord in coords) + { + Assert.True(shape.Cells[coord.Item1, coord.Item2], $"{coord.Item1}, {coord.Item2}"); + } + } + + + [Test] + public void FloatingPointXTest() + { + Shape shape = new Shape(7, 7); + shape.SetLine(new float2(0.6f, 1f), new float2(5.6f, 3f)); + + var coords = new[] { (1, 1), (2, 2), (3, 2), (4, 2), (5, 3), (6, 3) }; + foreach (var coord in coords) + { + Assert.True(shape.Cells[coord.Item1, coord.Item2], $"{coord.Item1}, {coord.Item2}"); + } + } +} + + diff --git a/Assets/Scripts/Tests/BresenhamTest.cs.meta b/Assets/Scripts/Tests/BresenhamTest.cs.meta new file mode 100644 index 000000000..cf6e30a70 --- /dev/null +++ b/Assets/Scripts/Tests/BresenhamTest.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 108b8cc35b87da84c8671ee8f9386799 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: