public Polygon GetPolygon(int edgeSubdivisions, float curvature) { Connector connector = new Connector (); for (int k=0; k<segments.Count; k++) { Segment s = segments [k]; if (!s.deleted) { if (edgeSubdivisions>1) { connector.AddRange (s.Subdivide(edgeSubdivisions, curvature)); } else { connector.Add (s); } } } return connector.ToPolygonFromLargestLineStrip (); }
GameObject GenerateCellRegionSurface(int cellIndex, Material material) { if (cellIndex<0 || cellIndex>=cells.Count) return null; Region region = cells [cellIndex].region; // Calculate region's surface points int numSegments = region.segments.Count; Connector connector = new Connector(); if (_terrain==null) { connector.AddRange(region.segments); } else { for (int i = 0; i<numSegments; i++) { Segment s = region.segments[i]; SurfaceSegmentForSurface(s, connector); } } Geom.Polygon surfacedPolygon = connector.ToPolygonFromLargestLineStrip(); List<Point> surfacedPoints = surfacedPolygon.contours[0].points; List<PolygonPoint> ppoints = new List<PolygonPoint>(surfacedPoints.Count); for (int k=0;k<surfacedPoints.Count;k++) { double x = surfacedPoints[k].x+2; double y = surfacedPoints[k].y+2; if (!IsTooNearPolygon(x, y, ppoints)) { float h = _terrain!=null ? _terrain.SampleHeight(transform.TransformPoint((float)x-2, (float)y-2,0)): 0; ppoints.Add (new PolygonPoint(x, y, h)); } } Poly2Tri.Polygon poly = new Poly2Tri.Polygon(ppoints); if (_terrain!=null) { if (steinerPoints==null) { steinerPoints = new List<TriangulationPoint>(6000); } else { steinerPoints.Clear(); } float stepX = 1.0f / heightMapWidth; float smallStep = 1.0f / heightMapWidth; float y = region.rect2D.yMin + smallStep; float ymax = region.rect2D.yMax - smallStep; float[] acumY = new float[terrainRoughnessMapWidth]; while(y<ymax) { int j = (int)((y + 0.5f) * terrainRoughnessMapHeight); // * heightMapHeight)) / TERRAIN_CHUNK_SIZE; if (j>=0) { if (j>=terrainRoughnessMapHeight) j=terrainRoughnessMapHeight-1; float sy = y + 2; float xin = GetFirstPointInRow(sy, ppoints) + smallStep; float xout = GetLastPointInRow(sy, ppoints) - smallStep; int k0 = -1; for (float x = xin; x<xout; x+=stepX) { int k = (int)((x + 0.5f) * terrainRoughnessMapWidth); //)) / TERRAIN_CHUNK_SIZE; if (k>=terrainRoughnessMapWidth) k=terrainRoughnessMapWidth-1; if (k0!=k) { k0=k; stepX = terrainRoughnessMap[j,k]; if (acumY[k] >= stepX) acumY[k] = 0; acumY[k] += smallStep; } if (acumY[k] >= stepX) { // Gather precision height float h = _terrain.SampleHeight (transform.TransformPoint(x,y,0)); float htl = _terrain.SampleHeight (transform.TransformPoint (x-smallStep, y+smallStep, 0)); if (htl>h) h = htl; float htr = _terrain.SampleHeight (transform.TransformPoint (x+smallStep, y+smallStep, 0)); if (htr>h) h = htr; float hbr = _terrain.SampleHeight (transform.TransformPoint (x+smallStep, y-smallStep, 0)); if (hbr>h) h = hbr; float hbl = _terrain.SampleHeight (transform.TransformPoint (x-smallStep, y-smallStep, 0)); if (hbl>h) h = hbl; steinerPoints.Add (new PolygonPoint (x+2, sy, h)); } } } y += smallStep; if (steinerPoints.Count>80000) { break; } } poly.AddSteinerPoints(steinerPoints); } P2T.Triangulate(poly); // Calculate & optimize mesh data int pointCount = poly.Triangles.Count*3; List<Vector3> meshPoints = new List<Vector3> (pointCount); int[] triNew = new int[pointCount]; if (surfaceMeshHit == null) surfaceMeshHit = new Dictionary<TriangulationPoint, int> (2000); else surfaceMeshHit.Clear (); int triNewIndex =-1; int newPointsCount = -1; if (_gridNormalOffset>0) { for (int k=0;k<poly.Triangles.Count;k++) { DelaunayTriangle dt = poly.Triangles[k]; TriangulationPoint p = dt.Points [0]; if (surfaceMeshHit.ContainsKey (p)) { triNew [++triNewIndex] = surfaceMeshHit [p]; } else { Vector3 np = new Vector3(p.Xf-2, p.Yf-2, -p.Zf); np += transform.InverseTransformVector(_terrain.terrainData.GetInterpolatedNormal(np.x+0.5f,np.y+0.5f)) * _gridNormalOffset; meshPoints.Add (np); surfaceMeshHit.Add (p, ++newPointsCount); triNew [++triNewIndex] = newPointsCount; } p = dt.Points [2]; if (surfaceMeshHit.ContainsKey (p)) { triNew [++triNewIndex] = surfaceMeshHit [p]; } else { Vector3 np = new Vector3(p.Xf-2, p.Yf-2, -p.Zf); np += transform.InverseTransformVector(_terrain.terrainData.GetInterpolatedNormal(np.x+0.5f,np.y+0.5f)) * _gridNormalOffset; meshPoints.Add (np); surfaceMeshHit.Add (p, ++newPointsCount); triNew [++triNewIndex] = newPointsCount; } p = dt.Points [1]; if (surfaceMeshHit.ContainsKey (p)) { triNew [++triNewIndex] = surfaceMeshHit [p]; } else { Vector3 np = new Vector3(p.Xf-2, p.Yf-2, -p.Zf); np += transform.InverseTransformVector(_terrain.terrainData.GetInterpolatedNormal(np.x+0.5f,np.y+0.5f)) * _gridNormalOffset; meshPoints.Add (np); surfaceMeshHit.Add (p, ++newPointsCount); triNew [++triNewIndex] = newPointsCount; } } } else { for (int k=0;k<poly.Triangles.Count;k++) { DelaunayTriangle dt = poly.Triangles[k]; TriangulationPoint p = dt.Points [0]; if (surfaceMeshHit.ContainsKey (p)) { triNew [++triNewIndex] = surfaceMeshHit [p]; } else { Vector3 np = new Vector3(p.Xf-2, p.Yf-2, -p.Zf); meshPoints.Add (np); surfaceMeshHit.Add (p, ++newPointsCount); triNew [++triNewIndex] = newPointsCount; } p = dt.Points [2]; if (surfaceMeshHit.ContainsKey (p)) { triNew [++triNewIndex] = surfaceMeshHit [p]; } else { Vector3 np = new Vector3(p.Xf-2, p.Yf-2, -p.Zf); meshPoints.Add (np); surfaceMeshHit.Add (p, ++newPointsCount); triNew [++triNewIndex] = newPointsCount; } p = dt.Points [1]; if (surfaceMeshHit.ContainsKey (p)) { triNew [++triNewIndex] = surfaceMeshHit [p]; } else { Vector3 np = new Vector3(p.Xf-2, p.Yf-2, -p.Zf); meshPoints.Add (np); surfaceMeshHit.Add (p, ++newPointsCount); triNew [++triNewIndex] = newPointsCount; } } } int cacheIndex = GetCacheIndexForCellRegion (cellIndex); string cacheIndexSTR = cacheIndex.ToString(); // Deletes potential residual surface Transform t = surfacesLayer.transform.FindChild(cacheIndexSTR); if (t!=null) DestroyImmediate(t.gameObject); GameObject surf = Drawing.CreateSurface (cacheIndexSTR, meshPoints.ToArray(), triNew, material); _lastVertexCount += surf.GetComponent<MeshFilter>().sharedMesh.vertexCount; surf.transform.SetParent (surfacesLayer.transform, false); surf.transform.localPosition = Vector3.zero; surf.layer = gameObject.layer; if (surfaces.ContainsKey(cacheIndex)) surfaces.Remove(cacheIndex); surfaces.Add (cacheIndex, surf); return surf; }
void SurfaceSegmentForSurface(Segment s, Connector connector) { // trace the line until roughness is exceeded double dist = s.magnitude; // (float)Math.Sqrt ( (p1.x-p0.x)*(p1.x-p0.x) + (p1.y-p0.y)*(p1.y-p0.y)); Point direction = s.end - s.start; int numSteps = (int)(dist / MIN_VERTEX_DISTANCE); Point t0 = s.start; float h0 = _terrain.SampleHeight(transform.TransformPoint(t0.vector3)); Point ta = t0; float h1; for (int i=1;i<numSteps;i++) { Point t1 = s.start + direction * i / numSteps; h1 = _terrain.SampleHeight(transform.TransformPoint(t1.vector3)); if (h0 < h1 || h0-h1 > effectiveRoughness) { //-effectiveRoughness) { if (t0!=ta) { Segment s0 = new Segment(t0, ta, s.border); connector.Add (s0); Segment s1 = new Segment(ta, t1, s.border); connector.Add (s1); } else { Segment s0 = new Segment(t0, t1, s.border); connector.Add (s0); } t0 = t1; h0 = h1; } ta = t1; } // Add last point Segment finalSeg = new Segment(t0, s.end, s.border); connector.Add (finalSeg); }
void FindTerritoryFrontiers() { if (territories==null) return; if (territoryFrontiers == null) { territoryFrontiers = new List<Segment>(cellNeighbourHit.Count); } else { territoryFrontiers.Clear (); } if (territoryNeighbourHit == null) { territoryNeighbourHit = new Dictionary<Segment, Region> (50000); } else { territoryNeighbourHit.Clear (); } Connector[] connectors = new Connector[territories.Count]; for (int k=0;k<territories.Count;k++) connectors[k] = new Connector(); for (int k=0; k<cells.Count; k++) { Cell cell = cells [k]; Region region = cell.region; int numSegments = region.segments.Count; for (int i = 0; i<numSegments; i++) { Segment seg = region.segments[i]; if (seg.border) { territoryFrontiers.Add (seg); int territory1 = cell.territoryIndex; connectors[territory1].Add (seg); continue; } if (territoryNeighbourHit.ContainsKey (seg)) { Region neighbour = territoryNeighbourHit [seg]; Cell neighbourCell = (Cell)neighbour.entity; if (neighbourCell.territoryIndex!=cell.territoryIndex) { territoryFrontiers.Add (seg); int territory1 = cell.territoryIndex; connectors[territory1].Add (seg); int territory2 = neighbourCell.territoryIndex; connectors[territory2].Add (seg); } } else { territoryNeighbourHit.Add (seg, region); } } } for (int k=0;k<territories.Count;k++) territories[k].polygon = connectors[k].ToPolygonFromLargestLineStrip(); }
void SetupHexagonalGrid() { // Make cell regions: we assume cells have only 1 region but that can change in the future int l = _numCells; int qx, qy; int q = (int)(Math.Sqrt (l)); q = q * 12 / 13; if (q<1) q= 1; qx=qy=q; int qx2 = qx * 4 / 3; double stepX = (transform.localScale.y / transform.localScale.x) / qx; double stepY = (transform.localScale.x / transform.localScale.y) / qy; double halfStepX = stepX*0.5; double halfStepY = stepY*0.5; Segment [,,] sides = new Segment[qx2,qy,6]; // 0 = left-up, 1 = top, 2 = right-up, 3 = right-down, 4 = down, 5 = left-down int c = -1; int subdivisions = goodGridCurvature > 0 ? 3: 1; for (int j=0;j<qy;j++) { for (int k=0;k<qx2;k++) { Point center = new Point((double)k/qx-0.5+halfStepX,(double)j/qy-0.5+halfStepY); center.x -= k * halfStepX/2; Cell cell = new Cell( (++c).ToString(), new Vector2((float)center.x, (float)center.y)); double offsetY = (k % 2==0) ? 0: -halfStepY; Segment leftUp = (k>0 && offsetY<0) ? sides[k-1, j, 3]: new Segment(center.Offset(-halfStepX, offsetY), center.Offset(-halfStepX/2, halfStepY + offsetY), k==0 || (j==qy-1 && offsetY==0)); sides[k, j, 0] = leftUp; Segment top = new Segment(center.Offset(-halfStepX/2, halfStepY + offsetY), center.Offset(halfStepX/2, halfStepY + offsetY), j==qy-1); sides[k, j, 1] = top; Segment rightUp = new Segment(center.Offset(halfStepX/2, halfStepY + offsetY), center.Offset(halfStepX, offsetY), k==qx2-1 || (j==qy-1 && offsetY==0)); sides[k, j, 2] = rightUp; Segment rightDown = (j > 0 && k<qx2-1 && offsetY<0) ? sides[k+1,j-1,0]: new Segment(center.Offset(halfStepX, offsetY), center.Offset(halfStepX/2, -halfStepY + offsetY), (j==0 && offsetY<0)|| k==qx2-1); sides[k, j, 3] = rightDown; Segment bottom = j>0 ? sides[k, j-1, 1] : new Segment(center.Offset(halfStepX/2, -halfStepY + offsetY), center.Offset(-halfStepX/2, -halfStepY +offsetY), true); sides[k, j, 4] = bottom; Segment leftDown; if (offsetY<0 && j>0) { leftDown = sides[k-1, j-1, 2]; } else if (offsetY==0 && k>0) { leftDown = sides[k-1, j, 2]; } else { leftDown = new Segment(center.Offset(-halfStepX/2, -halfStepY+offsetY), center.Offset(-halfStepX, offsetY), true); } sides[k, j, 5] = leftDown; if (j==0) { // leftDown.CropBottom(); // bottom.CropBottom(); // rightDown.CropBottom(); } if (k==qx2-1) { top.CropRight(); rightUp.CropRight(); rightDown.CropRight(); bottom.CropRight(); } Region cr = new Region (cell); if (subdivisions>1) { if (!top.deleted) cr.segments.AddRange (top.Subdivide(subdivisions, _gridCurvature)); if (!rightUp.deleted) cr.segments.AddRange (rightUp.Subdivide(subdivisions, _gridCurvature)); if (!rightDown.deleted) cr.segments.AddRange (rightDown.Subdivide(subdivisions, _gridCurvature)); if (!bottom.deleted) cr.segments.AddRange (bottom.Subdivide(subdivisions, _gridCurvature)); if (!leftDown.deleted) cr.segments.AddRange (leftDown.Subdivide(subdivisions, _gridCurvature)); if (!leftUp.deleted) cr.segments.AddRange (leftUp.Subdivide(subdivisions, _gridCurvature)); } else { if (!top.deleted) cr.segments.Add (top); if (!rightUp.deleted) cr.segments.Add (rightUp); if (!rightDown.deleted) cr.segments.Add (rightDown); if (!bottom.deleted) cr.segments.Add (bottom); if (!leftDown.deleted) cr.segments.Add (leftDown); if (!leftUp.deleted) cr.segments.Add (leftUp); } Connector connector = new Connector(); connector.AddRange(cr.segments); cr.polygon = connector.ToPolygon(); // FromLargestLineStrip(); if (cr.polygon!=null) { cell.region = cr; cells.Add (cell); } } } }
void SetupBoxGrid(bool strictQuads) { // Make cell regions: we assume cells have only 1 region but that can change in the future int l = _numCells; int qx, qy; int q = (int)(Math.Sqrt (l)); if (strictQuads) { qx=qy=q; } else { qx=l; qy=1; if (q<1) q=1; if ( (int)(q*q) != l) { // not squared if (!GetTwoFactors(l, out qx, out qy)) { // if number > 10 and it's prime, reduce by one so we can avoid ugly accordian grids if (l>10) GetTwoFactors(l-1, out qx, out qy); } } else { qx = qy = q; } } double stepX = (transform.localScale.y / transform.localScale.x) / qx; double stepY = (transform.localScale.x / transform.localScale.y) / qy; double halfStepX = stepX*0.5; double halfStepY = stepY*0.5; Segment [,,] sides = new Segment[qx,qy,4]; // 0 = left, 1 = top, 2 = right, 3 = bottom int c = -1; int subdivisions = goodGridCurvature > 0 ? 3: 1; for (int k=0;k<qx;k++) { for (int j=0;j<qy;j++) { Point center = new Point((double)k/qx-0.5+halfStepX,(double)j/qy-0.5+halfStepY); Cell cell = new Cell( (++c).ToString(), new Vector2((float)center.x, (float)center.y)); Segment left = k>0 ? sides[k-1, j, 2] : new Segment(center.Offset(-halfStepX, -halfStepY), center.Offset(-halfStepX, halfStepY), true); sides[k, j, 0] = left; Segment top = new Segment(center.Offset(-halfStepX, halfStepY), center.Offset(halfStepX, halfStepY), j==qy-1); sides[k, j, 1] = top; Segment right = new Segment(center.Offset(halfStepX, halfStepY), center.Offset(halfStepX, -halfStepY), k==qx-1); sides[k, j, 2] = right; Segment bottom = j>0 ? sides[k, j-1, 1] : new Segment(center.Offset(halfStepX, -halfStepY), center.Offset(-halfStepX, -halfStepY), true); sides[k, j, 3] = bottom; Region cr = new Region (cell); if (subdivisions>1) { cr.segments.AddRange (top.Subdivide(subdivisions, _gridCurvature)); cr.segments.AddRange (right.Subdivide(subdivisions, _gridCurvature)); cr.segments.AddRange (bottom.Subdivide(subdivisions, _gridCurvature)); cr.segments.AddRange (left.Subdivide(subdivisions, _gridCurvature)); } else { cr.segments.Add (top); cr.segments.Add (right); cr.segments.Add (bottom); cr.segments.Add (left); } Connector connector = new Connector(); connector.AddRange(cr.segments); cr.polygon = connector.ToPolygon(); // FromLargestLineStrip(); if (cr.polygon!=null) { cell.region = cr; cells.Add (cell); } } } }
GameObject GenerateTerritoryRegionSurface(int territoryIndex, Material material, Vector2 textureScale, Vector2 textureOffset, float textureRotation) { if (territoryIndex<0 || territoryIndex>=territories.Count) return null; Region region = territories [territoryIndex].region; // Calculate region's surface points int numSegments = region.segments.Count; Connector connector = new Connector(); if (_terrain==null) { connector.AddRange(region.segments); } else { for (int i = 0; i<numSegments; i++) { Segment s = region.segments[i]; SurfaceSegmentForSurface(s, connector); } } Geom.Polygon surfacedPolygon = connector.ToPolygonFromLargestLineStrip(); List<Point> surfacedPoints = surfacedPolygon.contours[0].points; List<PolygonPoint> ppoints = new List<PolygonPoint>(surfacedPoints.Count); for (int k=0;k<surfacedPoints.Count;k++) { double x = surfacedPoints[k].x+2; double y = surfacedPoints[k].y+2; if (!IsTooNearPolygon(x, y, ppoints)) { float h = _terrain!=null ? _terrain.SampleHeight(transform.TransformPoint((float)x-2, (float)y-2,0)): 0; ppoints.Add (new PolygonPoint(x, y, h)); } } Poly2Tri.Polygon poly = new Poly2Tri.Polygon(ppoints); if (_terrain!=null) { if (steinerPoints==null) { steinerPoints = new List<TriangulationPoint>(6000); } else { steinerPoints.Clear(); } float stepX = 1.0f / heightMapWidth; float smallStep = 1.0f / heightMapWidth; float y = region.rect2D.yMin + smallStep; float ymax = region.rect2D.yMax - smallStep; float[] acumY = new float[terrainRoughnessMapWidth]; while(y<ymax) { int j = (int)((y + 0.5f) * terrainRoughnessMapHeight); // * heightMapHeight)) / TERRAIN_CHUNK_SIZE; if (j>=terrainRoughnessMapHeight) j=terrainRoughnessMapHeight-1; float sy = y + 2; float xin = GetFirstPointInRow(sy, ppoints) + smallStep; float xout = GetLastPointInRow(sy, ppoints) - smallStep; int k0 = -1; for (float x = xin; x<xout; x+=stepX) { int k = (int)((x + 0.5f) * terrainRoughnessMapWidth); //)) / TERRAIN_CHUNK_SIZE; if (k>=terrainRoughnessMapWidth) k=terrainRoughnessMapWidth-1; if (k0!=k) { k0=k; stepX = terrainRoughnessMap[j,k]; if (acumY[k] >= stepX) acumY[k] = 0; acumY[k] += smallStep; } if (acumY[k] >= stepX) { // Gather precision height float h = _terrain.SampleHeight (transform.TransformPoint(x,y,0)); float htl = _terrain.SampleHeight (transform.TransformPoint (x-smallStep, y+smallStep, 0)); if (htl>h) h = htl; float htr = _terrain.SampleHeight (transform.TransformPoint (x+smallStep, y+smallStep, 0)); if (htr>h) h = htr; float hbr = _terrain.SampleHeight (transform.TransformPoint (x+smallStep, y-smallStep, 0)); if (hbr>h) h = hbr; float hbl = _terrain.SampleHeight (transform.TransformPoint (x-smallStep, y-smallStep, 0)); if (hbl>h) h = hbl; steinerPoints.Add (new PolygonPoint (x+2, sy, h)); } } y += smallStep; if (steinerPoints.Count>80000) { break; } } poly.AddSteinerPoints(steinerPoints); } P2T.Triangulate(poly); Vector3[] revisedSurfPoints = new Vector3[poly.Triangles.Count*3]; if (_gridNormalOffset>0) { for (int k=0;k<poly.Triangles.Count;k++) { DelaunayTriangle dt = poly.Triangles[k]; float x = dt.Points[0].Xf-2; float y = dt.Points[0].Yf-2; float z = -dt.Points[0].Zf; Vector3 nd = transform.InverseTransformVector(_terrain.terrainData.GetInterpolatedNormal(x+0.5f,y+0.5f)) * _gridNormalOffset; revisedSurfPoints[k*3].x = x + nd.x; revisedSurfPoints[k*3].y = y + nd.y; revisedSurfPoints[k*3].z = z + nd.z; x = dt.Points[2].Xf-2; y = dt.Points[2].Yf-2; z = -dt.Points[2].Zf; nd = transform.InverseTransformVector(_terrain.terrainData.GetInterpolatedNormal(x+0.5f,y+0.5f)) * _gridNormalOffset; revisedSurfPoints[k*3+1].x = x + nd.x; revisedSurfPoints[k*3+1].y = y + nd.y; revisedSurfPoints[k*3+1].z = z + nd.z; x = dt.Points[1].Xf-2; y = dt.Points[1].Yf-2; z = -dt.Points[1].Zf; nd = transform.InverseTransformVector(_terrain.terrainData.GetInterpolatedNormal(x+0.5f,y+0.5f)) * _gridNormalOffset; revisedSurfPoints[k*3+2].x = x + nd.x; revisedSurfPoints[k*3+2].y = y + nd.y; revisedSurfPoints[k*3+2].z = z + nd.z; } } else { for (int k=0;k<poly.Triangles.Count;k++) { DelaunayTriangle dt = poly.Triangles[k]; revisedSurfPoints[k*3].x = dt.Points[0].Xf-2; revisedSurfPoints[k*3].y = dt.Points[0].Yf-2; revisedSurfPoints[k*3].z = -dt.Points[0].Zf; revisedSurfPoints[k*3+1].x = dt.Points[2].Xf-2; revisedSurfPoints[k*3+1].y = dt.Points[2].Yf-2; revisedSurfPoints[k*3+1].z = -dt.Points[2].Zf; revisedSurfPoints[k*3+2].x = dt.Points[1].Xf-2; revisedSurfPoints[k*3+2].y = dt.Points[1].Yf-2; revisedSurfPoints[k*3+2].z = -dt.Points[1].Zf; } } int cacheIndex = GetCacheIndexForTerritoryRegion (territoryIndex); string cacheIndexSTR = cacheIndex.ToString(); // Deletes potential residual surface Transform t = surfacesLayer.transform.FindChild(cacheIndexSTR); if (t!=null) DestroyImmediate(t.gameObject); GameObject surf = Drawing.CreateSurface (cacheIndexSTR, revisedSurfPoints, material); surf.transform.SetParent (surfacesLayer.transform, false); surf.transform.localPosition = Vector3.zero; surf.layer = gameObject.layer; if (surfaces.ContainsKey(cacheIndex)) surfaces.Remove(cacheIndex); surfaces.Add (cacheIndex, surf); return surf; }
Polygon ComputeInternal(PolygonOp operation) { Polygon result = null; sortedEvents = new List<SweepEvent>(); // Init event queue eventQueue = new EventQueue(); // Test 1 for trivial result case if (subject.contours.Count * clipping.contours.Count == 0) { if (operation == PolygonOp.DIFFERENCE) result = subject; else if (operation == PolygonOp.UNION || operation == PolygonOp.XOR) result = (subject.contours.Count == 0) ? clipping : subject; return result; } // Test 2 for trivial result case Rectangle subjectBB = subject.boundingBox; Rectangle clippingBB = clipping.boundingBox; if (!subjectBB.Intersects(clippingBB)) { if (operation == PolygonOp.DIFFERENCE) result = subject; if (operation == PolygonOp.UNION || operation == PolygonOp.XOR) { result = subject; foreach(Contour c in clipping.contours) result.AddContour(c); } return result; } // Add each segment to the eventQueue, sorted from left to right. for(int k=0;k<subject.contours.Count;k++) { Contour sCont = subject.contours[k]; for (int pParse1=0;pParse1<sCont.points.Count;pParse1++) ProcessSegment(sCont.GetSegment(pParse1), PolygonType.SUBJECT); } for(int k=0;k<clipping.contours.Count;k++) { Contour cCont = clipping.contours[k]; for (int pParse2=0;pParse2<cCont.points.Count;pParse2++) ProcessSegment(cCont.GetSegment(pParse2), PolygonType.CLIPPING); } Connector connector = new Connector(); // This is the SweepLine. That is, we go through all the polygon edges // by sweeping from left to right. SweepEventSet S = new SweepEventSet(); double MINMAX_X = Math.Min(subjectBB.right, clippingBB.right) + Point.PRECISION; SweepEvent prev, next; int panicCounter = 0; // This is a safety check to prevent infinite loops (very rare but could happen due to floating-point issues with a high number of points) while (!eventQueue.isEmpty) { if (panicCounter++>10000) { Debug.Log("PANIC!"); break; } prev = null; next = null; SweepEvent e = eventQueue.Dequeue(); if ((operation == PolygonOp.INTERSECTION && e.p.x > MINMAX_X) || (operation == PolygonOp.DIFFERENCE && e.p.x > subjectBB.right + Point.PRECISION)) return connector.ToPolygonFromLargestLineStrip(); if (operation == PolygonOp.UNION && e.p.x > MINMAX_X) { // add all the non-processed line segments to the result if (!e.isLeft) connector.Add(e.segment); while (!eventQueue.isEmpty) { e = eventQueue.Dequeue(); if (!e.isLeft) connector.Add(e.segment); } return connector.ToPolygonFromLargestLineStrip(); } if (e.isLeft) { // the line segment must be inserted into S int pos = S.Insert(e); prev = (pos > 0) ? S.eventSet[pos - 1] : null; next = (pos < S.eventSet.Count - 1) ? S.eventSet[pos + 1] : null; if (prev == null) { e.inside = e.inOut = false; } else if (prev.edgeType != EdgeType.NORMAL) { if (pos - 2 < 0) { // e overlaps with prev // Not sure how to handle the case when pos - 2 < 0, but judging // from the C++ implementation this looks like how it should be handled. e.inside = e.inOut = false; if (prev.polygonType != e.polygonType) e.inside = true; else e.inOut = true; } else { SweepEvent prevTwo = S.eventSet[pos - 2]; if (prev.polygonType == e.polygonType) { e.inOut = !prev.inOut; e.inside = !prevTwo.inOut; } else { e.inOut = !prevTwo.inOut; e.inside = !prev.inOut; } } } else if (e.polygonType == prev.polygonType) { e.inside = prev.inside; e.inOut = !prev.inOut; } else { e.inside = !prev.inOut; e.inOut = prev.inside; } // Process a possible intersection between "e" and its next neighbor in S if (next != null) PossibleIntersection(e, next); // Process a possible intersection between "e" and its previous neighbor in S if (prev != null) PossibleIntersection(prev, e); } else { // the line segment must be removed from S // Get the next and previous line segments to "e" in S int otherPos = -1; for (int evt=0;evt<S.eventSet.Count;evt++) { if (e.otherSE.Equals(S.eventSet[evt])) { otherPos = evt; break; } } if (otherPos != -1) { prev = (otherPos > 0) ? S.eventSet[otherPos - 1] : null; next = (otherPos < S.eventSet.Count - 1) ? S.eventSet[otherPos + 1] : null; } switch (e.edgeType) { case EdgeType.NORMAL: switch (operation) { case PolygonOp.INTERSECTION: if (e.otherSE.inside) connector.Add(e.segment); break; case PolygonOp.UNION: if (!e.otherSE.inside) connector.Add(e.segment); break; case PolygonOp.DIFFERENCE: if ((e.polygonType == PolygonType.SUBJECT && !e.otherSE.inside) || (e.polygonType == PolygonType.CLIPPING && e.otherSE.inside)) connector.Add(e.segment); break; case PolygonOp.XOR: connector.Add (e.segment); break; } break; case EdgeType.SAME_TRANSITION: if (operation == PolygonOp.INTERSECTION || operation == PolygonOp.UNION) connector.Add(e.segment); break; case EdgeType.DIFFERENT_TRANSITION: if (operation == PolygonOp.DIFFERENCE) connector.Add(e.segment); break; } if (otherPos != -1) S.Remove(S.eventSet[otherPos]); if (next != null && prev != null) PossibleIntersection(prev, next); } } return connector.ToPolygonFromLargestLineStrip(); }
Polygon ComputeInternal(PolygonOp operation) { Polygon result = null; sortedEvents = new List <SweepEvent>(); // Init event queue eventQueue = new EventQueue(); // Test 1 for trivial result case if (subject.contours.Count * clipping.contours.Count == 0) { if (operation == PolygonOp.DIFFERENCE) { result = subject; } else if (operation == PolygonOp.UNION || operation == PolygonOp.XOR) { result = (subject.contours.Count == 0) ? clipping : subject; } return(result); } // Test 2 for trivial result case Rectangle subjectBB = subject.boundingBox; Rectangle clippingBB = clipping.boundingBox; if (!subjectBB.Intersects(clippingBB)) { if (operation == PolygonOp.DIFFERENCE) { result = subject; } if (operation == PolygonOp.UNION || operation == PolygonOp.XOR) { result = subject; foreach (Contour c in clipping.contours) { result.AddContour(c); } } return(result); } // Add each segment to the eventQueue, sorted from left to right. for (int k = 0; k < subject.contours.Count; k++) { Contour sCont = subject.contours[k]; for (int pParse1 = 0; pParse1 < sCont.points.Count; pParse1++) { ProcessSegment(sCont.GetSegment(pParse1), PolygonType.SUBJECT); } } for (int k = 0; k < clipping.contours.Count; k++) { Contour cCont = clipping.contours[k]; for (int pParse2 = 0; pParse2 < cCont.points.Count; pParse2++) { ProcessSegment(cCont.GetSegment(pParse2), PolygonType.CLIPPING); } } Connector connector = new Connector(); // This is the SweepLine. That is, we go through all the polygon edges // by sweeping from left to right. SweepEventSet S = new SweepEventSet(); double MINMAX_X = Math.Min(subjectBB.right, clippingBB.right) + Point.PRECISION; SweepEvent prev, next; int panicCounter = 0; // This is a safety check to prevent infinite loops (very rare but could happen due to floating-point issues with a high number of points) while (!eventQueue.isEmpty) { if (panicCounter++ > 10000) { Debug.Log("PANIC!"); break; } prev = null; next = null; SweepEvent e = eventQueue.Dequeue(); if ((operation == PolygonOp.INTERSECTION && e.p.x > MINMAX_X) || (operation == PolygonOp.DIFFERENCE && e.p.x > subjectBB.right + Point.PRECISION)) { return(connector.ToPolygonFromLargestLineStrip()); } if (operation == PolygonOp.UNION && e.p.x > MINMAX_X) { // add all the non-processed line segments to the result if (!e.isLeft) { connector.Add(e.segment); } while (!eventQueue.isEmpty) { e = eventQueue.Dequeue(); if (!e.isLeft) { connector.Add(e.segment); } } return(connector.ToPolygonFromLargestLineStrip()); } if (e.isLeft) // the line segment must be inserted into S { int pos = S.Insert(e); prev = (pos > 0) ? S.eventSet[pos - 1] : null; next = (pos < S.eventSet.Count - 1) ? S.eventSet[pos + 1] : null; if (prev == null) { e.inside = e.inOut = false; } else if (prev.edgeType != EdgeType.NORMAL) { if (pos - 2 < 0) // e overlaps with prev // Not sure how to handle the case when pos - 2 < 0, but judging // from the C++ implementation this looks like how it should be handled. { e.inside = e.inOut = false; if (prev.polygonType != e.polygonType) { e.inside = true; } else { e.inOut = true; } } else { SweepEvent prevTwo = S.eventSet[pos - 2]; if (prev.polygonType == e.polygonType) { e.inOut = !prev.inOut; e.inside = !prevTwo.inOut; } else { e.inOut = !prevTwo.inOut; e.inside = !prev.inOut; } } } else if (e.polygonType == prev.polygonType) { e.inside = prev.inside; e.inOut = !prev.inOut; } else { e.inside = !prev.inOut; e.inOut = prev.inside; } // Process a possible intersection between "e" and its next neighbor in S if (next != null) { PossibleIntersection(e, next); } // Process a possible intersection between "e" and its previous neighbor in S if (prev != null) { PossibleIntersection(prev, e); } } else // the line segment must be removed from S // Get the next and previous line segments to "e" in S { int otherPos = -1; for (int evt = 0; evt < S.eventSet.Count; evt++) { if (e.otherSE.Equals(S.eventSet[evt])) { otherPos = evt; break; } } if (otherPos != -1) { prev = (otherPos > 0) ? S.eventSet[otherPos - 1] : null; next = (otherPos < S.eventSet.Count - 1) ? S.eventSet[otherPos + 1] : null; } switch (e.edgeType) { case EdgeType.NORMAL: switch (operation) { case PolygonOp.INTERSECTION: if (e.otherSE.inside) { connector.Add(e.segment); } break; case PolygonOp.UNION: if (!e.otherSE.inside) { connector.Add(e.segment); } break; case PolygonOp.DIFFERENCE: if ((e.polygonType == PolygonType.SUBJECT && !e.otherSE.inside) || (e.polygonType == PolygonType.CLIPPING && e.otherSE.inside)) { connector.Add(e.segment); } break; case PolygonOp.XOR: connector.Add(e.segment); break; } break; case EdgeType.SAME_TRANSITION: if (operation == PolygonOp.INTERSECTION || operation == PolygonOp.UNION) { connector.Add(e.segment); } break; case EdgeType.DIFFERENT_TRANSITION: if (operation == PolygonOp.DIFFERENCE) { connector.Add(e.segment); } break; } if (otherPos != -1) { S.Remove(S.eventSet[otherPos]); } if (next != null && prev != null) { PossibleIntersection(prev, next); } } } return(connector.ToPolygonFromLargestLineStrip()); }