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