private static bool IsEarTip(IList <Vector2> polygon, PolygonVertex vertex, IList <PolygonVertex> reflexVertices) { if (vertex.IsReflex) { return(false); } Vector2 a = polygon[vertex.Prev.Index]; Vector2 b = polygon[vertex.Index]; Vector2 c = polygon[vertex.Next.Index]; foreach (var reflexVertex in reflexVertices) { int index = reflexVertex.Index; if (index == vertex.Prev.Index || index == vertex.Next.Index) { continue; } if (TriangleContains(a, b, c, polygon[index])) { return(false); } } return(true); }
private static float WindingValue(IList <Vector2> polygon, PolygonVertex vertex) { Vector2 a = polygon[vertex.Prev.Index]; Vector2 b = polygon[vertex.Index]; Vector2 c = polygon[vertex.Next.Index]; return((float)((b.x - a.x) * (c.y - b.y) - (c.x - b.x) * (b.y - a.y))); }
/// <remarks>v0 is connected to v1 and v1 is connected to v2</remarks> private static Vector3 GetVertexNormal(PolygonVertex v0, PolygonVertex v1, PolygonVertex v2) { var ba = v0.Position - v1.Position; var bc = v2.Position - v1.Position; ba.Normalize(); bc.Normalize(); return(Vector3.Cross(ba, bc)); }
/// <remarks>v0 is connected to v1 and v1 is connected to v2</remarks> private void AddFaceFrom(List <PolygonFace> faces, PolygonVertex v0, PolygonVertex v1, PolygonVertex v2) { //search face foreach (var face in faces) { if (face.Contains(v0) && face.Contains(v1) && face.Contains(v2)) { return; } } var normal = GetVertexNormal(v0, v1, v2); for (int i = 0; i < 3; i++) { var v3 = v2.Edges[i]; if (v3 == v1) { continue; // don't walk back } var normal2 = GetVertexNormal(v1, v2, v3); var dot = Vector3.Dot(normal, normal2); if (dot < 0.85f) { continue; } for (int k = 0; k < 3; k++) { var v4 = v3.Edges[k]; if (v4 == v2) { continue; // don't walk back } var normal3 = GetVertexNormal(v2, v3, v4); dot = Vector3.Dot(normal, normal3); if (dot < 0.85f) { continue; } for (int j = 0; j < 3; j++) { if (v0 == v4.Edges[j]) { var face = new PolygonFace(normal); face.Vertices.Add(v0); face.Vertices.Add(v1); face.Vertices.Add(v2); face.Vertices.Add(v3); face.Vertices.Add(v4); faces.Add(face); } } } } return; }
public bool AddVertex(PolygonVertex v) { if (PolyVertexCache.ContainsKey(v)) { return(false); } PolyVertexCache.Add(v, PolyVertextCount++); return(true); }
/// <summary> /// ランチャーの解析 /// </summary> public List <Launcher> ParseLauncher(float tolerance = 1f) { List <PolygonVertex> vertices = polygon.GetPolygonVertices(); int size = vertices.Count; List <Launcher> launchers = new List <Launcher>(); for (int i = 0; i < size; ++i) { PolygonVertex p1 = vertices[(i + 1) % size]; PolygonVertex p2 = vertices[(i + 2) % size]; float sumAngle = p1.angle + p2.angle; if (180f - tolerance < sumAngle && sumAngle < 180f + tolerance) { PolygonVertex p0 = vertices[i]; PolygonVertex p3 = vertices[(i + 3) % size]; //座標 Vector2 point = (p2.point - p1.point) * 0.5f + p1.point; //角度 float angle = GeomUtil.TwoPointAngle(p0.point, p1.point); //砲身長 float barrel0 = (p1.point - p0.point).magnitude; float barrel1 = (p3.point - p2.point).magnitude; float barrel = (barrel0 + barrel1) * 0.5f; //口径 Line l1 = Line.FromPoints(p0.point, p1.point); Line l2 = Line.FromPoints(p2.point, GeomUtil.RotateVector2(p3.point - p2.point, 90f) + p2.point); //l1の垂直線 Vector2 intersection = Vector2.zero; l1.GetIntersectionPoint(l2, ref intersection); float caliber = (intersection - p2.point).magnitude; launchers.Add(new Launcher(point, angle, barrel, caliber)); } } return(launchers); }
/// <remarks>v0 is connected to v1 and v1 is connected to v2</remarks> private static Vector3 GetVertexNormal(PolygonVertex v0, PolygonVertex v1, PolygonVertex v2) { var ba = v0.Position - v1.Position; var bc = v2.Position - v1.Position; ba.Normalize(); bc.Normalize(); return Vector3.Cross(ba, bc); }
/// <remarks>v0 is connected to v1 and v1 is connected to v2</remarks> private void AddFaceFrom(List<PolygonFace> faces, PolygonVertex v0, PolygonVertex v1, PolygonVertex v2) { //search face foreach (var face in faces) { if (face.Contains(v0) && face.Contains(v1) && face.Contains(v2)) return; } var normal = GetVertexNormal(v0, v1, v2); for (int i = 0; i < 3; i++) { var v3 = v2.Edges[i]; if (v3 == v1) continue; // don't walk back var normal2 = GetVertexNormal(v1, v2, v3); var dot = Vector3.Dot(normal, normal2); if (dot < 0.85f) continue; for (int k = 0; k < 3; k++) { var v4 = v3.Edges[k]; if (v4 == v2) continue; // don't walk back var normal3 = GetVertexNormal(v2, v3, v4); dot = Vector3.Dot(normal, normal3); if (dot < 0.85f) continue; for (int j = 0; j < 3; j++) { if (v0 == v4.Edges[j]) { var face = new PolygonFace(normal); face.Vertices.Add(v0); face.Vertices.Add(v1); face.Vertices.Add(v2); face.Vertices.Add(v3); face.Vertices.Add(v4); faces.Add(face); } } } } return; }
/// <summary> /// Find spreadsheets areas from cells. /// <para>Based on O'Rourke's `Uniqueness of orthogonal connect-the-dots`.</para> /// </summary> /// <param name="cells"></param> public static List <TableRectangle> FindSpreadsheetsFromCells(List <TableRectangle> cells) { // via: http://stackoverflow.com/questions/13746284/merging-multiple-adjacent-rectangles-into-one-polygon List <TableRectangle> rectangles = new List <TableRectangle>(); HashSet <PdfPoint> pointSet = new HashSet <PdfPoint>(); Dictionary <PdfPoint, PdfPoint> edgesH = new Dictionary <PdfPoint, PdfPoint>(); Dictionary <PdfPoint, PdfPoint> edgesV = new Dictionary <PdfPoint, PdfPoint>(); int i = 0; cells = new List <TableRectangle>(new HashSet <TableRectangle>(cells)); Utils.Sort(cells, new TableRectangle.ILL_DEFINED_ORDER()); foreach (TableRectangle cell in cells) { foreach (PdfPoint pt in cell.Points) { if (pointSet.Contains(pt)) { pointSet.Remove(pt); // shared vertex, remove it } else { pointSet.Add(pt); } } } // X first sort List <PdfPoint> pointsSortX = new List <PdfPoint>(pointSet); pointsSortX.Sort(new X_FIRST_POINT_COMPARER()); // Y first sort List <PdfPoint> pointsSortY = new List <PdfPoint>(pointSet); pointsSortY.Sort(new POINT_COMPARER()); while (i < pointSet.Count) { float currY = (float)pointsSortY[i].Y; while (i < pointSet.Count && Utils.Feq(pointsSortY[i].Y, currY)) { edgesH[pointsSortY[i]] = pointsSortY[i + 1]; edgesH[pointsSortY[i + 1]] = pointsSortY[i]; i += 2; } } i = 0; while (i < pointSet.Count) { float currX = (float)pointsSortX[i].X; while (i < pointSet.Count && Utils.Feq(pointsSortX[i].X, currX)) { edgesV[pointsSortX[i]] = pointsSortX[i + 1]; edgesV[pointsSortX[i + 1]] = pointsSortX[i]; i += 2; } } // Get all the polygons List <List <PolygonVertex> > polygons = new List <List <PolygonVertex> >(); PdfPoint nextVertex; while (edgesH.Count != 0) { List <PolygonVertex> polygon = new List <PolygonVertex>(); PdfPoint first = edgesH.Keys.First(); polygon.Add(new PolygonVertex(first, Direction.HORIZONTAL)); edgesH.Remove(first); while (true) { PolygonVertex curr = polygon[polygon.Count - 1]; PolygonVertex lastAddedVertex; if (curr.direction == Direction.HORIZONTAL) { nextVertex = edgesV[curr.point]; edgesV.Remove(curr.point); lastAddedVertex = new PolygonVertex(nextVertex, Direction.VERTICAL); polygon.Add(lastAddedVertex); } else { nextVertex = edgesH[curr.point]; edgesH.Remove(curr.point); lastAddedVertex = new PolygonVertex(nextVertex, Direction.HORIZONTAL); polygon.Add(lastAddedVertex); } if (lastAddedVertex.Equals(polygon[0])) { // closed polygon polygon.RemoveAt(polygon.Count - 1); break; } } foreach (PolygonVertex vertex in polygon) { edgesH.Remove(vertex.point); edgesV.Remove(vertex.point); } polygons.Add(polygon); } // calculate axis-aligned minimum area rectangles for each found polygon foreach (List <PolygonVertex> poly in polygons) { double top = double.MinValue; //java.lang.Float.MAX_VALUE; double left = double.MaxValue; //java.lang.Float.MAX_VALUE; double bottom = double.MaxValue; //java.lang.Float.MIN_VALUE; double right = double.MinValue; //java.lang.Float.MIN_VALUE; foreach (PolygonVertex pt in poly) { top = Math.Max(top, pt.point.Y); // Min left = Math.Min(left, pt.point.X); bottom = Math.Min(bottom, pt.point.Y); // Max right = Math.Max(right, pt.point.X); } rectangles.Add(new TableRectangle(new PdfRectangle(left, bottom, right, top))); // top, left, right - left, bottom - top)); } return(rectangles); }
/// <summary> /// Splits a polygon into triangles. /// </summary> /// <param name="polygon"> /// Polygon to be split. /// </param> /// <returns> /// List of vertices in input polygon that form triangles (will either /// be empty or have count that is a factor of 3). Triangles will be clockwise if /// polygon is clockwise and counter-clockwise if polygon is counter-clockwise. /// </returns> public static IEnumerable <int> Triangulate(IList <Vector2> polygon) { int N = polygon.Count; // Not a polygon. if (N <= 2) { return(new int[] { }); } IList <int> triangles = new List <int>(); // Initialize leftmost and vertices for polygon IList <PolygonVertex> vertices = polygon.Select(v => new PolygonVertex()).ToList(); int iLeftMost = 0; for (int i = 0; i < N; i++) { int iPrev = MathUtil.Mod(i - 1, N); int iNext = MathUtil.Mod(i + 1, N); // Init polygon vertex vertices[i].Index = i; vertices[i].Prev = vertices[iPrev]; vertices[i].Next = vertices[iNext]; vertices[i].Prev.Index = iPrev; vertices[i].Next.Index = iNext; vertices[i].WindingValue = WindingValue(polygon, vertices[i]); vertices[i].IsReflex = false; // Update leftmost for polygon Vector2 p = polygon[i]; Vector2 lm = polygon[iLeftMost]; if (p.x < lm.x || (p.x == lm.x && p.y < lm.y)) { iLeftMost = i; } } // Check if polygon is counter-clockwise bool isCcw = vertices[iLeftMost].WindingValue > 0.0f; // Initialize list of reflex vertices IList <PolygonVertex> reflexVertices = new List <PolygonVertex>(); foreach (var vertex in vertices) { if (IsReflex(isCcw, vertex)) { vertex.IsReflex = true; reflexVertices.Add(vertex); } } // Perform triangulation int skipped = 0; // Number of consecutive vertices skipped int nVertices = vertices.Count; // Number of vertices left in polygon PolygonVertex current = vertices[0]; // While polygon not a triangle while (nVertices > 3) { PolygonVertex prev = current.Prev; PolygonVertex next = current.Next; if (IsEarTip(polygon, current, reflexVertices)) { // Add this ear to list of triangles triangles.Add(prev.Index); triangles.Add(current.Index); triangles.Add(next.Index); // Remove this ear from polygon prev.Next = next; next.Prev = prev; // Re-calculate reflexivity of adjacent vertices PolygonVertex[] adjacent = { prev, next }; foreach (var vertex in adjacent) { if (vertex.IsReflex) { vertex.WindingValue = WindingValue(polygon, vertex); vertex.IsReflex = IsReflex(isCcw, vertex); if (!vertex.IsReflex) { reflexVertices.Remove(vertex); } } } nVertices--; skipped = 0; } else if (++skipped > nVertices) { // If we have gone through all remaining vertices and not found ear, then fail. return(new int[] { }); } current = next; } // Remaining polygon _is_ a triangle. triangles.Add(current.Prev.Index); triangles.Add(current.Index); triangles.Add(current.Next.Index); return(triangles); }
private static bool IsReflex(bool isCcw, PolygonVertex v) { return(isCcw ? v.WindingValue <= 0.0f : v.WindingValue >= 0.0f); }
public bool ContainsVertex(PolygonVertex v) { return(PolyVertexCache.ContainsKey(v)); }