diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..85d61f4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +.vs +**/bin/** +**/obj/** +*.user \ No newline at end of file diff --git a/Demo/Demo.csproj b/Demo/Demo.csproj new file mode 100644 index 0000000..9c03f60 --- /dev/null +++ b/Demo/Demo.csproj @@ -0,0 +1,13 @@ + + + + WinExe + net5.0-windows + true + + + + + + + \ No newline at end of file diff --git a/Demo/Form1.Designer.cs b/Demo/Form1.Designer.cs new file mode 100644 index 0000000..9cfbb3d --- /dev/null +++ b/Demo/Form1.Designer.cs @@ -0,0 +1,64 @@ + +namespace Demo +{ + partial class Form1 + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.pictureBox1 = new System.Windows.Forms.PictureBox(); + ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit(); + this.SuspendLayout(); + // + // pictureBox1 + // + this.pictureBox1.Dock = System.Windows.Forms.DockStyle.Fill; + this.pictureBox1.Location = new System.Drawing.Point(0, 0); + this.pictureBox1.Name = "pictureBox1"; + this.pictureBox1.Size = new System.Drawing.Size(800, 450); + this.pictureBox1.TabIndex = 0; + this.pictureBox1.TabStop = false; + this.pictureBox1.Click += new System.EventHandler(this.pictureBox1_Click); + // + // Form1 + // + this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(800, 450); + this.Controls.Add(this.pictureBox1); + this.Name = "Form1"; + this.Text = "Form1"; + ((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit(); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.PictureBox pictureBox1; + } +} + diff --git a/Demo/Form1.cs b/Demo/Form1.cs new file mode 100644 index 0000000..f343f4d --- /dev/null +++ b/Demo/Form1.cs @@ -0,0 +1,90 @@ +using YMath; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Drawing.Drawing2D; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace Demo +{ + public partial class Form1 : Form + { + public Form1() + { + InitializeComponent(); + } + + private void pictureBox1_Click(object sender, EventArgs e) + { + /*var g = pictureBox1.CreateGraphics(); + g.SmoothingMode = SmoothingMode.HighQuality; + var pen = new Pen(Brushes.Black, 2); + var rect = CubePoints(100.0, 2); + DrawLines(g, pen, rect);*/ + }/* + + // [-1; 1] -> [0; width] + private static Vector[] RelativeToAbsolute(Vector[] points, int width, int height) + { + return points.Select(p => new Vector( + (p[0] + 1) / 2 * width, + (p[1] + 1) / 2 * height)) + .ToArray(); + } + + private static void DrawLines(Graphics g, Pen pen, Line[] lines) + { + foreach (var line in lines) + g.DrawLine(pen, + new PointF((float)line.Start[0], (float)line.End[1]), + new PointF((float)line.Start[0], (float)line.End[1])); + } + + public struct Line + { + public Line(Vector start, Vector end) + { + Start = start; + End = end; + } + + public Vector Start { get; set; } + public Vector End { get; set; } + } + + public static Line[] Cube(int nDimensions) + { + if (nDimensions == 0) + return new Vector[] { new Vector(0) }; + + var result = new List(); + var offset = new Vector(nDimensions); + offset[nDimensions - 1] = 1; + var face /*боже*//* = Cube(nDimensions - 1); + result.AddRange(face.Select(p => p + offset)); + result.AddRange(face.Select(p => p - offset)); + + } + + public static Vector[] CubePoints(double size, int nDimensions) + { + int n = 1 << nDimensions; + if (nDimensions >= 32) + throw new ArgumentException("Fuck yourself"); + var result = new Vector[n]; + for (int i = 0; i < n; i++) + { + var vec = new Vector(nDimensions); + for (int j = 0; j < nDimensions; j++) + vec[j] = ((i >> j) & 1) - 0.5; + result[i] = vec; + } + return result; + }*/ + } +} diff --git a/Demo/Form1.resx b/Demo/Form1.resx new file mode 100644 index 0000000..f298a7b --- /dev/null +++ b/Demo/Form1.resx @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/Demo/Program.cs b/Demo/Program.cs new file mode 100644 index 0000000..4007da9 --- /dev/null +++ b/Demo/Program.cs @@ -0,0 +1,35 @@ +using YMath; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace Demo +{ + static class Program + { + /// + /// The main entry point for the application. + /// + [STAThread] + static void Main() + { + Application.SetHighDpiMode(HighDpiMode.SystemAware); + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault(false); + + + Projector p = new Projector( + new Camera(1), + new CoordSystem(new Vector(), Matrix.Ident(4))); + var point = p.Project(new Vector(1, 1, 1, 2)); + + + + + + Application.Run(new Form1()); + } + } +} diff --git a/README.md b/README.md index b63408d..9a61398 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,7 @@ -# YMath-Demo +# YMath -Some basic math (matrices, vectors) \ No newline at end of file +Some basic math + +`YMath` - the Y-Math library + +`Demo` - the demo project, YMath usage example \ No newline at end of file diff --git a/YMath.sln b/YMath.sln new file mode 100644 index 0000000..a505dae --- /dev/null +++ b/YMath.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30711.63 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "YMath", "YMath\YMath.csproj", "{33638CEC-2BD2-4B91-A1CB-0383F7E429BE}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Demo", "Demo\Demo.csproj", "{CE175AF3-5242-4178-BB97-54956FE958D8}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {33638CEC-2BD2-4B91-A1CB-0383F7E429BE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {33638CEC-2BD2-4B91-A1CB-0383F7E429BE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {33638CEC-2BD2-4B91-A1CB-0383F7E429BE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {33638CEC-2BD2-4B91-A1CB-0383F7E429BE}.Release|Any CPU.Build.0 = Release|Any CPU + {CE175AF3-5242-4178-BB97-54956FE958D8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CE175AF3-5242-4178-BB97-54956FE958D8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CE175AF3-5242-4178-BB97-54956FE958D8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CE175AF3-5242-4178-BB97-54956FE958D8}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {C03EF0F0-151C-44B0-A13B-96F2820629C7} + EndGlobalSection +EndGlobal diff --git a/YMath/Camera.cs b/YMath/Camera.cs new file mode 100644 index 0000000..0d17255 --- /dev/null +++ b/YMath/Camera.cs @@ -0,0 +1,47 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace YMath +{ + + /// + /// Представляет камеру, выполняющую преобразование между трёхмерны пространством объектом и двумерной плоскостью изображения + /// + public class Camera + { + public Camera(double t) + { + T = t; + } + /// + /// Коэффициент функции преобразования камеры + /// + public double T { get; set; } + + public Vector WorldToImage(Vector worldPoint) + { + int imageOrder = worldPoint.Dimensions - 1; + var result = new Vector(imageOrder); + double last = worldPoint[worldPoint.Dimensions - 1]; + + for (int i = 0; i < imageOrder; i++) + result[i] = worldPoint[i] * T / last; + + return result; + } + + public Vector ImageToWorld(Vector imagePoint) + { + int worldOrder = imagePoint.Dimensions + 1; + var result = new Vector(worldOrder); + + for (int i = 0; i < worldOrder-1; i++) + result[i] = imagePoint[i] * 1 / T; + result[worldOrder - 1] = 1; + + return result; + } + } +} diff --git a/YMath/CoordSystem.cs b/YMath/CoordSystem.cs new file mode 100644 index 0000000..59dd2c3 --- /dev/null +++ b/YMath/CoordSystem.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace YMath +{ + /// + /// Представляет систему координат, имеющую заданные линейные и угловые смещения + /// + public class CoordSystem + { + public CoordSystem(Vector shift, Matrix rotationMatrix) + { + Shift = shift; + RotationMatrix = rotationMatrix; + } + + /// + /// Вектор линейных смещений + /// + public Vector Shift { get; set; } + + /// + /// Матрица угловых смещений + /// + public Matrix RotationMatrix { get; set; } + + public Vector LocalToWorld(Vector localPoint) + { + return RotationMatrix * localPoint + Shift; + } + + public Vector WorldToLocal(Vector worldPoint) + { + return RotationMatrix.GetTranspose() * (worldPoint - Shift); + } + } +} diff --git a/YMath/Matrix.cs b/YMath/Matrix.cs new file mode 100644 index 0000000..03ba9f5 --- /dev/null +++ b/YMath/Matrix.cs @@ -0,0 +1,141 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace YMath +{ + /// + /// Представляет матрицу с определённым количеством строк и столбцов + /// + public class Matrix + { + /// + /// Создание матрицы с заданным числом строк и столбцов + /// + /// + /// + public Matrix(int rows, int coluumns) + { + Data = new double[coluumns , rows]; + } + + public Matrix(int rows, int coluumns, double[] values) + : this(rows, coluumns) + { + if (values.Length != Rows * Columns) + throw new InvalidCastException("values array length is invalid"); + for (int i = 0; i < values.Length; i++) + this[i / Columns, i % Columns] = values[i]; + } + + + protected double[,] Data { get; set; } + public int Rows { get { return Data.GetLength(1); } } + public int Columns { get { return Data.GetLength(0); } } + + public double this[int row, int column] + { + get { return Data[column, row]; } + set { Data[column, row] = value; } + } + + public override string ToString() + { + StringBuilder result = new StringBuilder(); + for (int i = 0; i < Rows; i++) + { + for (int j = 0; j < Columns; j++) + { + string s = string.Format("{0:0.00}", this[i, j]); + s = s.PadRight(7); + result.Append(s); + } + result.AppendLine(); + } + return result.ToString(); + } + + public void Add(Matrix other) + { + int rows = Math.Min(Rows, other.Rows); + int cols = Math.Min(Columns, other.Columns); + for(int i = 0; i < rows; i++) + for (int j = 0; j < cols; j++) + this[i, j] += other[i, j]; + } + + /// + /// Returns the negated version of the matrix + /// + public static Matrix operator -(Matrix matrix) + { + Matrix result = new Matrix(matrix.Rows, matrix.Columns); + for (int i = 0; i < matrix.Rows; i++) + for (int j = 0; j < matrix.Columns; j++) + result[i, j] = -matrix[i, j]; + return result; + } + + + public static Matrix operator +(Matrix left, Matrix right) + { + Matrix result = new Matrix( + Math.Max(left.Rows, right.Rows), + Math.Max(left.Columns, right.Columns)); + result.Add(left); + result.Add(right); + return result; + } + + public static Matrix operator -(Matrix left, Matrix right) + { + Matrix result = new Matrix( + Math.Max(left.Rows, right.Rows), + Math.Max(left.Columns, right.Columns)); + result.Add(left); + result.Add(-right); + return result; + } + + public static Matrix operator *(Matrix left, Matrix right) + { + if(left.Columns != right.Rows) + throw new ArgumentException("Left.Columns != Right.Rows"); + Matrix result = new Matrix(left.Rows, right.Columns); + for (int i = 0; i < result.Rows; i++) + for (int j = 0; j < result.Columns; j++) + for (int r = 0; r < left.Columns; r++) + result[i, j] += left[i, r] * right[r, j]; + return result; + } + + public Matrix GetTranspose() + { + Matrix result = new Matrix(Columns, Rows); + for (int i = 0; i < Columns; i++) + for (int j = 0; j < Rows; j++) + result[j, i] = this[i, j]; + return result; + } + + public bool IsInvalid() + { + foreach (double f in Data) + if (double.IsNaN(f) || double.IsInfinity(f)) + return true; + return false; + } + + /// + /// Returns n-dimensional identity matrix + /// + public static Matrix Ident(int nDimensions) + { + var result = new Matrix(nDimensions, nDimensions); + for (int i = 0; i < nDimensions; i++) + result[i, i] = 1; + return result; + } + } +} diff --git a/YMath/Projector.cs b/YMath/Projector.cs new file mode 100644 index 0000000..c27ec31 --- /dev/null +++ b/YMath/Projector.cs @@ -0,0 +1,39 @@ +using YMath; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace YMath +{ + public class Projector + { + public Projector(Camera camera, CoordSystem coordSystem) + { + Camera = camera; + CoordSystem = coordSystem; + } + + /// + /// The coordinate system containing the points + /// + public CoordSystem CoordSystem { get; set; } + + /// + /// A camera building the image + /// + public Camera Camera { get; set; } + + /// + /// Projects the given point + /// + /// The N-1 dimensional point + public Vector Project(Vector point) + { + point = CoordSystem.LocalToWorld(point); + point = Camera.WorldToImage(point); + return point; + } + } +} diff --git a/YMath/Vector.cs b/YMath/Vector.cs new file mode 100644 index 0000000..47f27f3 --- /dev/null +++ b/YMath/Vector.cs @@ -0,0 +1,171 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace YMath +{ + /// + /// Представляет вектор-столбец произвольной размерности + /// + public class Vector : Matrix + { + /// + /// Создаёт вектор-столбец размерности n + /// + /// + public Vector(int n) + : base(n, 1) + { + } + + /// + /// Создаёт вектор-столбец размерности n и с указанными значениями + /// + public Vector(int n, double[] values) + : base(n, 1, values) + { + } + + /// + /// Создаёт вектор-столбец с указанными значениями + /// + public Vector(params double[] values) + : base(values.Length, 1, values) + { + } + + public double this[int row] + { + get { return this[row, 0]; } + set { this[row, 0] = value; } + } + + /// + /// Возвращает размерность вектора + /// + public int Dimensions { get { return Rows; } } + + public override string ToString() + { + List vals = new List(); + for (int i = 0; i < Dimensions; i++) + vals.Add(string.Format("{0:0.00}", this[i])); + return "(" + string.Join("; ", vals.ToArray()) + ")"; + } + + /// + /// Представление матрицы как вектора + /// + public static Vector FromMatrix(Matrix m) + { + if (m.Columns != 1) + throw new InvalidCastException("Not a vector"); + Vector vec = new Vector(m.Rows); + vec.Add(m); + return vec; + } + + public static Vector operator +(Vector left, Matrix right) + { + return FromMatrix((Matrix)left + (Matrix)right); + } + public static Vector operator +(Matrix left, Vector right) + { + return FromMatrix((Matrix)left + (Matrix)right); + } + public static Vector operator +(Vector left, Vector right) + { + return FromMatrix((Matrix)left + (Matrix)right); + } + public static Vector operator -(Vector left, Vector right) + { + return FromMatrix((Matrix)left - (Matrix)right); + } + public static Vector operator -(Vector vec) + { + return FromMatrix(-(Matrix)vec); + } + + + + + /// + /// Реализация умножения вектора на матрицу + /// + public static Vector operator *(Matrix left, Vector right) + { + return FromMatrix((Matrix)left * (Matrix)right); + } + + public static Vector operator *(Vector left, double right) + { + Vector result = new Vector(left.Dimensions); + for (int i = 0; i < result.Dimensions; i++) + result[i] = left[i] * right; + return result; + } + + public static Vector operator /(Vector left, double right) + { + Vector result = new Vector(left.Dimensions); + for (int i = 0; i < result.Dimensions; i++) + result[i] = left[i] / right; + return result; + } + + + /// + /// Кмножение двух векторов через оператор будет возвращать их скалярное произведение + /// + public static double operator *(Vector left, Vector right) + { + return left.DotProduct(right); + } + + /// + /// Реализация скалярного произведения двух векторов + /// + public double DotProduct(Vector other) + { + double result = 0; + int length = Math.Min(Dimensions, other.Dimensions); + for (int i = 0; i < length; i++) + result += this[i] * other[i]; + return result; + } + + /// + /// Реализация скалярного произведения двух векторов + /// + public Vector CrossProduct(Vector other) + { + if (Dimensions != 3 || other.Dimensions != 3) + throw new InvalidOperationException("Can CrossProduct only 3D vectors"); + return new Vector(new double[] { + this[1]*other[2] - this[2]*other[1], + this[2]*other[0] - this[0]*other[2], + this[0]*other[1] - this[1]*other[0] + }); + } + + /// + /// Возвращает длину вектора + /// + public double Length + { + get + { + double len = 0; + for (int i = 0; i < Dimensions; i++) + len += Math.Pow(this[i], 2); + return Math.Sqrt(len); + } + } + + public Vector GetNormalized() + { + return this / Length; + } + } +} diff --git a/YMath/YMath.7z b/YMath/YMath.7z new file mode 100644 index 0000000..727a314 Binary files /dev/null and b/YMath/YMath.7z differ diff --git a/YMath/YMath.csproj b/YMath/YMath.csproj new file mode 100644 index 0000000..8361ed2 --- /dev/null +++ b/YMath/YMath.csproj @@ -0,0 +1,10 @@ + + + + net5.0 + enable + nullable + + + +