/// <summary> /// Returns the buffer edges that lie between the corresponding source edges. /// </summary> /// <param name="coordinates">The list of coordinates.</param> /// <param name="sourceIndex">Index of the source coordinate.</param> /// <returns>The list of edges.</returns> private List <Tuple <Coordinate, Coordinate> > GetConvolutionEdges(IReadOnlyList <Coordinate> coordinates, Int32 sourceIndex) { List <Tuple <Coordinate, Coordinate> > edges = new List <Tuple <Coordinate, Coordinate> >(); Double previousElementX = sourceIndex != 0 ? coordinates[sourceIndex - 1].X : coordinates[coordinates.Count - 2].X; Double previousElementY = sourceIndex != 0 ? coordinates[sourceIndex - 1].Y : coordinates[coordinates.Count - 2].Y; Double firstAngle = Math.Atan2(coordinates[sourceIndex].Y - previousElementY, coordinates[sourceIndex].X - previousElementX); Double secondAngle = Math.Atan2(coordinates[sourceIndex + 1].Y - coordinates[sourceIndex].Y, coordinates[sourceIndex + 1].X - coordinates[sourceIndex].X); if (firstAngle < 0 && secondAngle == 0) { secondAngle = 2 * Math.PI; } if (firstAngle < 0) { firstAngle = 2 * Math.PI + firstAngle; } if (secondAngle < 0) { secondAngle = 2 * Math.PI + secondAngle; } for (Int32 bufferIndex = 0; bufferIndex < this.bufferCoordinates.Count - 1; bufferIndex++) { Double middleAngle = Math.Atan2(this.bufferCoordinates[bufferIndex + 1].Y - this.bufferCoordinates[bufferIndex].Y, this.bufferCoordinates[bufferIndex + 1].X - this.bufferCoordinates[bufferIndex].X); if (middleAngle < 0) { middleAngle = 2 * Math.PI + middleAngle; } Boolean addCondition = false; if (PolygonAlgorithms.Orientation(coordinates, this.PrecisionModel) == Orientation.Clockwise) { if (firstAngle >= secondAngle) { addCondition = (middleAngle <= firstAngle) && (middleAngle >= secondAngle); } else { addCondition = middleAngle <= firstAngle || middleAngle >= secondAngle; } } else { if (firstAngle <= secondAngle) { addCondition = middleAngle >= firstAngle && middleAngle <= secondAngle; } else { addCondition = (middleAngle > firstAngle) || (middleAngle < secondAngle); } } if (addCondition) { edges.Add( Tuple.Create( this.PrecisionModel.MakePrecise(new Coordinate(coordinates[sourceIndex].X + this.bufferCoordinates[bufferIndex].X, coordinates[sourceIndex].Y + this.bufferCoordinates[bufferIndex].Y)), this.PrecisionModel.MakePrecise(new Coordinate(coordinates[sourceIndex].X + this.bufferCoordinates[bufferIndex + 1].X, coordinates[sourceIndex].Y + this.bufferCoordinates[bufferIndex + 1].Y)))); } } return(edges); }
/// <summary> /// Creates the result polygon with separated shell and hole coordinates. /// </summary> /// <param name="coordinates">The list of coordinates.</param> /// <param name="isHole">A value indicating whether the coordinate list is a hole.</param> private void ComputeResultPolygon(IReadOnlyList <Coordinate> coordinates, Boolean isHole) { BentleyOttmannAlgorithm algorithm = new BentleyOttmannAlgorithm(coordinates, this.PrecisionModel); List <Coordinate> intersections = new List <Coordinate>(algorithm.Intersections); List <Tuple <Int32, Int32> > edgeIndexes = new List <Tuple <Int32, Int32> >(algorithm.EdgeIndexes); // add edge intersection coordinates to the list of coordinates in counterclockwise order List <Coordinate> coordinatesWithIntersections = new List <Coordinate>(); coordinatesWithIntersections = this.GetCoordinatesWithIntersections(coordinates, intersections, edgeIndexes); // remove unnecessary internal coordinates and create holes List <List <Coordinate> > internalPolygons = new List <List <Coordinate> >(); List <List <Coordinate> > holes = new List <List <Coordinate> >(); List <Int32> nonShellIndexes = new List <Int32>(); Int32 internalPolygonIndex = -1; for (Int32 coordinateIndex = 0; coordinateIndex < coordinatesWithIntersections.Count; coordinateIndex++) { if (internalPolygonIndex != -1) { internalPolygons[internalPolygonIndex].Add(coordinatesWithIntersections[coordinateIndex]); nonShellIndexes.Add(coordinateIndex); } if (intersections.Any(x => x.Equals(coordinatesWithIntersections[coordinateIndex]))) { Int32 matchingPolygonIndex = internalPolygons.FindIndex(x => x[0].Equals(coordinatesWithIntersections[coordinateIndex])); if (internalPolygonIndex != -1 && internalPolygonIndex < internalPolygons.Count && matchingPolygonIndex != -1) { if (PolygonAlgorithms.Orientation(internalPolygons[matchingPolygonIndex], this.PrecisionModel) == Orientation.Clockwise) { holes.Add(internalPolygons[matchingPolygonIndex]); } for (Int32 polygonIndex = internalPolygons.Count - 1; polygonIndex >= matchingPolygonIndex; polygonIndex--) { internalPolygons.RemoveAt(polygonIndex); } internalPolygonIndex = matchingPolygonIndex - 1; } else { internalPolygonIndex++; internalPolygons.Add(new List <Coordinate>() { coordinatesWithIntersections[coordinateIndex] }); } } } for (Int32 index = 0; index < nonShellIndexes.Count; index++) { coordinatesWithIntersections.RemoveAt(nonShellIndexes[nonShellIndexes.Count - 1 - index]); } // add shell and hole coordinates to the result if (isHole) { this.resultHoleCoordinates.Add(new List <Coordinate>(coordinatesWithIntersections)); } else { this.resultShellCoordinates = coordinatesWithIntersections; foreach (List <Coordinate> hole in holes) { this.resultHoleCoordinates.Add(hole); } } }