private void DrawTriangle(OpenGL gl, trTriangle triangle, Interfaces.Color color) { gl.Color(color.R / 255.0f, color.G / 255.0f, color.B / 255.0f, color.A / 255.0f); gl.Begin(OpenGL.GL_TRIANGLES); gl.Vertex(triangle.A.X, openGLControlView.Height - triangle.A.Y); gl.Vertex(triangle.B.X, openGLControlView.Height - triangle.B.Y); gl.Vertex(triangle.C.X, openGLControlView.Height - triangle.C.Y); gl.End(); }
private string IsPointInTriagle(Point point, trTriangle triangle, ref Point[] edgeWithPoint) { double x1 = triangle.A.X; double y1 = triangle.A.Y; double x2 = triangle.B.X; double y2 = triangle.B.Y; double x3 = triangle.C.X; double y3 = triangle.C.Y; double x0 = point.X; double y0 = point.Y; double first = (x1 - x0) * (y2 - y1) - (x2 - x1) * (y1 - y0); double second = (x2 - x0) * (y3 - y2) - (x3 - x2) * (y2 - y0); double third = (x3 - x0) * (y1 - y3) - (x1 - x3) * (y3 - y0); double[] scalarProduct = new double[3] { first, second, third }; List <int> signs = scalarProduct.Select(x => Math.Sign(Math.Round(x, ClosenessCount))).ToList(); int EdgeNumb = signs.FindIndex(x => Math.Abs(x) < ClosenessMeasure); if (EdgeNumb != -1) { Point beg = new Point(), end = new Point(); switch (EdgeNumb) { case 0: { beg = new Point(x2, y2); end = new Point(x1, y1); break; } case 1: { beg = new Point(x3, y3); end = new Point(x2, y2); break; } case 2: { beg = new Point(x3, y3); end = new Point(x1, y1); break; } } if (point.X >= beg.X && point.X <= end.X || point.X <= beg.X && point.X >= end.X) { edgeWithPoint[0] = beg; edgeWithPoint[1] = end; return("On the border"); } else { return("External"); } } else if (signs.All(x => x < 0) || signs.All(x => x > 0)) { return("Inner"); } else { return("External"); } }
protected void CreateTriangulation() { //просто одно ребро, потом понадобится Point[] edge; _triangles = new List <trTriangle>(); _figureBorder = new List <Point>(); foreach (var Border in _onlyPoints) { _figureBorder.AddRange(Border); } //начальная триангуляция с исползованием выпуклой оболочки for (int i = 0; i < _convexHull.Count - 2; i++) { Point A = _convexHull[0]; Point B = _convexHull[i + 1]; Point C = _convexHull[i + 2]; //Point center = new Point((A.X + B.X + C.X) / 3, (A.Y + B.Y + C.Y) / 3); _triangles.Add(new trTriangle(_convexHull[0], _convexHull[i + 1], _convexHull[i + 2])); } #region подготовка к корректированию триангуляции //выбираем вершины, не попавшие в выпуклую оболочку List <Point> InnerOrBoundary = _figureBorder.Except(_convexHull, new PointComparer()).ToList(); if (InnerOrBoundary.Count != 0) { //для каждой (не из выпуклой оболочки) вершине ставим в соответсиве два треугольника, которые содержат на своей границе эту вершину. List <List <trTriangle> > boundaryTriangles = new List <List <trTriangle> >(); //в соответствие этим треугольникам поставим ребро, которое содержит на себе точку. List <Point[]> edgesWithPoint = new List <Point[]>(); edge = new Point[2]; //вершины, которые собственно и лежат на границе List <Point> boundaryPoints = new List <Point>(); //вершины внутри выпуклой оболочки и не на границе List <Point> innerPoints = new List <Point>(); DeleteBoundaryPoints: for (int i = 0; i < InnerOrBoundary.Count; i++) { var twoTriangles = _triangles.Where(x => IsPointInTriagle(InnerOrBoundary[i], x, ref edge) == "On the border").ToList(); if (twoTriangles.Count != 0) { boundaryTriangles.Add(twoTriangles); edgesWithPoint.Add(edge); boundaryPoints.Add(InnerOrBoundary[i]); } else { innerPoints.Add(InnerOrBoundary[i]); } } #endregion //теперь у нас есть соответствие (вершина на ребре - ребро - треугольники, для которых это ребро смежное) //+ вершины не на ребрах #region нейтрализация вершин на границах //Вместо двух треугольников запихаем четыре. while (boundaryTriangles.Count != 0) { foreach (trTriangle triangle in boundaryTriangles.First()) { List <Point> trianglePoints = new List <Point> { triangle.A, triangle.B, triangle.C }; IEnumerable <Point> outerPoint = trianglePoints.Except(edgesWithPoint.First(), new PointComparer()); var firstAdding = new trTriangle(outerPoint.First(), edgesWithPoint.First()[0], boundaryPoints.First()); var secondAdding = new trTriangle(outerPoint.First(), edgesWithPoint.First()[1], boundaryPoints.First()); _triangles.Add(firstAdding); _triangles.Add(secondAdding); //убираем использованный _triangles.Remove(triangle); //теперь, если вдруг после добавления тех двух треугольников внутренняя точка перешла в разряд точек на границе //мы ее добавим в конец в граничные, а так же два треугольникак как смежные и тд. foreach (Point point in innerPoints) { if (IsPointAtLine(point, edgesWithPoint.First()[0], edgesWithPoint.First()[1])) { boundaryTriangles.Add(new List <trTriangle>() { firstAdding, secondAdding }); edgesWithPoint.Add(edgesWithPoint.First()); boundaryPoints.Add(point); } } } //обработали первую граничную точку, убрали все с ней связанное, и по новой boundaryTriangles.RemoveAt(0); edgesWithPoint.RemoveAt(0); boundaryPoints.RemoveAt(0); } #endregion edge = new Point[2]; #region обработка внутренних точек //работаем с тупо внутренними точками while (innerPoints.Count != 0) { Point considered = innerPoints.First(); //найдем внешний для точки треугольник int indexExtTriangle = _triangles.FindIndex(x => IsPointInTriagle(considered, x, ref edge) == "Inner"); trTriangle externalTriangle = _triangles[indexExtTriangle]; //создадим три новых треугольника вместо старого trTriangle first = new trTriangle(externalTriangle.A, externalTriangle.B, considered); trTriangle second = new trTriangle(externalTriangle.B, externalTriangle.C, considered); trTriangle third = new trTriangle(externalTriangle.A, externalTriangle.C, considered); _triangles.Add(first); _triangles.Add(second); _triangles.Add(third); //точку и старый треугольник удалим _triangles.RemoveAt(indexExtTriangle); innerPoints.RemoveAt(0); //определим точки, которые не были на ребрах, а теперь стали boundaryPoints.AddRange(innerPoints.FindAll(x => IsPointAtLine(x, externalTriangle.A, considered))); boundaryPoints.AddRange(innerPoints.FindAll(x => IsPointAtLine(x, externalTriangle.B, considered))); boundaryPoints.AddRange(innerPoints.FindAll(x => IsPointAtLine(x, externalTriangle.C, considered))); innerPoints.RemoveAll(x => boundaryPoints.Contains(x)); //если в итоге такие точки есть, то по новой их удалять if (boundaryPoints.Count != 0) { //очень плохая идея и некачественный код InnerOrBoundary.Clear(); InnerOrBoundary.AddRange(boundaryPoints); boundaryPoints.Clear(); goto DeleteBoundaryPoints; } } #endregion #region коррекция пересечения ребер с треугольниками в триангуляции _figureBorder.Clear(); foreach (List <Point> Border in _onlyPoints) { _figureBorder.AddRange(Border); _figureBorder.Add(Border[0]); _figureBorder.Add(null); } for (int i = 0; i < _figureBorder.Count - 1; i++) {//организовать цикл по всем ребрам, включая последнее if (_figureBorder[i] == null || _figureBorder[i + 1] == null) { continue; } edge[0] = _figureBorder[i]; edge[1] = _figureBorder[i + 1]; List <int> ToRemove = new List <int>(); List <trTriangle> ToAdd = new List <trTriangle>(); foreach (var item in _triangles.Select((value, j) => new { value, j })) { List <trTriangle> possibleAdd = LineTriangleIntersection(edge, item.value); if (possibleAdd != null) { ToAdd.AddRange(possibleAdd); ToRemove.Add(item.j); } } foreach (int index in ToRemove.OrderByDescending(item => item)) { _triangles.RemoveAt(index); } _triangles.AddRange(ToAdd); } #endregion //выкинем те треугольники, которые не лежат в фигуре //(триангулировалась выпуклая оболочка с точками внутри) List <int> ExtraTriangles = new List <int>(); foreach (var item in _triangles.Select((value, i) => new { i, value })) { if (!IsPointInner(item.value.GetCenter())) { ExtraTriangles.Add(item.i); } } foreach (int index in ExtraTriangles.OrderByDescending(item => item)) { _triangles.RemoveAt(index); } } }
private List <trTriangle> LineTriangleIntersection(Point[] line, trTriangle triangle) { List <Point> dots = new List <Point> { triangle.A, triangle.B, triangle.C }; //чтобы понять, необходимо нарисовать пересеение линии и треугольника //сделаем соответствие - {пересекает ли искомая линия ребро треугольника; само ребро} var all = new[] { new { intersection = getLinesIntersect(line[0], line[1], triangle.A, triangle.B), segmt = new Point[2] { triangle.A, triangle.B } }, new { intersection = getLinesIntersect(line[0], line[1], triangle.B, triangle.C), segmt = new Point[2] { triangle.B, triangle.C } }, new { intersection = getLinesIntersect(line[0], line[1], triangle.A, triangle.C), segmt = new Point[2] { triangle.A, triangle.C } } }.ToList(); //если не пересекает ни одно, то выйдем if (all.All(item => item.intersection == null)) { return(null); } //иначе вернем новые треугольники вместо старого List <trTriangle> result = new List <trTriangle>(); //точки пересечения var intersecters = all.FindAll(item => item.intersection != null); //костыль: если треугольник пересекается линией, исходящей из его вершины if (intersecters.Count == 1) { Point beginOfLine = dots.Except ( new List <Point>() { intersecters[0].segmt[0], intersecters[0].segmt[1] }, new PointComparer() ).First(); result.Add(new trTriangle(beginOfLine, intersecters.First().intersection, intersecters.First().segmt[0])); result.Add(new trTriangle(beginOfLine, intersecters.First().intersection, intersecters.First().segmt[1])); return(result); } else { List <Point> intersectVertex = intersecters.Select(item => item.intersection).ToList(); if (intersectVertex[0] == intersectVertex[1]) { return(null); } //запилим три треугольника вместо одного //первый (включающий две точки пересечения) Point thirdVertex = new Point( intersecters[0].segmt[0] == intersecters[1].segmt[0] || intersecters[0].segmt[0] == intersecters[1].segmt[1] ? intersecters[0].segmt[0] : intersecters[0].segmt[1]); result.Add(new trTriangle(thirdVertex, intersectVertex[0], intersectVertex[1])); //два других //возьмем то ребро, которое она не пересекает Point[] OnNonIntersctEdge = all.Find(item => item.intersection == null).segmt; //чтобы точно сделать из двух ребер два треугольника (непересекающихся) //нужно отсортировать точки в них if (OnNonIntersctEdge[0].Y > OnNonIntersctEdge[1].Y) { Swap(ref OnNonIntersctEdge[0], ref OnNonIntersctEdge[1]); } //не буду писать метод для одной замены if (intersectVertex[0].Y > intersectVertex[1].Y) { var temp = intersectVertex[0]; intersectVertex[0] = intersectVertex[1]; intersectVertex[1] = temp; } result.Add(new trTriangle(OnNonIntersctEdge[0], intersectVertex[0], intersectVertex[1])); result.Add(new trTriangle(OnNonIntersctEdge[0], OnNonIntersctEdge[1], intersectVertex[1])); return(result); } }