99 lines
3.6 KiB
C#
99 lines
3.6 KiB
C#
using System.Numerics;
|
|
using Robust.Client.AutoGenerated;
|
|
using Robust.Client.UserInterface.Controls;
|
|
using Robust.Client.UserInterface.XAML;
|
|
using Robust.Shared.Random;
|
|
|
|
namespace Content.Client.Paper.UI;
|
|
|
|
[GenerateTypedNameReferences]
|
|
public sealed partial class StampCollection : Container
|
|
{
|
|
private List<StampWidget> _stamps = new();
|
|
|
|
/// Seed for random number generator to place stamps deterministically
|
|
public int PlacementSeed;
|
|
|
|
public StampCollection()
|
|
{
|
|
RobustXamlLoader.Load(this);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Remove any stamps from the page
|
|
/// </summary>
|
|
public void RemoveStamps()
|
|
{
|
|
_stamps.Clear();
|
|
InvalidateArrange();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds a stamp to the display; will perform
|
|
/// automatic layout.
|
|
/// </summary>
|
|
public void AddStamp(StampWidget s)
|
|
{
|
|
_stamps.Add(s);
|
|
AddChild(s);
|
|
}
|
|
|
|
protected override Vector2 ArrangeOverride(Vector2 finalSize)
|
|
{
|
|
var random = new Random(PlacementSeed);
|
|
var r = (finalSize * 0.5f).Length();
|
|
var dtheta = -MathHelper.DegreesToRadians(90);
|
|
var theta0 = random.Next(0, 3) * dtheta;
|
|
var thisCenter = PixelSizeBox.TopLeft + finalSize * UIScale * 0.5f;
|
|
|
|
// Here's where we lay out the stamps. The first stamp goes in the
|
|
// center of this container; subsequent stamps will chose an angle
|
|
// (theta) to place the center of the stamp. The stamp is moved out
|
|
// as far as it can in that direction, taking the size and
|
|
// orientation of the stamp into account.
|
|
for (var i = 0; i < _stamps.Count; i++)
|
|
{
|
|
var stampOrientation = MathHelper.DegreesToRadians((random.NextFloat() - 0.5f) * 10.0f) ;
|
|
_stamps[i].Orientation = stampOrientation;
|
|
|
|
var theta = theta0 + dtheta * 0.5f + dtheta * i + (i > 4 ? MathF.Log(1 + i / 4) * dtheta : 0); // There is probably a better way to lay these out, to minimize overlaps
|
|
var childCenterOnCircle = thisCenter;
|
|
if (i > 0)
|
|
{
|
|
// First stamp can go in the center. Subsequent stamps have to find space.
|
|
childCenterOnCircle += new Vector2(MathF.Cos(theta), MathF.Sin(theta)) * r * UIScale;
|
|
}
|
|
|
|
var childHeLocal = _stamps[i].DesiredPixelSize * 0.5f;
|
|
var c = childHeLocal * MathF.Abs(MathF.Cos(stampOrientation));
|
|
var s = childHeLocal * MathF.Abs(MathF.Sin(stampOrientation));
|
|
var childHePage = new Vector2(c.X + s.Y, s.X + c.Y);
|
|
var controlBox = new UIBox2(PixelSizeBox.TopLeft, PixelSizeBox.TopLeft + finalSize * UIScale);
|
|
var clampedCenter = Clamp(Shrink(controlBox, childHePage), childCenterOnCircle);
|
|
var finalPosition = clampedCenter - childHePage;
|
|
var finalPositionAsInt = new Vector2i((int)finalPosition.X, (int)finalPosition.Y);
|
|
_stamps[i].ArrangePixel(new UIBox2i(finalPositionAsInt, finalPositionAsInt + _stamps[i].DesiredPixelSize));
|
|
}
|
|
|
|
return finalSize;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Shrink a UIBox2 by a half extents, moving both the top-left and
|
|
/// bottom-right closer together.
|
|
/// </summary>
|
|
private UIBox2 Shrink(UIBox2 box, Vector2 shrinkHe)
|
|
{
|
|
return new UIBox2(box.TopLeft + shrinkHe, box.BottomRight - shrinkHe);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the input vector clamped to be within the UIBox
|
|
/// </summary>
|
|
private Vector2 Clamp(UIBox2 box, Vector2 point)
|
|
{
|
|
return Vector2.Min(box.BottomRight, Vector2.Max(box.TopLeft, point));
|
|
}
|
|
}
|
|
|