Refractoring
This commit is contained in:
14
Game/Assets/Scripts/Primitives/IPositionable.cs
Normal file
14
Game/Assets/Scripts/Primitives/IPositionable.cs
Normal file
@ -0,0 +1,14 @@
|
||||
|
||||
namespace TransportGame.Primitives
|
||||
{
|
||||
/// <summary>
|
||||
/// Object can be positioned using a point
|
||||
/// </summary>
|
||||
public interface IPositionable
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the position of the object
|
||||
/// </summary>
|
||||
Vector2 Position { get; }
|
||||
}
|
||||
}
|
12
Game/Assets/Scripts/Primitives/IPositionable.cs.meta
Normal file
12
Game/Assets/Scripts/Primitives/IPositionable.cs.meta
Normal file
@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b1f8e20ab1416e64aa689620b275d16e
|
||||
timeCreated: 1434538689
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
98
Game/Assets/Scripts/Primitives/Interval.cs
Normal file
98
Game/Assets/Scripts/Primitives/Interval.cs
Normal file
@ -0,0 +1,98 @@
|
||||
using System;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace TransportGame.Primitives
|
||||
{
|
||||
/// <summary>
|
||||
/// Interval
|
||||
/// </summary>
|
||||
public class Interval
|
||||
{
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets the minimum value of the interval
|
||||
/// </summary>
|
||||
[XmlAttribute("min")]
|
||||
public float Min { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the maximum value of the interval
|
||||
/// </summary>
|
||||
[XmlAttribute("max")]
|
||||
public float Max { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Other properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the length of the interval
|
||||
/// </summary>
|
||||
[XmlIgnore]
|
||||
public float Length
|
||||
{
|
||||
get
|
||||
{
|
||||
return Max - Min;
|
||||
}
|
||||
set
|
||||
{
|
||||
Max = Min + value;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Initializes interval
|
||||
/// </summary>
|
||||
public Interval()
|
||||
{
|
||||
Min = 0;
|
||||
Max = 1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes interval
|
||||
/// </summary>
|
||||
/// <param name="min">Minimum value</param>
|
||||
/// <param name="max">Maximum value</param>
|
||||
public Interval(float min, float max)
|
||||
{
|
||||
Min = min;
|
||||
Max = max;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Operations
|
||||
|
||||
/// <summary>
|
||||
/// Tests if interval contains value
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
/// <returns></returns>
|
||||
public bool Contains(float value)
|
||||
{
|
||||
return (value >= Min && value <= Max);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Object overrides
|
||||
|
||||
/// <summary>
|
||||
/// Gets string representation
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return String.Format("[{0}, {1}]", Min, Max);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
12
Game/Assets/Scripts/Primitives/Interval.cs.meta
Normal file
12
Game/Assets/Scripts/Primitives/Interval.cs.meta
Normal file
@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e3c02b6aafb292640a692c9ea354adb1
|
||||
timeCreated: 1434538689
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
178
Game/Assets/Scripts/Primitives/LineSegment.cs
Normal file
178
Game/Assets/Scripts/Primitives/LineSegment.cs
Normal file
@ -0,0 +1,178 @@
|
||||
using System;
|
||||
|
||||
namespace TransportGame.Primitives
|
||||
{
|
||||
/// <summary>
|
||||
/// A line segment
|
||||
/// </summary>
|
||||
public struct LineSegment
|
||||
{
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// X coordinate of first point
|
||||
/// </summary>
|
||||
public float X0 { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Y coordinate of first point
|
||||
/// </summary>
|
||||
public float Y0 { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// X coordinate of second point
|
||||
/// </summary>
|
||||
public float X1 { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Y coordinate of second point
|
||||
/// </summary>
|
||||
public float Y1 { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the first point
|
||||
/// </summary>
|
||||
public Vector2 P0
|
||||
{
|
||||
get
|
||||
{
|
||||
return new Vector2(X0, Y0);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the second point
|
||||
/// </summary>
|
||||
public Vector2 P1
|
||||
{
|
||||
get
|
||||
{
|
||||
return new Vector2(X1, Y1);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the length of the segment
|
||||
/// </summary>
|
||||
public float Length
|
||||
{
|
||||
get
|
||||
{
|
||||
return (P1 - P0).Length;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the length squared of the segment
|
||||
/// </summary>
|
||||
public float LengthSq
|
||||
{
|
||||
get
|
||||
{
|
||||
return (P1 - P0).LengthSq;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the direction vector of the segment
|
||||
/// </summary>
|
||||
public Vector2 Direction
|
||||
{
|
||||
get
|
||||
{
|
||||
return (P1 - P0).Normalized;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Initializes line segment
|
||||
/// </summary>
|
||||
/// <param name="p0">First point</param>
|
||||
/// <param name="p1">Second point</param>
|
||||
public LineSegment(Vector2 p0, Vector2 p1)
|
||||
: this()
|
||||
{
|
||||
X0 = p0.X; Y0 = p0.Y;
|
||||
X1 = p1.X; Y1 = p1.Y;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes line segment
|
||||
/// </summary>
|
||||
/// <param name="x0">X coordinate of first segment</param>
|
||||
/// <param name="y0">Y coordinate of first segment</param>
|
||||
/// <param name="x1">X coordinate of second segment</param>
|
||||
/// <param name="y1">Y coordinate of second segment</param>
|
||||
public LineSegment(float x0, float y0, float x1, float y1)
|
||||
: this()
|
||||
{
|
||||
X0 = x0; Y0 = y0;
|
||||
X1 = x1; Y1 = y1;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Operations
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the intersection between two line segments.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Ignores case where intersection is a line.
|
||||
/// </remarks>
|
||||
/// <param name="a">First line segment</param>
|
||||
/// <param name="b">Second line segment</param>
|
||||
/// <returns>Intersection point, or null if segments don't intersect.</returns>
|
||||
public static Vector2? Intersect(LineSegment a, LineSegment b)
|
||||
{
|
||||
float s1x = a.X1 - a.X0, s1y = a.Y1 - a.Y0;
|
||||
float s2x = b.X1 - b.X0, s2y = b.Y1 - b.Y0;
|
||||
|
||||
float det = (-s2x * s1y + s1x * s2y);
|
||||
|
||||
// Avoid division by zero
|
||||
// Note: this is an edge case, the vectors might be parallel or colliniar
|
||||
if (det == 0) return null;
|
||||
|
||||
float s = (-s1y * (a.X0 - b.X0) + s1x * (a.Y0 - b.Y0)) / det;
|
||||
float t = (s2x * (a.Y0 - b.Y0) - s2y * (a.X0 - b.X0)) / det;
|
||||
|
||||
// Collision detected
|
||||
if (s >= 0 && s <= 1 && t >= 0 && t <= 1)
|
||||
return new Vector2(a.X0 + t * s1x, a.Y0 + t * s1y);
|
||||
|
||||
// No collision
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the distance from a point p to a line segment
|
||||
/// </summary>
|
||||
/// <param name="p">Point</param>
|
||||
/// <returns>Distance</returns>
|
||||
public float Distance(Vector2 p)
|
||||
{
|
||||
float det = (Y1 - Y0) * p.X - (X1 - X0) * p.Y + X1 * Y0 - Y1 * X0;
|
||||
return Math.Abs(det) / Length;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ToString
|
||||
|
||||
/// <summary>
|
||||
/// Gets string representation of line segment
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("({0}, {1})->({2}, {3})", X0, Y0, X1, Y1);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
12
Game/Assets/Scripts/Primitives/LineSegment.cs.meta
Normal file
12
Game/Assets/Scripts/Primitives/LineSegment.cs.meta
Normal file
@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a6b34c7e4ed62b04fbb44b61fbb7bc23
|
||||
timeCreated: 1434538689
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
275
Game/Assets/Scripts/Primitives/Polygon.cs
Normal file
275
Game/Assets/Scripts/Primitives/Polygon.cs
Normal file
@ -0,0 +1,275 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace TransportGame.Primitives
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a polygon
|
||||
/// </summary>
|
||||
public class Polygon : IPositionable
|
||||
{
|
||||
#region Private data types
|
||||
|
||||
private struct Edge
|
||||
{
|
||||
public int U, V;
|
||||
|
||||
public Edge(int U, int V)
|
||||
{
|
||||
this.U = U;
|
||||
this.V = V;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the points that define the polygon
|
||||
/// </summary>
|
||||
public Vector2[] Points { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Other properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets the gravitational center
|
||||
/// </summary>
|
||||
public Vector2 Position
|
||||
{
|
||||
get { return Points.Aggregate((x, y) => x + y) / Points.Length; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Initializes polygon
|
||||
/// </summary>
|
||||
public Polygon()
|
||||
{
|
||||
Points = new Vector2[0];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes polygon using given points
|
||||
/// </summary>
|
||||
/// <param name="points">Points</param>
|
||||
public Polygon(IEnumerable<Vector2> points)
|
||||
{
|
||||
Points = points.ToArray();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes polygon using given points
|
||||
/// </summary>
|
||||
/// <param name="points">Points</param>
|
||||
public Polygon(params Vector2[] points)
|
||||
{
|
||||
Points = points;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Operations
|
||||
|
||||
/// <summary>
|
||||
/// Returns the union between given polygons
|
||||
/// </summary>
|
||||
/// <param name="polys">Polygons</param>
|
||||
/// <returns>Polygon representing union</returns>
|
||||
public static Polygon Union(params Polygon[] polys)
|
||||
{
|
||||
return Union(polys);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the union between given polygons
|
||||
/// </summary>
|
||||
/// <param name="polys">Polygons</param>
|
||||
/// <returns>Polygon representing union</returns>
|
||||
public static Polygon Union(IEnumerable<Polygon> polys)
|
||||
{
|
||||
List<Vector2> vertices = new List<Vector2>();
|
||||
List<Edge> edges = new List<Edge>();
|
||||
List<Vector2> union = new List<Vector2>();
|
||||
|
||||
foreach (var poly in polys)
|
||||
{
|
||||
// Add all points
|
||||
for (int i = 0; i < poly.Points.Length; i++)
|
||||
{
|
||||
int j = (i + 1) % poly.Points.Length;
|
||||
|
||||
// Get/add first point
|
||||
int indexi = vertices.IndexOf(poly.Points[i]);
|
||||
if (indexi == -1)
|
||||
{
|
||||
vertices.Add(poly.Points[i]);
|
||||
indexi = vertices.Count - 1;
|
||||
}
|
||||
|
||||
// Get/add second point
|
||||
int indexj = vertices.IndexOf(poly.Points[j]);
|
||||
if (indexj == -1)
|
||||
{
|
||||
vertices.Add(poly.Points[j]);
|
||||
indexj = vertices.Count - 1;
|
||||
}
|
||||
|
||||
// Add edge
|
||||
edges.Add(new Edge(indexi, indexj));
|
||||
}
|
||||
}
|
||||
|
||||
// Intersect edges
|
||||
for (int i = 0; i < edges.Count; i++)
|
||||
for (int j = i + 1; j < edges.Count; j++)
|
||||
{
|
||||
LineSegment a = new LineSegment(vertices[edges[i].U], vertices[edges[i].V]);
|
||||
LineSegment b = new LineSegment(vertices[edges[j].U], vertices[edges[j].V]);
|
||||
|
||||
var inters = LineSegment.Intersect(a, b);
|
||||
if (inters.HasValue && inters.Value != a.P0 && inters.Value != a.P1 && inters.Value != b.P0 && inters.Value != b.P1)
|
||||
{
|
||||
vertices.Add(inters.Value);
|
||||
int index = vertices.Count - 1;
|
||||
|
||||
edges.Add(new Edge(index, edges[i].V));
|
||||
edges[i] = new Edge(edges[i].U, index);
|
||||
edges.Add(new Edge(index, edges[j].V));
|
||||
edges[j] = new Edge(edges[j].U, index);
|
||||
}
|
||||
}
|
||||
|
||||
// Compute union
|
||||
int start = 0;
|
||||
|
||||
// Find starting point
|
||||
for (int i = 0; i < vertices.Count; i++)
|
||||
if (vertices[i].X + vertices[i].Y < vertices[start].X + vertices[start].Y)
|
||||
start = i;
|
||||
|
||||
int v = start, vold = -1;
|
||||
Vector2 prev = vertices[v].Normalized;
|
||||
|
||||
do
|
||||
{
|
||||
union.Add(vertices[v]);
|
||||
|
||||
int newV = -1;
|
||||
Vector2 smallestDir = Vector2.Zero;
|
||||
|
||||
foreach (var edge in edges)
|
||||
{
|
||||
if ((edge.U == v || edge.V == v) && edge.V != vold && edge.U != vold)
|
||||
{
|
||||
int otherv = (edge.U == v) ? edge.V : edge.U;
|
||||
Vector2 dir = (vertices[otherv] - vertices[v]).Normalized;
|
||||
|
||||
// Find smallest angle
|
||||
if (newV == -1 || Vector2.TrigonometricComparer.Compare(dir, smallestDir) < 0)
|
||||
{
|
||||
newV = otherv;
|
||||
smallestDir = dir;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Advance
|
||||
prev = smallestDir;
|
||||
vold = v;
|
||||
v = newV;
|
||||
|
||||
} while (v != start);
|
||||
|
||||
return new Polygon(union);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests if two polygons intersect
|
||||
/// </summary>
|
||||
/// <param name="a"></param>
|
||||
/// <param name="b"></param>
|
||||
/// <returns></returns>
|
||||
public static bool Intersect(Polygon a, Polygon b)
|
||||
{
|
||||
foreach (var poly in new[] { a, b })
|
||||
{
|
||||
for (int i = 0; i < poly.Points.Length; i++)
|
||||
{
|
||||
int j = (i + 1) % poly.Points.Length;
|
||||
|
||||
var normal = new Vector2(poly.Points[j].Y - poly.Points[i].Y, poly.Points[i].X - poly.Points[j].X);
|
||||
|
||||
double? minA = null, maxA = null;
|
||||
foreach (var p in a.Points)
|
||||
{
|
||||
var projected = Vector2.Dot(normal, p);
|
||||
if (minA == null || projected < minA)
|
||||
minA = projected;
|
||||
if (maxA == null || projected > maxA)
|
||||
maxA = projected;
|
||||
}
|
||||
|
||||
double? minB = null, maxB = null;
|
||||
foreach (var p in b.Points)
|
||||
{
|
||||
var projected = Vector2.Dot(normal, p);
|
||||
if (minB == null || projected < minB)
|
||||
minB = projected;
|
||||
if (maxB == null || projected > maxB)
|
||||
maxB = projected;
|
||||
}
|
||||
|
||||
if (maxA <= minB || maxB <= minA)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests if polygon contains point
|
||||
/// </summary>
|
||||
/// <param name="point"></param>
|
||||
/// <returns></returns>
|
||||
public bool Contains(Vector2 point)
|
||||
{
|
||||
// Quick bounding box check
|
||||
Vector2 min = Points[0], max = Points[0];
|
||||
|
||||
for (int i = 0; i < Points.Length; i++)
|
||||
{
|
||||
min.X = Math.Min(min.X, Points[i].X);
|
||||
min.Y = Math.Min(min.Y, Points[i].Y);
|
||||
max.X = Math.Max(max.X, Points[i].X);
|
||||
max.Y = Math.Max(max.Y, Points[i].Y);
|
||||
}
|
||||
|
||||
if (point.X < min.X || point.Y < min.Y || point.X > max.X || point.Y > max.Y)
|
||||
return false;
|
||||
|
||||
// Ray casting method
|
||||
int j = Points.Length - 1;
|
||||
bool result = false;
|
||||
for (int i = 0; i < Points.Length; i++)
|
||||
{
|
||||
if ((Points[i].Y > point.Y) != (Points[j].Y > point.Y) &&
|
||||
(point.X < (Points[j].X - Points[i].X) * (point.Y - Points[i].Y) / (Points[j].Y - Points[i].Y) + Points[i].X))
|
||||
result = !result;
|
||||
|
||||
j = i;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
12
Game/Assets/Scripts/Primitives/Polygon.cs.meta
Normal file
12
Game/Assets/Scripts/Primitives/Polygon.cs.meta
Normal file
@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3e0bb5f9b3dd2d84a9996806e2048f9d
|
||||
timeCreated: 1434538689
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
156
Game/Assets/Scripts/Primitives/Rectangle.cs
Normal file
156
Game/Assets/Scripts/Primitives/Rectangle.cs
Normal file
@ -0,0 +1,156 @@
|
||||
using System;
|
||||
|
||||
namespace TransportGame.Primitives
|
||||
{
|
||||
/// <summary>
|
||||
/// Rectangle
|
||||
/// </summary>
|
||||
public struct Rectangle
|
||||
{
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the left boundary
|
||||
/// </summary>
|
||||
public float Left { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the top boundary
|
||||
/// </summary>
|
||||
public float Bottom { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the right boundary
|
||||
/// </summary>
|
||||
public float Right { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the bottom boundary
|
||||
/// </summary>
|
||||
public float Top { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
|
||||
/// <summary>
|
||||
/// Initializes this rectangle
|
||||
/// </summary>
|
||||
/// <param name="left">Left boundary</param>
|
||||
/// <param name="bottom">Bottom boundary</param>
|
||||
/// <param name="right">Right boundary</param>
|
||||
/// <param name="top">Top boundary</param>
|
||||
public Rectangle(float left, float bottom, float right, float top)
|
||||
: this()
|
||||
{
|
||||
Left = left;
|
||||
Bottom = bottom;
|
||||
Right = right;
|
||||
Top = top;
|
||||
|
||||
if (left > right)
|
||||
throw new ArgumentException("Left must be smaller than right.");
|
||||
|
||||
if (bottom > top)
|
||||
throw new ArgumentException("Bottom must be smaller than top.");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Other properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the width of the rectangle
|
||||
/// </summary>
|
||||
public float Width
|
||||
{
|
||||
get
|
||||
{
|
||||
return Right - Left;
|
||||
}
|
||||
set
|
||||
{
|
||||
Right = Left + value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the height of the rectangle
|
||||
/// </summary>
|
||||
public float Height
|
||||
{
|
||||
get
|
||||
{
|
||||
return Top - Bottom;
|
||||
}
|
||||
set
|
||||
{
|
||||
Top = Bottom + value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the area of the rectangle
|
||||
/// </summary>
|
||||
public float Area
|
||||
{
|
||||
get
|
||||
{
|
||||
return Width * Height;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Operations
|
||||
|
||||
/// <summary>
|
||||
/// Tests if rectangle contains given point
|
||||
/// </summary>
|
||||
/// <param name="x">x coordinate</param>
|
||||
/// <param name="y">y coordinate</param>
|
||||
/// <returns>True if point is inside</returns>
|
||||
public bool Contains(float x, float y)
|
||||
{
|
||||
return x >= Left && x <= Right && y >= Bottom && y <= Top;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests if rectangle contains given point
|
||||
/// </summary>
|
||||
/// <param name="p">Vector</param>
|
||||
/// <returns>True if point is inside</returns>
|
||||
public bool Contains(Vector2 p)
|
||||
{
|
||||
return Contains(p.X, p.Y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests if two rectangles intersect
|
||||
/// </summary>
|
||||
/// <param name="a"></param>
|
||||
/// <param name="b"></param>
|
||||
/// <returns>True if rectangles intersect</returns>
|
||||
public static bool Intersect (Rectangle a, Rectangle b)
|
||||
{
|
||||
return !(b.Left > a.Right ||
|
||||
b.Right < a.Left ||
|
||||
b.Bottom > a.Top ||
|
||||
b.Top < a.Bottom);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Object overrides
|
||||
|
||||
/// <summary>
|
||||
/// Gets string representation
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return string.Format("({0}, {1}, {2}, {3})", Left, Bottom, Right, Top);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
12
Game/Assets/Scripts/Primitives/Rectangle.cs.meta
Normal file
12
Game/Assets/Scripts/Primitives/Rectangle.cs.meta
Normal file
@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 089c6381ade2028499552d764dcdaa25
|
||||
timeCreated: 1434538689
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
393
Game/Assets/Scripts/Primitives/Vector2.cs
Normal file
393
Game/Assets/Scripts/Primitives/Vector2.cs
Normal file
@ -0,0 +1,393 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Xml.Serialization;
|
||||
|
||||
namespace TransportGame.Primitives
|
||||
{
|
||||
/// <summary>
|
||||
/// 2D vector
|
||||
/// </summary>
|
||||
public struct Vector2
|
||||
{
|
||||
#region Constant vectors
|
||||
|
||||
/// <summary>
|
||||
/// Zero vector
|
||||
/// </summary>
|
||||
public static readonly Vector2 Zero = new Vector2(0, 0);
|
||||
|
||||
/// <summary>
|
||||
/// Unit vector
|
||||
/// </summary>
|
||||
public static readonly Vector2 Unit = new Vector2(1, 0);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <summary>
|
||||
/// Gets the X component
|
||||
/// </summary>
|
||||
[XmlAttribute("x")]
|
||||
public float X { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Y component
|
||||
/// </summary>
|
||||
[XmlAttribute("y")]
|
||||
public float Y { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a vector2
|
||||
/// </summary>
|
||||
/// <param name="x">X component</param>
|
||||
/// <param name="y">Y component</param>
|
||||
public Vector2(float x, float y)
|
||||
: this()
|
||||
{
|
||||
X = x;
|
||||
Y = y;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the vector corresponding with specified angle (in radians)
|
||||
/// </summary>
|
||||
/// <param name="rads">Radians</param>
|
||||
/// <returns>Vector</returns>
|
||||
public static Vector2 FromRadians(float rads)
|
||||
{
|
||||
return new Vector2((float)Math.Cos(rads), (float)Math.Sin(rads));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the vector corresponding with specified angle (in degrees)
|
||||
/// </summary>
|
||||
/// <param name="degs">Degrees</param>
|
||||
/// <returns>Vector</returns>
|
||||
public static Vector2 FromDegrees(float degs)
|
||||
{
|
||||
float rads = (degs * (float)Math.PI / 180f);
|
||||
return FromRadians(rads);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties (length, normalized)
|
||||
|
||||
/// <summary>
|
||||
/// Gets the length of the vector
|
||||
/// </summary>
|
||||
public float Length
|
||||
{
|
||||
get
|
||||
{
|
||||
return (float)Math.Sqrt(LengthSq);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the length of the vector squared
|
||||
/// </summary>
|
||||
public float LengthSq
|
||||
{
|
||||
get
|
||||
{
|
||||
return X * X + Y * Y;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the normalized vector
|
||||
/// </summary>
|
||||
/// <returns>Normalized vector</returns>
|
||||
public Vector2 Normalized
|
||||
{
|
||||
get
|
||||
{
|
||||
float len = Length;
|
||||
return new Vector2(X / len, Y / len);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the normalized vector raised to second power
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is less computationally expensive (no need to calculate square root).
|
||||
/// </remarks>
|
||||
/// <returns>Normalized vector</returns>
|
||||
public Vector2 NormalizedSq
|
||||
{
|
||||
get
|
||||
{
|
||||
float len2 = LengthSq;
|
||||
return new Vector2(X * X / len2, Y * Y / len2);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Operations
|
||||
|
||||
/// <summary>
|
||||
/// Rotates vector by given number of radians
|
||||
/// </summary>
|
||||
/// <param name="radians"></param>
|
||||
/// <returns></returns>
|
||||
public Vector2 Rotate(float radians)
|
||||
{
|
||||
float sin = (float)Math.Sin(radians);
|
||||
float cos = (float)Math.Cos(radians);
|
||||
|
||||
return new Vector2(X * cos - Y * sin, X * sin + Y * cos);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rotates vector by given number of degrees
|
||||
/// </summary>
|
||||
/// <param name="degrees"></param>
|
||||
/// <returns></returns>
|
||||
public Vector2 RotateDeg(float degrees)
|
||||
{
|
||||
return Rotate(degrees * (float)Math.PI / 180f);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sum operator
|
||||
/// </summary>
|
||||
/// <param name="a">First vector</param>
|
||||
/// <param name="b">Second vector</param>
|
||||
/// <returns>Result of addition</returns>
|
||||
public static Vector2 operator +(Vector2 a, Vector2 b)
|
||||
{
|
||||
return new Vector2(a.X + b.X, a.Y + b.Y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subtract operator
|
||||
/// </summary>
|
||||
/// <param name="a">First vector</param>
|
||||
/// <param name="b">Second vector</param>
|
||||
/// <returns>Result of subtraction</returns>
|
||||
public static Vector2 operator -(Vector2 a, Vector2 b)
|
||||
{
|
||||
return new Vector2(a.X - b.X, a.Y - b.Y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Negation operator
|
||||
/// </summary>
|
||||
/// <param name="a">Vector</param>
|
||||
/// <returns>Negated vector</returns>
|
||||
public static Vector2 operator -(Vector2 a)
|
||||
{
|
||||
return new Vector2(-a.X, -a.Y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Multiply by constant
|
||||
/// </summary>
|
||||
/// <param name="a">Vector</param>
|
||||
/// <param name="c">Constant</param>
|
||||
/// <returns>Result</returns>
|
||||
public static Vector2 operator *(Vector2 a, float c)
|
||||
{
|
||||
return new Vector2(a.X * c, a.Y * c);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Multiply by constant
|
||||
/// </summary>
|
||||
/// <param name="c">Constant</param>
|
||||
/// <param name="a">Vector</param>
|
||||
/// <returns>Result</returns>
|
||||
public static Vector2 operator *(float c, Vector2 a)
|
||||
{
|
||||
return new Vector2(a.X * c, a.Y * c);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Divide by constant
|
||||
/// </summary>
|
||||
/// <param name="a">Vector</param>
|
||||
/// <param name="c">Constant</param>
|
||||
/// <returns>Result</returns>
|
||||
public static Vector2 operator /(Vector2 a, float c)
|
||||
{
|
||||
return new Vector2(a.X / c, a.Y / c);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Equality operator
|
||||
/// </summary>
|
||||
/// <param name="a">First vector</param>
|
||||
/// <param name="b">Second vector</param>
|
||||
/// <returns>True if vectors are equal</returns>
|
||||
public static bool operator ==(Vector2 a, Vector2 b)
|
||||
{
|
||||
return a.X == b.X && a.Y == b.Y;
|
||||
}
|
||||
/// <summary>
|
||||
/// Inequality operator
|
||||
/// </summary>
|
||||
/// <param name="a">First vector</param>
|
||||
/// <param name="b">Second vector</param>
|
||||
/// <returns>True if vectors are not equal</returns>
|
||||
public static bool operator !=(Vector2 a, Vector2 b)
|
||||
{
|
||||
return a.X != b.X || a.Y != b.Y;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates dot product of two vectors
|
||||
/// </summary>
|
||||
/// <param name="a">First vector</param>
|
||||
/// <param name="b">Second vector</param>
|
||||
/// <returns>Dot product</returns>
|
||||
public static float Dot(Vector2 a, Vector2 b)
|
||||
{
|
||||
return a.X * b.X + a.Y * b.Y;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the magnitude of the cross product between the two vectors (z considered 0)
|
||||
/// </summary>
|
||||
/// <param name="a">First vector</param>
|
||||
/// <param name="b">Second vector</param>
|
||||
/// <returns>Magnitude of cross product</returns>
|
||||
public static float Cross(Vector2 a, Vector2 b)
|
||||
{
|
||||
return (a.X * b.Y) - (a.Y * b.X);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests if two vectors are colliniar
|
||||
/// </summary>
|
||||
/// <param name="a">a</param>
|
||||
/// <param name="b">b</param>
|
||||
/// <returns>True if vectors are colliniar</returns>
|
||||
public static bool AreColliniar(Vector2 a, Vector2 b)
|
||||
{
|
||||
return Math.Abs(Cross(a, b)) < 1e-12;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Object overrides
|
||||
|
||||
/// <summary>
|
||||
/// Gets string representation of vector2
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return String.Format("({0}, {1})", X, Y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests if two vectors are equal.
|
||||
/// </summary>
|
||||
/// <param name="obj"></param>
|
||||
/// <returns></returns>
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (obj is Vector2)
|
||||
{
|
||||
Vector2 other = (Vector2)obj;
|
||||
return X == other.X && Y == other.Y;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets hash code of vector2
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return X.GetHashCode() * 29 + Y.GetHashCode();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Comparers
|
||||
|
||||
private class LengthComparerImpl : IComparer<Vector2>
|
||||
{
|
||||
public int Compare(Vector2 a, Vector2 b)
|
||||
{
|
||||
if (a.LengthSq > b.LengthSq)
|
||||
return 1;
|
||||
|
||||
if (a.LengthSq < b.LengthSq)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
private class TrigonometricComparerImpl : IComparer<Vector2>
|
||||
{
|
||||
private int Quad(Vector2 v)
|
||||
{
|
||||
if (v.Y >= 0)
|
||||
{
|
||||
if (v.X >= 0)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (v.X < 0)
|
||||
return 2;
|
||||
return 3;
|
||||
}
|
||||
}
|
||||
|
||||
public int Compare(Vector2 a, Vector2 b)
|
||||
{
|
||||
// If vectors are in different quadrants, we can use quadrant number
|
||||
int qa = Quad(a), qb = Quad(b);
|
||||
|
||||
if (qa != qb)
|
||||
{
|
||||
return qa - qb;
|
||||
}
|
||||
|
||||
// In same quadrant. Compute cross product which gives us sin(ab)*len(a)*len(b)
|
||||
// Vectors are in same quadrant, so angle should be less than 90deg
|
||||
float cross = Cross(a, b);
|
||||
if (cross < 0) // Angle > 180 degrees => a > b
|
||||
return 1;
|
||||
if (cross > 0) // Angle < 180 degrees => a < b
|
||||
return -1;
|
||||
|
||||
// Points are on the same line. Use distance
|
||||
if (a.LengthSq > b.LengthSq)
|
||||
return 1;
|
||||
else if (a.LengthSq < b.LengthSq)
|
||||
return -1;
|
||||
|
||||
// Points are equal
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Length comparer - compares vectors by length
|
||||
/// </summary>
|
||||
public static readonly IComparer<Vector2> LengthComparer = new LengthComparerImpl();
|
||||
|
||||
/// <summary>
|
||||
/// Trigonometric comparer - compares vectors by angle
|
||||
/// </summary>
|
||||
public static readonly IComparer<Vector2> TrigonometricComparer = new TrigonometricComparerImpl();
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
12
Game/Assets/Scripts/Primitives/Vector2.cs.meta
Normal file
12
Game/Assets/Scripts/Primitives/Vector2.cs.meta
Normal file
@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e100b9a2be8bd3243b55552195f6ce14
|
||||
timeCreated: 1434538689
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
Reference in New Issue
Block a user