Improve paper stamping experience (#17135)
This commit is contained in:
98
Content.Client/Paper/UI/StampCollection.xaml.cs
Normal file
98
Content.Client/Paper/UI/StampCollection.xaml.cs
Normal file
@@ -0,0 +1,98 @@
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user