/// <summary>
 /// Треугольник, задаваемый вершинами
 /// </summary>
 /// <param name="v1">Первая вершина</param>
 /// <param name="v2">Вторая вершина</param>
 /// <param name="v3">Третья вершина</param>
 public Triangle(Point v1, Point v2, Point v3)
 {
     // массив сторон треугольника
     double[] sides = new double[] { GeometryCalcs.Distance(v1, v2),
                                     GeometryCalcs.Distance(v2, v3), GeometryCalcs.Distance(v1, v3) };
     _Init(sides);
 }
 /// <summary>
 /// Проверяет корректность сторон треугольника
 /// </summary>
 /// <param name="sides">Массив сторон треугольника</param>
 private void _CheckSides(double[] sides)
 {
     Debug.Assert(sides[0] > 0, "Triangle side 1 must be positive");
     Debug.Assert(sides[1] > 0, "Triangle side 2 must be positive");
     Debug.Assert(sides[2] > 0, "Triangle side 3 must be positive");
     Debug.Assert(GeometryCalcs.TriangleExists(sides[0], sides[1], sides[2]), "Triangle must exist");
 }
 /// <summary>
 /// Инициализирует данные треугольника по массиву его сторон
 /// </summary>
 /// <param name="sides">Массив сторон треугольника</param>
 private void _InitTriangleData(double[] sides)
 {
     // сортируем стороны по длине
     Array.Sort(sides);
     // треугольник может быть равносторонним
     if (GeometryCalcs.TriangleIsEquilateral(sides[0], sides[1], sides[2]))
     {
         this.data = new EquilateralTriangleData(sides[0]);
     }
     // треугольник может быть прямоугольным (первые два элемента после сортировки — катеты)
     else if (GeometryCalcs.TriangleIsRightAngled(sides[0], sides[1], sides[2]))
     {
         this.data = new RightAngledTriangleData(sides[0], sides[1], sides[2]);
     }
     // обычный треугольник, задаваемый сторонами
     else
     {
         this.data = new SemiScaleneTriangleData(sides[0], sides[1], sides[2]);
     }
 }