/// <summary> /// 多边形三角形 /// </summary> /// <param name="_vertexA">顶点A</param> /// <param name="_vertexB">顶点B</param> /// <param name="_vertexC">顶点C</param> public PolygonTriangle2D(PolygonPoint2D _vertexA, PolygonPoint2D _vertexB, PolygonPoint2D _vertexC) { //Vector3 ab = (_vertexB.position - _vertexA.position).normalized; //Vector3 ac = (_vertexC.position - _vertexA.position).normalized; //Vector3 vc = Vector3.Cross(ab, ac); //if (vc.z > 0) //{ // //逆时针三角形,B,C点交换 // PolygonPoint2D p = _vertexC; // _vertexC = _vertexB; // _vertexB = p; //} vertexs = new PolygonPoint2D[3] { _vertexA, _vertexB, _vertexC }; edges = new PolygonEdge2D[3] { new PolygonEdge2D(_vertexA, _vertexB), new PolygonEdge2D(_vertexB, _vertexC), new PolygonEdge2D(_vertexC, _vertexA) }; circumCircleCenter = Geometry2DUtility.TriangleCircumCircle(_vertexA.position, _vertexB.position, _vertexC.position); circumCircleRadius = Vector3.Distance(circumCircleCenter, _vertexA.position); key = (_vertexA.index.ToString() + _vertexB.index.ToString() + _vertexC.ToString()).UniqueHashCode(); }
/// <summary> /// 除遮罩之外都填充 /// </summary> /// <param name="_vh">顶点Helper</param> /// <param name="_masks">绘制组</param> protected virtual void OnFillExceptMask(VertexHelper _vh, List <AbsUIGuideGraphic> _masks) { #region 屏幕矩形 GraphicRect2D sceneRect = new GraphicRect2D(-1, this); #endregion #region 遮罩矩形 List <GraphicRect2D> maskRect = new List <GraphicRect2D>(); GraphicRect2D rect; for (int i = 0; i < _masks.Count; i++) { rect = new GraphicRect2D(i, _masks[i].graphic); maskRect.Add(rect); } //求两矩形填充点和矩形在屏幕上的填充点 List <Vector3> useVertexs = new List <Vector3>(); List <Vector3> tempVertexs = new List <Vector3>(); List <Vector3> validateVertexs = new List <Vector3>(); if (maskRect.Count <= 1) { tempVertexs = maskRect[0].IntersectionFill(sceneRect); if (tempVertexs.Count > 0) { validateVertexs.AddRange(tempVertexs); } } else { foreach (GraphicRect2D a in maskRect) { foreach (GraphicRect2D b in maskRect) { tempVertexs = a.IntersectionFill(b); if (tempVertexs.Count > 0) { validateVertexs.AddRange(tempVertexs); } } } } #endregion validateVertexs.InsertRange(0, sceneRect.corners); #region 清理重复的点 List <int> hasPoint = new List <int>(); int pk = 0; Vector3 tp = Vector3.zero; foreach (Vector3 cv in validateVertexs) { //顶点不超过屏幕 tp.x = cv.x; tp.x = Mathf.Max(tp.x, sceneRect.rect.xMin); tp.x = Mathf.Min(tp.x, sceneRect.rect.xMax); tp.y = cv.y; tp.y = Mathf.Max(tp.y, sceneRect.rect.yMin); tp.y = Mathf.Min(tp.y, sceneRect.rect.yMax); //过滤重复点 pk = (tp.x.ToString() + tp.y.ToString()).UniqueHashCode(); if (!hasPoint.Contains(pk)) { hasPoint.Add(pk); useVertexs.Add(tp); } } #endregion mDelaunayResult = Polygon2DUtility.BuilderDelaunay2D(useVertexs, new List <int>() { 0, 1, 2, 3 }); #region 绘制三角面 _vh.Clear(); Vector4 uv = (sprite != null) ? UnityEngine.Sprites.DataUtility.GetOuterUV(sprite) : Vector4.zero; UIVertex[] vertexs = new UIVertex[mDelaunayResult.vertexs.Count]; List <int> vertexIndexs = new List <int>(); foreach (PolygonPoint2D p in mDelaunayResult.vertexs) { vertexs[p.index] = new UIVertex() { position = p.position, color = color, uv0 = Rect.PointToNormalized(sceneRect.rect, p.position) }; } bool isInAnyRect = false; foreach (PolygonTriangle2D tri in mDelaunayResult.delaunayTriangle) { foreach (GraphicRect2D r in maskRect) { isInAnyRect = Geometry2DUtility.IsPointInRect(tri.vertexs[0].position, r.rect) && Geometry2DUtility.IsPointInRect(tri.vertexs[1].position, r.rect) && Geometry2DUtility.IsPointInRect(tri.vertexs[2].position, r.rect); if (isInAnyRect) { break; } } if (!isInAnyRect) { vertexIndexs.Add(tri.vertexs[0].index); vertexIndexs.Add(tri.vertexs[1].index); vertexIndexs.Add(tri.vertexs[2].index); } } _vh.AddUIVertexStream(new List <UIVertex>(vertexs), vertexIndexs); #endregion }
/// <summary> /// 生成三角剖分结果 /// </summary> /// <param name="_vertexs">离散顶点集</param> /// <param name="_polygonBoundsPointIndexs">多边形边界点索引</param> /// <returns>三角剖分结果</returns> public static PolygonDelaunayResult2D BuilderDelaunay2D(List <Vector3> _vertexs, List <int> _polygonBoundsPointIndexs) { List <PolygonPoint2D> polygonVertexs = new List <PolygonPoint2D>(); Bounds polygonBounds = new Bounds(); PolygonTriangle2D polygonSupperTriangle = new PolygonTriangle2D(new PolygonPoint2D(0, Vector3.zero), new PolygonPoint2D(1, Vector3.zero), new PolygonPoint2D(2, Vector3.zero)); //已完成的剖分三角形 List <PolygonTriangle2D> polygonDelaunayTriangles = new List <PolygonTriangle2D>(); //多边形边界边 Dictionary <int, PolygonEdge2D> polygonBoundsEdgeMaping = new Dictionary <int, PolygonEdge2D>(); if (_vertexs.Count >= 3) { #region 分析点 Vector3 max = Vector3.one * float.MinValue; Vector3 min = Vector3.one * float.MaxValue; //构建顶点 for (int i = 0; i < _vertexs.Count; i++) { polygonVertexs.Add(new PolygonPoint2D(i, _vertexs[i])); min.x = Mathf.Min(min.x, _vertexs[i].x); min.y = Mathf.Min(min.y, _vertexs[i].y); min.z = Mathf.Min(min.z, _vertexs[i].z); max.x = Mathf.Max(max.x, _vertexs[i].x); max.y = Mathf.Max(max.y, _vertexs[i].y); max.z = Mathf.Max(max.z, _vertexs[i].z); } PolygonEdge2D tempPolygonEdge2D = null; if (_polygonBoundsPointIndexs != null && _polygonBoundsPointIndexs.Count > 0) { for (int i = 0; i < _polygonBoundsPointIndexs.Count; i++) { if (i < _polygonBoundsPointIndexs.Count - 1) { tempPolygonEdge2D = new PolygonEdge2D(polygonVertexs[i], polygonVertexs[i + 1]); } else { tempPolygonEdge2D = new PolygonEdge2D(polygonVertexs[i], polygonVertexs[0]); } polygonBoundsEdgeMaping.Add(tempPolygonEdge2D.key, tempPolygonEdge2D); } } //x轴从左往右排序 polygonVertexs.Sort((x, y) => { return(x.position.x >= y.position.x ? 1 : -1); }); //y轴从下往上排序 polygonVertexs.Sort((x, y) => { return((x.position.x == y.position.x && x.position.y >= y.position.y) ? 1 : -1); }); //构建一个包含所有点集的三角形 polygonBounds = new Bounds(Vector3.Lerp(min, max, 0.5f), Vector3.zero); polygonBounds.SetMinMax(min, max); float raudis = Vector3.Distance(polygonBounds.min, polygonBounds.max) * 0.6f; Vector3 tvc = polygonBounds.center + Vector3.back * raudis; Vector3 tvl = polygonBounds.min + Vector3.back * (raudis - Mathf.Abs(polygonBounds.extents.z)); Vector3 axis = tvc - polygonBounds.center; Vector3 vc = (tvl - tvc).normalized * raudis * 2; Vector3 tp0 = vc + tvc; vc = Quaternion.AngleAxis(120, axis) * vc; Vector3 tp1 = vc + tvc; vc = Quaternion.AngleAxis(120, axis) * vc; Vector3 tp2 = vc + tvc; tp0.z = tp1.z = tp2.z = 0; polygonSupperTriangle = new PolygonTriangle2D(new PolygonPoint2D(_vertexs.Count, tp0, true), new PolygonPoint2D(_vertexs.Count + 1, tp1, true), new PolygonPoint2D(_vertexs.Count + 2, tp2, true)); #endregion #region 角剖分 //缓存的剖分三角形 List <PolygonTriangle2D> tempDelaunayTriangles = new List <PolygonTriangle2D>(); //等待检测的三角形 List <PolygonTriangle2D> validateTriangles = new List <PolygonTriangle2D>() { polygonSupperTriangle }; //从第一个顶点开始,进行三角剖分检测 int count = polygonVertexs.Count; for (int i = 0; i < count; i++) { List <PolygonTriangle2D> delTris = FindDelaunayTriangle(polygonVertexs[i], ref validateTriangles); if (delTris.Count > 0) { tempDelaunayTriangles.AddRange(delTris); } } tempDelaunayTriangles.AddRange(validateTriangles); #endregion #region 除超级三角形 //需要补边的三角形 List <PolygonTriangle2D> pushEdgeTriangles = new List <PolygonTriangle2D>(); int supperPointCount = 0; foreach (PolygonTriangle2D tri in tempDelaunayTriangles) { //缓存三角形有任意点是超三角形的点 supperPointCount = 0; foreach (PolygonPoint2D v in tri.vertexs) { if (v.isSupperPoint) { supperPointCount++; } } if (supperPointCount > 0) { pushEdgeTriangles.Add(tri); } else { polygonDelaunayTriangles.Add(tri); foreach (PolygonEdge2D edge in tri.edges) { polygonBoundsEdgeMaping.Remove(edge.key); } } } #endregion #region 对删除的超级三角形与边界比对,补充未绘制的边界三角形 //点索引 int pointIndex = _vertexs.Count; //交点 Vector3 point = Vector3.zero; //边是否相交 bool isIntersection = false; //边交点 Dictionary <int, Dictionary <int, PolygonPoint2D> > edgePoint = new Dictionary <int, Dictionary <int, PolygonPoint2D> >(); //三角形被边界线切割后的多边形点 Dictionary <int, PolygonPoint2D> polygonPoints = new Dictionary <int, PolygonPoint2D>(); //检测三角形 foreach (PolygonTriangle2D tri in pushEdgeTriangles) { polygonPoints.Clear(); foreach (PolygonEdge2D triEdge in tri.edges) { foreach (PolygonEdge2D edge in polygonBoundsEdgeMaping.Values) { #region 求交点 isIntersection = false; point = Vector3.zero; if (edgePoint.ContainsKey(triEdge.key) && edgePoint[triEdge.key].ContainsKey(edge.key)) { isIntersection = true; } else if (Geometry2DUtility.EdgeIntersection(triEdge, edge, ref point)) { isIntersection = true; if (!edgePoint.ContainsKey(triEdge.key)) { edgePoint.Add(triEdge.key, new Dictionary <int, PolygonPoint2D>()); } if (!edgePoint[triEdge.key].ContainsKey(edge.key)) { edgePoint[triEdge.key].Add(edge.key, new PolygonPoint2D(pointIndex, point)); } polygonVertexs.Add(edgePoint[triEdge.key][edge.key]); pointIndex++; } if (isIntersection) { //加入交点 if (!polygonPoints.ContainsKey(edgePoint[triEdge.key][edge.key].index)) { polygonPoints.Add(edgePoint[triEdge.key][edge.key].index, edgePoint[triEdge.key][edge.key]); } } #endregion } } if (polygonPoints.Count > 0) { //如果有任意交点,保留三角形非超级点 foreach (PolygonPoint2D p in tri.vertexs) { if (!p.isSupperPoint) { if (!polygonPoints.ContainsKey(p.index)) { polygonPoints.Add(p.index, p); } } } if (polygonPoints.Count >= 3) { List <PolygonPoint2D> pps = new List <PolygonPoint2D>(polygonPoints.Values); if (pps.Count == 3) { polygonDelaunayTriangles.Add( new PolygonTriangle2D(pps[0], pps[1], pps[2])); } else if (pps.Count == 4) { //如果有两个交点,则pps中前两个是交点,后两个是原三角形的两个顶点 //交点与顶点各取一点组成两条边,然后看两条边是否相交,相交则是对角线,不相交则不是 //找到对角线后,与非对角线点的另外一个交点和顶点组成新的两个三角形 PolygonEdge2D edge1 = new PolygonEdge2D(pps[0], pps[2]); PolygonEdge2D edge2 = new PolygonEdge2D(pps[1], pps[3]); if (Geometry2DUtility.EdgeIntersection(edge1, edge2)) { //0,2,1 和0,2,3 组成两个新的三角形 polygonDelaunayTriangles.Add( new PolygonTriangle2D(pps[0], pps[2], pps[1])); polygonDelaunayTriangles.Add( new PolygonTriangle2D(pps[0], pps[2], pps[3])); } else { //0,3,1 和 0,3,2 组成两个新的三角形 polygonDelaunayTriangles.Add( new PolygonTriangle2D(pps[0], pps[3], pps[1])); polygonDelaunayTriangles.Add( new PolygonTriangle2D(pps[0], pps[3], pps[2])); } } } } } #endregion } else { throw new UnityException("There are not enough vertexs to constitute polygon,the vertexs are least of three."); } return(new PolygonDelaunayResult2D(polygonVertexs, polygonBounds, polygonSupperTriangle, polygonDelaunayTriangles)); }
/// <summary> /// 除遮罩之外都填充 /// </summary> /// <param name="_sceneRect">屏幕矩形</param> /// <param name="_masks">绘制组</param> protected virtual void OnFillExceptMask(Rect _sceneRect, List <AbsUIGuideGraphic> _masks) { Sprite sprite = null; Image image = null; Vector4 spriteUVRatio = Vector4.zero; Vector4 spriteUV = Vector4.zero; Vector4 spriteUVClip = Vector4.zero; Vector4 spriteBorder = Vector4.zero; Vector4 rectUV = Vector4.zero; Vector4 sceneGraphicWh = Vector4.zero; Vector4 graphicRectSlicedBorder = Vector4.zero; Vector4 graphicUvSlicedBorderRatio = Vector4.zero; Vector2 rectMin = Vector2.zero; Vector2 rectMax = Vector2.zero; bool isGraphicSliced = false; sceneGraphicWh.x = _sceneRect.size.x; sceneGraphicWh.y = _sceneRect.size.y; Rect maskRect = new Rect(); for (int i = 0; i < _masks.Count; i++) { spriteUV = uvDefault; spriteUVClip = uvDefault; spriteBorder = Vector4.zero; isGraphicSliced = false; maskRect = _masks[i].graphic.GraphicLocalRectForVertexHelper(); sceneGraphicWh.z = maskRect.size.x; sceneGraphicWh.w = maskRect.size.y; rectMin = Rect.PointToNormalized(_sceneRect, maskRect.min); rectMax = Rect.PointToNormalized(_sceneRect, maskRect.max); rectUV.Set(rectMin.x, rectMin.y, rectMax.x, rectMax.y); if (!Geometry2DUtility.IsPointInRect(maskRect.min, _sceneRect)) { if (rectMin.x == 0) { spriteUVClip.x = Mathf.InverseLerp(maskRect.min.x, maskRect.max.x, _sceneRect.min.x); } if (rectMin.y == 0) { spriteUVClip.y = Mathf.InverseLerp(maskRect.min.y, maskRect.max.y, _sceneRect.min.y); } } if (_masks[i].graphic is Image) { image = (Image)_masks[i].graphic; sprite = image.overrideSprite; if (sprite != null) { spriteUV = UnityEngine.Sprites.DataUtility.GetOuterUV(sprite); Texture2D texSrc = sprite.texture; spriteUVRatio.x = sprite.rect.x / texSrc.width; spriteUVRatio.y = sprite.rect.y / texSrc.height; spriteUVRatio.z = sprite.rect.width / texSrc.width; spriteUVRatio.w = sprite.rect.height / texSrc.height; graphicRectSlicedBorder.x = sprite.border.x / _masks[i].graphic.GetPixelAdjustedRect().width * 0.5f; graphicRectSlicedBorder.y = sprite.border.y / _masks[i].graphic.GetPixelAdjustedRect().height * 0.5f; graphicRectSlicedBorder.z = sprite.border.z / _masks[i].graphic.GetPixelAdjustedRect().width * 0.5f; graphicRectSlicedBorder.w = sprite.border.w / _masks[i].graphic.GetPixelAdjustedRect().height * 0.5f; graphicRectSlicedBorder.z = 1 - graphicRectSlicedBorder.z; graphicRectSlicedBorder.w = 1 - graphicRectSlicedBorder.w; graphicUvSlicedBorderRatio.x = sprite.border.x / sprite.textureRect.width; graphicUvSlicedBorderRatio.y = sprite.border.y / sprite.textureRect.height; graphicUvSlicedBorderRatio.z = sprite.border.z / sprite.textureRect.width; graphicUvSlicedBorderRatio.w = sprite.border.w / sprite.textureRect.height; } switch (image.type) { case Image.Type.Sliced: case Image.Type.Tiled: isGraphicSliced = true; break; } } material.SetTexture("_GraphicTex" + i, _masks[i].graphic.mainTexture); material.SetVector("_GraphicUvSlicedBorderRatio" + i, graphicUvSlicedBorderRatio); material.SetVector("_SceneGraphicWh" + i, sceneGraphicWh); material.SetVector("_GraphicUvRatio" + i, spriteUVRatio); material.SetVector("_GraphicUvMinMax" + i, spriteUV); material.SetVector("_GraphicUvMinMaxClip" + i, spriteUVClip); material.SetVector("_GraphicUvMaskForScene" + i, rectUV); material.SetInt("_isGraphicSliced" + i, isGraphicSliced ? 1 : 0); material.SetVector("_GraphicRectSlicedBorder" + i, graphicRectSlicedBorder); } }