Основываясь на множестве других ответов, я придумал это в C #:
/// <param name="rotation">The number of rotations (if negative, the <see cref="Matrix{TValue}"/> is rotated counterclockwise;
/// otherwise, it's rotated clockwise). A single (positive) rotation is equivalent to 90° or -270°; a single (negative) rotation is
/// equivalent to -90° or 270°. Matrices may be rotated by 90°, 180°, or 270° only (or multiples thereof).</param>
/// <returns></returns>
public Matrix<TValue> Rotate(int rotation)
{
var result = default(Matrix<TValue>);
//This normalizes the requested rotation (for instance, if 10 is specified, the rotation is actually just +-2 or +-180°, but all
//correspond to the same rotation).
var d = rotation.ToDouble() / 4d;
d = d - (int)d;
var degree = (d - 1d) * 4d;
//This gets the type of rotation to make; there are a total of four unique rotations possible (0°, 90°, 180°, and 270°).
//Each correspond to 0, 1, 2, and 3, respectively (or 0, -1, -2, and -3, if in the other direction). Since
//1 is equivalent to -3 and so forth, we combine both cases into one.
switch (degree)
{
case -3:
case +1:
degree = 3;
break;
case -2:
case +2:
degree = 2;
break;
case -1:
case +3:
degree = 1;
break;
case -4:
case 0:
case +4:
degree = 0;
break;
}
switch (degree)
{
//The rotation is 0, +-180°
case 0:
case 2:
result = new TValue[Rows, Columns];
break;
//The rotation is +-90°
case 1:
case 3:
result = new TValue[Columns, Rows];
break;
}
for (uint i = 0; i < Columns; ++i)
{
for (uint j = 0; j < Rows; ++j)
{
switch (degree)
{
//If rotation is 0°
case 0:
result._values[j][i] = _values[j][i];
break;
//If rotation is -90°
case 1:
//Transpose, then reverse each column OR reverse each row, then transpose
result._values[i][j] = _values[j][Columns - i - 1];
break;
//If rotation is +-180°
case 2:
//Reverse each column, then reverse each row
result._values[(Rows - 1) - j][(Columns - 1) - i] = _values[j][i];
break;
//If rotation is +90°
case 3:
//Transpose, then reverse each row
result._values[i][j] = _values[Rows - j - 1][i];
break;
}
}
}
return result;
}
Где _values
соответствует частному двумерному массиву, определенному Matrix<TValue>
(в форме [][]
). result = new TValue[Columns, Rows]
возможно через неявную перегрузку оператора и преобразует двумерный массив в Matrix<TValue>
.
Два свойства Columns
и Rows
являются открытыми свойствами, которые получают количество столбцов и строк текущего экземпляра:
public uint Columns
=> (uint)_values[0].Length;
public uint Rows
=> (uint)_values.Length;
Предполагая, конечно, что вы предпочитаете работать с беззнаковыми индексами; -)
Все это позволяет вам указать, сколько раз его следует повернуть и нужно ли поворачивать влево (если меньше нуля) или вправо (если больше нуля). Вы можете улучшить это, чтобы проверять вращение в фактических градусах, но затем вы захотите вызвать исключение, если значение не кратно 90. С этим входом вы можете изменить метод соответствующим образом:
public Matrix<TValue> Rotate(int rotation)
{
var _rotation = (double)rotation / 90d;
if (_rotation - Math.Floor(_rotation) > 0)
{
throw new NotSupportedException("A matrix may only be rotated by multiples of 90.").
}
rotation = (int)_rotation;
...
}
Поскольку степень более точно выражена double
, чем int
, но матрица может вращаться только с кратным 90, гораздо более интуитивно понятно, чтобы аргумент соответствовал чему-то еще, что может быть точно представлено структура данных используется. int
идеально, потому что он может сказать вам, сколько раз повернуть его до определенной единицы (90), а также направление. double
вполне может также рассказать вам об этом, но также включает в себя значения, которые не поддерживаются этой операцией (что по своей сути нелогично).