CoordinateTransformer.cs 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. using MathNet.Numerics.LinearAlgebra;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Drawing;
  5. using System.Linq;
  6. using System.Text;
  7. using System.Threading.Tasks;
  8. namespace TeamAAS_VP.Core
  9. {
  10. /// <summary>
  11. /// 三点建立坐标系(或者原点及角度),并实现Local与Word坐标系直接的坐标互转换
  12. /// </summary>
  13. public class CoordinateTransformer
  14. {
  15. /// <summary>
  16. /// 三个点建立坐标系时确定是以X轴作为基准还是Y轴作为基准
  17. /// </summary>
  18. public enum Axis { X, Y };
  19. /// <summary>
  20. /// P1 作为新坐标系的原点
  21. /// </summary>
  22. public Vector<double> P1 { get; }
  23. /// <summary>
  24. /// 坐标系的基向量
  25. /// </summary>
  26. public Vector<double> E1 { get; }
  27. /// <summary>
  28. /// 坐标系的基向量
  29. /// </summary>
  30. public Vector<double> E2 { get; }
  31. public Matrix<double> R { get; }
  32. /// <summary>
  33. /// 旋转矩阵
  34. /// </summary>
  35. public Matrix<double> InvR { get; }
  36. /// <summary>
  37. /// 平移矩阵
  38. /// </summary>
  39. public Matrix<double> T { get; }
  40. /// <summary>
  41. /// 坐标系的旋转角度
  42. /// </summary>
  43. public double Angle
  44. {
  45. get
  46. {
  47. //Vector2 e1 = new Vector2(Math.Cos(radians), Math.Sin(radians)); // 新x轴向量
  48. //Vector2 e2 = new Vector2(-Math.Sin(radians), Math.Cos(radians)); // 新y轴向量
  49. double thetaRand1 = Math.Asin(E1[1]);
  50. double thetaDeg1 = thetaRand1 * 180 / Math.PI; // 将弧度值转换为角度值
  51. return Math.Round(thetaDeg1, 3);
  52. }
  53. }
  54. /// <summary>
  55. /// 三点建立坐标系,并实现Local与Word坐标系直接的坐标互转换
  56. /// </summary>
  57. /// <param name="x1">原点X坐标</param>
  58. /// <param name="y1">原点Y坐标</param>
  59. /// <param name="x2">X轴上的点X坐标</param>
  60. /// <param name="y2">X轴上的点Y坐标</param>
  61. /// <param name="x3">Y轴上的点X坐标</param>
  62. /// <param name="y3">Y轴上的点Y坐标</param>
  63. /// <param name="axis">三个点建立坐标系时确定是以X轴作为基准还是Y轴作为基准</param>
  64. public CoordinateTransformer(double x1, double y1, double x2, double y2, double x3, double y3, Axis axis = Axis.X)
  65. {
  66. //假设我们有三个点 P1(x1, y1), P2(x2, y2), P3(x3, y3),它们是笛卡尔坐标系的三个点。我们要基于这三个点创建一个经过旋转和平移的坐标系,并且要求两个坐标系中的点可以互相转换。
  67. //1.新坐标系的原点:我们可以将 P1 作为新坐标系的原点。因此,新坐标系的原点坐标为 (x1, y1)。
  68. P1 = Vector<double>.Build.Dense(new double[] { x1, y1 });
  69. // Calculate basis vectors
  70. Vector<double> p2 = Vector<double>.Build.Dense(new double[] { x2, y2 });
  71. Vector<double> p3 = Vector<double>.Build.Dense(new double[] { x3, y3 });
  72. //2.计算坐标系的基向量:
  73. if (axis == Axis.X)
  74. {
  75. E1 = (p2 - P1) / (p2 - P1).L2Norm();
  76. E2 = (p3 - P1 - E1.DotProduct(p3 - P1) * E1) / (p3 - P1 - E1.DotProduct(p3 - P1) * E1).L2Norm();
  77. }
  78. else
  79. {
  80. E2 = (p3 - P1) / (p3 - P1).L2Norm();
  81. E1 = (p2 - P1 - E2.DotProduct(p2 - P1) * E2) / (p2 - P1 - E2.DotProduct(p2 - P1) * E2).L2Norm();
  82. }
  83. //3.计算旋转矩阵:我们可以使用基向量组成的矩阵作为旋转矩阵。具体地,我们可以将基向量 e1 和 e2 分别作为新坐标系下的 $x$ 和 $y$ 轴,然后组成一个矩阵,再对这个矩阵求逆即可得到旋转矩阵。代码实现:
  84. // Compute rotation matrix
  85. R = Matrix<double>.Build.DenseOfColumnVectors(E1, E2);
  86. InvR = R.Inverse();
  87. //4.平移矩阵:由于我们选择的原点是 P1,因此我们需要对所有点进行平移,使得新坐标系的原点与笛卡尔坐标系的原点重合。具体地,我们可以使用以下平移矩阵:
  88. // Compute translation matrix
  89. T = Matrix<double>.Build.DenseIdentity(3);
  90. T[0, 2] = -P1[0];
  91. T[1, 2] = -P1[1];
  92. }
  93. /// <summary>
  94. /// 三点建立坐标系,并实现Local与Word坐标系直接的坐标互转换
  95. /// </summary>
  96. /// <param name="Po">原点坐标</param>
  97. /// <param name="Px">X轴上的点坐标</param>
  98. /// <param name="Py">Y轴上的点坐标</param>
  99. /// <param name="axis">三个点建立坐标系时确定是以X轴作为基准还是Y轴作为基准</param>
  100. public CoordinateTransformer(PointF Po, PointF Px, PointF Py, Axis axis = Axis.X)
  101. {
  102. //假设我们有三个点 P1(x1, y1), P2(x2, y2), P3(x3, y3),它们是笛卡尔坐标系的三个点。我们要基于这三个点创建一个经过旋转和平移的坐标系,并且要求两个坐标系中的点可以互相转换。
  103. //1.新坐标系的原点:我们可以将 P1 作为新坐标系的原点。因此,新坐标系的原点坐标为 (x1, y1)。
  104. P1 = Vector<double>.Build.Dense(new double[] { Po.X, Po.Y });
  105. // Calculate basis vectors
  106. Vector<double> p2 = Vector<double>.Build.Dense(new double[] { Px.X, Px.Y });
  107. Vector<double> p3 = Vector<double>.Build.Dense(new double[] { Py.X, Py.Y });
  108. //2.计算坐标系的基向量:
  109. if (axis == Axis.X)
  110. {
  111. E1 = (p2 - P1) / (p2 - P1).L2Norm();
  112. E2 = (p3 - P1 - E1.DotProduct(p3 - P1) * E1) / (p3 - P1 - E1.DotProduct(p3 - P1) * E1).L2Norm();
  113. }
  114. else
  115. {
  116. E2 = (p3 - P1) / (p3 - P1).L2Norm();
  117. E1 = (p2 - P1 - E2.DotProduct(p2 - P1) * E2) / (p2 - P1 - E2.DotProduct(p2 - P1) * E2).L2Norm();
  118. }
  119. //3.计算旋转矩阵:我们可以使用基向量组成的矩阵作为旋转矩阵。具体地,我们可以将基向量 e1 和 e2 分别作为新坐标系下的 $x$ 和 $y$ 轴,然后组成一个矩阵,再对这个矩阵求逆即可得到旋转矩阵。代码实现:
  120. // Compute rotation matrix
  121. R = Matrix<double>.Build.DenseOfColumnVectors(E1, E2);
  122. InvR = R.Inverse();
  123. //4.平移矩阵:由于我们选择的原点是 P1,因此我们需要对所有点进行平移,使得新坐标系的原点与笛卡尔坐标系的原点重合。具体地,我们可以使用以下平移矩阵:
  124. // Compute translation matrix
  125. T = Matrix<double>.Build.DenseIdentity(3);
  126. T[0, 2] = -P1[0];
  127. T[1, 2] = -P1[1];
  128. }
  129. /// <summary>
  130. /// 原点及角度建立坐标系
  131. /// </summary>
  132. /// <param name="Po">原点坐标</param>
  133. /// <param name="Theta">角度</param>
  134. public CoordinateTransformer(PointF Po, double theta)
  135. {
  136. double radians = theta * Math.PI / 180; // 将角度值转换为弧度值
  137. //1.新坐标系的原点:我们可以将 P1 作为新坐标系的原点。因此,新坐标系的原点坐标为 (x1, y1)。
  138. P1 = Vector<double>.Build.Dense(new double[] { Po.X, Po.Y });
  139. //2.计算坐标系的基向量:
  140. E1 = Vector<double>.Build.Dense(new double[] { Math.Cos(radians), Math.Sin(radians) }); // 新x轴向量
  141. E2 = Vector<double>.Build.Dense(new double[] { -Math.Sin(radians), Math.Cos(radians) }); // 新y轴向量
  142. //3.计算旋转矩阵:我们可以使用基向量组成的矩阵作为旋转矩阵。具体地,我们可以将基向量 e1 和 e2 分别作为新坐标系下的 $x$ 和 $y$ 轴,然后组成一个矩阵,再对这个矩阵求逆即可得到旋转矩阵。代码实现:
  143. // Compute rotation matrix
  144. R = Matrix<double>.Build.DenseOfColumnVectors(E1, E2);
  145. InvR = R.Inverse();
  146. //4.平移矩阵:由于我们选择的原点是 P1,因此我们需要对所有点进行平移,使得新坐标系的原点与笛卡尔坐标系的原点重合。具体地,我们可以使用以下平移矩阵:
  147. // Compute translation matrix
  148. T = Matrix<double>.Build.DenseIdentity(3);
  149. T[0, 2] = -P1[0];
  150. T[1, 2] = -P1[1];
  151. }
  152. /// <summary>
  153. /// Word坐标系下的点转换成Local坐标系下的点
  154. /// </summary>
  155. /// <param name="x">Word坐标系下的点X坐标</param>
  156. /// <param name="y">Word坐标系下的点Y坐标</param>
  157. /// <returns>返回Local坐标系下的点</returns>
  158. public Vector<double> ToNewCoord(double x, double y)
  159. {
  160. Vector<double> pOld = Vector<double>.Build.Dense(new double[] { x, y });
  161. Vector<double> pNew = InvR * (pOld - P1);
  162. return pNew;
  163. }
  164. /// <summary>
  165. /// Local坐标系下的点转换成Word坐标系下的点
  166. /// </summary>
  167. /// <param name="x">Local坐标系下的点X坐标</param>
  168. /// <param name="y">Local坐标系下的点Y坐标</param>
  169. /// <returns>返回Word坐标系下的点</returns>
  170. public Vector<double> ToOldCoord(double x, double y)
  171. {
  172. Vector<double> pNew = Vector<double>.Build.Dense(new double[] { x, y });
  173. Vector<double> pOld = R * pNew + P1;
  174. return pOld;
  175. }
  176. }
  177. }