Skip to content

Commit 39906f1

Browse files
committedOct 27, 2022
Added Boudary struct.
1 parent bc68c80 commit 39906f1

File tree

2 files changed

+371
-1
lines changed

2 files changed

+371
-1
lines changed
 

‎src/DtronixCommon/Boundary.cs

+370
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,370 @@
1+
using System.Runtime.CompilerServices;
2+
3+
namespace DtronixCommon;
4+
5+
/// <summary>
6+
/// Boundary represented as [MinX, MinY] (Bottom Left) and [MaxX, MaxY] (Top Right) points.
7+
/// </summary>
8+
/// <remarks>
9+
/// MinY could be down and MaxY up as in cartesian coordinates, or vice vercia as is common
10+
/// in screen coordinates. Depends upon which system was selected for use.
11+
/// </remarks>
12+
public readonly struct Boundary
13+
{
14+
/// <summary>
15+
/// Minimum X coordinate position. (Left)
16+
/// </summary>
17+
public readonly float MinX;
18+
19+
/// <summary>
20+
/// Minimum Y coordinate position (Bottom/Top)
21+
/// </summary>
22+
public readonly float MinY;
23+
24+
/// <summary>
25+
/// Maximum X coordinate position (Right)
26+
/// </summary>
27+
public readonly float MaxX;
28+
29+
/// <summary>
30+
/// Maximum Y coordinate position (Top/Bottom)
31+
/// </summary>
32+
public readonly float MaxY;
33+
34+
/// <summary>
35+
/// Width of the boundary.
36+
/// </summary>
37+
public readonly float Width => MaxX - MinX;
38+
39+
/// <summary>
40+
/// Height of the boundary.
41+
/// </summary>
42+
public readonly float Height => MaxY - MinY;
43+
44+
/// <summary>
45+
/// Maximum sized boundary.
46+
/// </summary>
47+
public static Boundary Max { get; } = new(
48+
float.MinValue,
49+
float.MinValue,
50+
float.MaxValue,
51+
float.MaxValue);
52+
53+
/// <summary>
54+
/// Half the maximum size of the boundary.
55+
/// </summary>
56+
public static Boundary HalfMax { get; } = new(
57+
float.MinValue / 2,
58+
float.MinValue / 2,
59+
float.MaxValue / 2,
60+
float.MaxValue / 2);
61+
62+
/// <summary>
63+
/// Zero sized boundary.
64+
/// </summary>
65+
public static Boundary Zero { get; } = new(0, 0, 0, 0);
66+
67+
/// <summary>
68+
/// Empty boundary.
69+
/// </summary>
70+
public static Boundary Empty { get; } = new(
71+
float.PositiveInfinity,
72+
float.PositiveInfinity,
73+
float.NegativeInfinity,
74+
float.NegativeInfinity);
75+
76+
/// <summary>
77+
/// Returns true if the boundary has no volume.
78+
/// </summary>
79+
public bool IsEmpty => MinY >= MaxY || MinX >= MaxX;
80+
81+
/// <summary>
82+
/// Creates a boundary with the specified left, bottom, right, top distances from origin.
83+
/// </summary>
84+
/// <param name="minX">Left distance from origin.</param>
85+
/// <param name="minY">Bottom distance from origin.</param>
86+
/// <param name="maxX">Right distance from origin.</param>
87+
/// <param name="maxY">Top distance from origin.</param>
88+
public Boundary(float minX, float minY, float maxX, float maxY)
89+
{
90+
MinX = minX;
91+
MinY = minY;
92+
MaxX = maxX;
93+
MaxY = maxY;
94+
}
95+
96+
/// <summary>
97+
/// IntersectsWith - Returns true if the Boundary intersects with this Boundary
98+
/// Returns false otherwise.
99+
/// Note that if one edge is coincident, this is considered an intersection.
100+
/// </summary>
101+
/// <returns>
102+
/// Returns true if the Boundary intersects with this Boundary
103+
/// Returns false otherwise.
104+
/// </returns>
105+
/// <param name="boundary"> Rect </param>
106+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
107+
public bool IntersectsWith(in Boundary boundary)
108+
{
109+
return boundary.MinX <= MaxX &&
110+
boundary.MaxX >= MinX &&
111+
boundary.MinY <= MaxY &&
112+
boundary.MaxY >= MinY;
113+
}
114+
115+
/// <summary>
116+
/// IntersectsWith - Returns true if the Boundary intersects with this Boundary
117+
/// Returns false otherwise.
118+
/// Note that if one edge is coincident, this is considered an intersection.
119+
/// </summary>
120+
/// <param name="boundary1">First boundary.</param>
121+
/// <param name="boundary2">Second boundary.</param>
122+
/// <returns>
123+
/// Returns true if the Boundary intersects with this Boundary
124+
/// Returns false otherwise.
125+
/// </returns>
126+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
127+
public static bool Intersects(in Boundary boundary1, in Boundary boundary2)
128+
{
129+
return boundary1.MinX <= boundary2.MaxX &&
130+
boundary1.MaxX >= boundary2.MinX &&
131+
boundary1.MinY <= boundary2.MaxY &&
132+
boundary1.MaxY >= boundary2.MinY;
133+
}
134+
135+
/// <summary>
136+
/// Checks if a point is contained by the boundary.
137+
/// </summary>
138+
/// <param name="x">X coordinate.</param>
139+
/// <param name="y">Y coordinate.</param>
140+
/// <returns>True if the point is contained, false otherwise.</returns>
141+
public bool Contains(float x, float y)
142+
{
143+
return x >= MinX
144+
&& x <= MaxX
145+
&& y >= MinY
146+
&& y <= MaxY;
147+
}
148+
149+
150+
/// <summary>
151+
/// Takes the current boundary and offsets it by the specified X & Y vectors.
152+
/// </summary>
153+
/// <param name="x">X vector offset.</param>
154+
/// <param name="y">Y vector offset.</param>
155+
/// <returns>New offset boundary.</returns>
156+
public Boundary CreateOffset(in float x, in float y)
157+
{
158+
return new Boundary(
159+
(float)(MinX + x),
160+
(float)(MinY + y),
161+
(float)(MaxX + x),
162+
(float)(MaxY + y));
163+
}
164+
165+
/// <summary>
166+
/// Creates a union between two boundaries.
167+
/// </summary>
168+
/// <param name="boundary">second boundary to union with.</param>
169+
/// <returns>new union boundary.</returns>
170+
public Boundary Union(in Boundary boundary)
171+
{
172+
if (IsEmpty)
173+
return boundary;
174+
175+
if (boundary.IsEmpty)
176+
return this;
177+
178+
return new Boundary(
179+
Math.Min(MinX, boundary.MinX),
180+
Math.Min(MinY, boundary.MinY),
181+
Math.Max(MaxX, boundary.MaxX),
182+
Math.Max(MaxY, boundary.MaxY));
183+
}
184+
185+
/// <summary>
186+
/// Gets a rough approximation of the hypotenuse of the rectangle.
187+
/// Uses different algorithms based upon the passed integer.
188+
/// </summary>
189+
/// <param name="algorithm">0 - 3. Determines algorithm used. 0 is the fastest, 3 is the slowest but most accurate.</param>
190+
/// <returns>Approximate length</returns>
191+
/// <remarks>
192+
/// https://stackoverflow.com/a/26607206
193+
/// All these assume 0 ≤ a ≤ b.
194+
/// 0. h = b + 0.337 * a // less sorting order of the a & b variables;
195+
/// 1. h = b + 0.337 * a // max error ≈ 5.5 %
196+
/// 2. h = max(b, 0.918 * (b + (a >> 1))) // max error ≈ 2.6 %
197+
/// 3. h = b + 0.428 * a* a / b // max error ≈ 1.04 %
198+
/// </remarks>
199+
public float GetHypotenuseApproximate(int algorithm)
200+
{
201+
var a = MathF.Abs(MaxX - MinX);
202+
var b = MathF.Abs(MaxY - MinY);
203+
204+
if (algorithm == 0)
205+
return b + 0.337f * a;
206+
207+
// Transpose variables to ensure "b" is larger than A
208+
if (a > b)
209+
(a, b) = (b, a);
210+
211+
switch (algorithm)
212+
{
213+
case 1:
214+
return b + 0.337f * a;
215+
case 2:
216+
return MathF.Max(b, 0.918f * (b + (a / 2f)));
217+
case 3:
218+
return b + 0.428f * a * a / b;
219+
default:
220+
throw new ArgumentException("Must select algorithm 0 through 3", nameof(algorithm));
221+
}
222+
223+
}
224+
225+
/// <summary>
226+
/// Rotates the boundary from its center point by the specified number of degrees.
227+
/// </summary>
228+
/// <param name="degrees">Degrees to rotate.</param>
229+
/// <returns>New rotated boundary.</returns>
230+
public Boundary Rotate(float degrees)
231+
{
232+
// Center position of the rectangle.
233+
//private const m_PosX : Number, m_PosY : Number;
234+
235+
// Rectangle orientation, in radians.
236+
var m_Orientation = degrees * MathF.PI / 180.0f;
237+
// Half-width and half-height of the rectangle.
238+
var m_HalfSizeX = Width / 2;
239+
var m_HalfSizeY = Height / 2;
240+
var m_PosX = MinX + m_HalfSizeX;
241+
var m_PosY = MinY + m_HalfSizeY;
242+
243+
// corner_1 is right-top corner of unrotated rectangle, relative to m_Pos.
244+
// corner_2 is right-bottom corner of unrotated rectangle, relative to m_Pos.
245+
var corner_1_x = m_HalfSizeX;
246+
var corner_2_x = m_HalfSizeX;
247+
var corner_1_y = -m_HalfSizeY;
248+
var corner_2_y = m_HalfSizeY;
249+
250+
var sin_o = MathF.Sin(m_Orientation);
251+
var cos_o = MathF.Cos(m_Orientation);
252+
253+
// xformed_corner_1, xformed_corner_2 are points corner_1, corner_2 rotated by angle m_Orientation.
254+
var xformed_corner_1_x = corner_1_x * cos_o - corner_1_y * sin_o;
255+
var xformed_corner_1_y = corner_1_x * sin_o + corner_1_y * cos_o;
256+
var xformed_corner_2_x = corner_2_x * cos_o - corner_2_y * sin_o;
257+
var xformed_corner_2_y = corner_2_x * sin_o + corner_2_y * cos_o;
258+
259+
// ex, ey are extents (half-sizes) of the final AABB.
260+
var ex = MathF.Max(MathF.Abs(xformed_corner_1_x), MathF.Abs(xformed_corner_2_x));
261+
var ey = MathF.Max(MathF.Abs(xformed_corner_1_y), MathF.Abs(xformed_corner_2_y));
262+
return new Boundary(m_PosX - ex, m_PosY - ey, m_PosX + ex, m_PosY + ey);
263+
//var aabb_min_x = m_PosX - ex;
264+
//var aabb_max_x = m_PosX + ex;
265+
//var aabb_min_y = m_PosY - ey;
266+
//var aabb_max_y = m_PosY + ey;
267+
}
268+
269+
/// <summary>
270+
/// Unions two boundaries.
271+
/// </summary>
272+
/// <param name="boundary1">First boundary.</param>
273+
/// <param name="boundary2">Second boundary.</param>
274+
/// <returns>New union boundary.</returns>
275+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
276+
public static Boundary Union(in Boundary boundary1, in Boundary boundary2)
277+
{
278+
return new Boundary(
279+
boundary1.MinX < boundary2.MinX ? boundary1.MinX : boundary2.MinX,
280+
boundary1.MinY < boundary2.MinY ? boundary1.MinY : boundary2.MinY,
281+
boundary1.MaxX > boundary2.MaxX ? boundary1.MaxX : boundary2.MaxX,
282+
boundary1.MaxY > boundary2.MaxY ? boundary1.MaxY : boundary2.MaxY);
283+
}
284+
285+
/// <summary>
286+
/// Returns a String which represents the boundary instance.
287+
/// </summary>
288+
/// <returns>Value</returns>
289+
public override string ToString()
290+
{
291+
return $"MinX:{MinX:F}; MinY:{MinY:F}; MaxX:{MaxX:F}; MaxY:{MaxY:F}; Width: {MaxX - MinX:F}; Height: {MaxY - MinY:F}";
292+
}
293+
294+
295+
/// <summary>
296+
/// Checks to see if the the two boundaries are equal.
297+
/// </summary>
298+
/// <param name="boundary1">First boundary.</param>
299+
/// <param name="boundary2">Second boundary.</param>
300+
/// <returns>True if the two boundaries are equal.</returns>
301+
public static bool operator ==(in Boundary boundary1, in Boundary boundary2)
302+
{
303+
return boundary1.Equals(boundary2);
304+
}
305+
306+
/// <summary>
307+
/// Checks to see if the the two boundaries are equal.
308+
/// </summary>
309+
/// <param name="boundary1">First boundary.</param>
310+
/// <param name="boundary2">Second boundary.</param>
311+
/// <returns>True if the two boundaries are not equal.</returns>
312+
public static bool operator !=(in Boundary boundary1, in Boundary boundary2)
313+
{
314+
return boundary1.MinX != boundary2.MaxX ||
315+
boundary1.MaxX != boundary2.MinX ||
316+
boundary1.MinY != boundary2.MaxY ||
317+
boundary1.MaxY != boundary2.MinY;
318+
}
319+
320+
/// <summary>
321+
/// Checks to see if the other boundary is equal to this boundary.
322+
/// </summary>
323+
/// <param name="other">Other boundary.</param>
324+
/// <returns>True if the two boundaries are equal.</returns>
325+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
326+
public bool Equals(in Boundary other)
327+
{
328+
return MinX.Equals(other.MinX)
329+
&& MinY.Equals(other.MinY)
330+
&& MaxX.Equals(other.MaxX)
331+
&& MaxY.Equals(other.MaxY);
332+
}
333+
334+
335+
/// <summary>
336+
/// Checks to see if the other object is a boundary and if it is, if it is equal to this boundary.
337+
/// </summary>
338+
/// <param name="other">Other boundary.</param>
339+
/// <returns>True if the two boundaries are equal.</returns>
340+
public override bool Equals(object? obj)
341+
{
342+
return obj is Boundary other && Equals(other);
343+
}
344+
345+
/// <summary>
346+
/// Gets the hash code of this boundary.
347+
/// </summary>
348+
/// <returns>Hash.</returns>
349+
public override int GetHashCode()
350+
{
351+
return HashCode.Combine(MinX, MinY, MaxX, MaxY);
352+
}
353+
354+
/// <summary>
355+
/// Creates a new boundary from a circle at the specified location.
356+
/// </summary>
357+
/// <param name="x">X coordinate of the circle center.</param>
358+
/// <param name="y">Y coordinate of the circle center.</param>
359+
/// <param name="radius">Radius of the circle.</param>
360+
/// <returns>New boundary.</returns>
361+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
362+
public static Boundary FromCircle(float x, float y, float radius)
363+
{
364+
return new Boundary(
365+
x - radius,
366+
y - radius,
367+
x + radius,
368+
y + radius);
369+
}
370+
}

‎src/DtronixCommon/DtronixCommon.csproj

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
<PropertyGroup>
33
<TargetFrameworks>net5.0;net6.0</TargetFrameworks>
44
<ImplicitUsings>enable</ImplicitUsings>
5-
<Version>0.6.3.0</Version>
5+
<Version>0.6.4.0</Version>
66
<Nullable>enable</Nullable>
77
<LangVersion>10</LangVersion>
88
<Company>Dtronix</Company>

0 commit comments

Comments
 (0)
Please sign in to comment.