namespace RioJoy.Core.Overlay; /// Which part of a region's box a drag grabs. public enum OverlayBoxHandle { None, Move, North, South, East, West, NorthWest, NorthEast, SouthWest, SouthEast, } /// /// The pure math behind the wallpaper maker's box editor: which handle a /// template-space point grabs on a region, and the region's new rectangle after /// dragging that handle. Kept UI-free so it is unit-testable; the canvas only /// converts mouse pixels to template space and applies the result. /// public static class OverlayRegionEdit { /// /// The handle at a template-space point: corners win over edges, edges over /// (anywhere else inside the box), all /// within template pixels of the border. /// public static OverlayBoxHandle HitHandle(OverlayRegion region, double x, double y, double tolerance) { if (region is null) throw new ArgumentNullException(nameof(region)); double l = region.X, t = region.Y, r = region.X + region.Width, b = region.Y + region.Height; if (x < l - tolerance || x > r + tolerance || y < t - tolerance || y > b + tolerance) return OverlayBoxHandle.None; bool west = Math.Abs(x - l) <= tolerance; bool east = Math.Abs(x - r) <= tolerance; bool north = Math.Abs(y - t) <= tolerance; bool south = Math.Abs(y - b) <= tolerance; if (north && west) return OverlayBoxHandle.NorthWest; if (north && east) return OverlayBoxHandle.NorthEast; if (south && west) return OverlayBoxHandle.SouthWest; if (south && east) return OverlayBoxHandle.SouthEast; if (north) return OverlayBoxHandle.North; if (south) return OverlayBoxHandle.South; if (west) return OverlayBoxHandle.West; if (east) return OverlayBoxHandle.East; return x >= l && x < r && y >= t && y < b ? OverlayBoxHandle.Move : OverlayBoxHandle.None; } /// /// The rectangle after dragging by (dx, dy) template /// pixels from the drag-start rectangle. Resizes clamp so width/height never /// drop below (the opposite edge stays put). /// public static (double X, double Y, double Width, double Height) Apply( double x, double y, double width, double height, OverlayBoxHandle handle, double dx, double dy, double minSize = 4) { double l = x, t = y, r = x + width, b = y + height; switch (handle) { case OverlayBoxHandle.Move: return (x + dx, y + dy, width, height); case OverlayBoxHandle.West: case OverlayBoxHandle.NorthWest: case OverlayBoxHandle.SouthWest: l = Math.Min(l + dx, r - minSize); break; } switch (handle) { case OverlayBoxHandle.East: case OverlayBoxHandle.NorthEast: case OverlayBoxHandle.SouthEast: r = Math.Max(r + dx, l + minSize); break; } switch (handle) { case OverlayBoxHandle.North: case OverlayBoxHandle.NorthWest: case OverlayBoxHandle.NorthEast: t = Math.Min(t + dy, b - minSize); break; case OverlayBoxHandle.South: case OverlayBoxHandle.SouthWest: case OverlayBoxHandle.SouthEast: b = Math.Max(b + dy, t + minSize); break; } return (l, t, r - l, b - t); } }