using System; using System.Collections.Generic; using Chernobyl.Mathematics.Vectors; namespace Chernobyl.Mathematics.Geometry { /// /// Extension methods for working with 2 dimensional surfaces. /// public static class Plane { /// /// Converts the 2 dimensional output of to a 1 dimensional /// dataset of a fixed size determined by and . /// The 1D dataset starts at the bottom left of the dataset, outputting each row left-to-right /// before moving to the row above and so on and so forth. All values are normalized between /// 0 and 1. This means that bottom left is (0.0, 0.0) and top right is (1.0, 1.0). /// /// A method that outputs a value based on a normalized (0 to 1) /// position it is given. For X, 0 is the left most side and 1 is the right most. For Y, /// 0 is the bottom most and 1 the top most. /// The horizontal size of the 2D dataset. Must be at least 2. /// The vertical size of the 2D dataset. Must be at least 2. public static IEnumerable To1D(this Func factory, int width, int height) { // A 2D dataset requires at least 2 values in each dimension. width.Throw(nameof(width)).IfLessThan(2); height.Throw(nameof(height)).IfLessThan(2); // To make sure the 'factory' always gets a 0 for the smallest coordinate and 1 for the // largest coordinate, the normalized (X, Y) coordinates, we will use the one less than // the width/height in the calculations. var indexedWidth = width - 1; var indexedHeight = height - 1; for (double y = height - 1; y >= 0; y--) { for (var x = 0.0; x < width; x++) { yield return factory((x / indexedWidth, y / indexedHeight)); } } } /// /// Converts the to a factory that takes the normalized (X, Y) /// coordinates. /// public static Func To2D(this Func factory) => position => factory(); /// /// Methods for selecting a set of points on a 2 dimensional surface. /// public static class Predicate { /// /// Returns a predicate that returns true if the position it is given is within the specified /// square. This method assumes the 2D space has its origin at the bottom left. /// /// The bottom left corner of the rectangle, with the X being the /// horizontal position and Y being the vertical position. /// The horizontal size of the rectangle. Must be greater than zero. /// The vertical size of the rectangle. Must be greater than zero. public static Func Square(Vector2d bottomLeft, double width, double height) { width.Throw(nameof(width)).IfLessThanOrEqualTo(0); height.Throw(nameof(height)).IfLessThanOrEqualTo(0); var xEnd = bottomLeft.X + width; var yEnd = bottomLeft.Y + height; return position => { if (bottomLeft.X <= position.X && position.X <= xEnd) { if (bottomLeft.Y <= position.Y && position.Y <= yEnd) return true; } return false; }; } } } }