| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798 |
- 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));
- }
- }
|