private static void AddReshapeConnect(IPath connectionLineAtCutOff, IPath pathOnTarget, IDictionary <AdjustedCutSubcurve, IGeometry> toConnectPaths, IGeometry geometryToReshape) { bool connectsAtFromPoint = GeometryUtils.AreEqualInXY(connectionLineAtCutOff.ToPoint, pathOnTarget.FromPoint); IPath connectLineAtFrom = connectsAtFromPoint ? connectionLineAtCutOff : null; IPath connectLineAtTo = connectsAtFromPoint ? null : connectionLineAtCutOff; var adjustedCutSubcurve = new AdjustedCutSubcurve( pathOnTarget, connectLineAtFrom, connectLineAtTo); //if (GeometryUtils.AreEqualInXY(connectionLineAtCutOff.FromPoint, // pathOnTarget.FromPoint)) //{ // connectionLineAtCutOff.ReverseOrientation(); //} //ISegmentCollection pathOnTargetClone = // (ISegmentCollection) GeometryFactory.Clone(pathOnTarget); //((ISegmentCollection) connectionLineAtCutOff).AddSegmentCollection( // pathOnTargetClone); toConnectPaths.Add(adjustedCutSubcurve, geometryToReshape); }
private bool ApplyReshapePath( [NotNull] IGeometry geometryToReshape, [NotNull] AdjustedCutSubcurve adjustCurve, NotificationCollection notifications, IDictionary <IGeometry, NotificationCollection> reshapedGeometries) { // if the connect line crosses the geometry's boundary only reshape to the first intersection // and subsequently try the remaining bits. Otherwise this could result in a flipped geometry (reshape to the wrong side) IPath remainderAtFrom, remainderAtTo; IPath cutConnectLineAtFrom = SplitConnectline(adjustCurve, geometryToReshape, true, out remainderAtFrom); IPath cutConnectLineAtTo = SplitConnectline(adjustCurve, geometryToReshape, false, out remainderAtTo); var mainSubcurve = new AdjustedCutSubcurve( adjustCurve.PathOnTarget, cutConnectLineAtFrom, cutConnectLineAtTo); IPath reshapePath = mainSubcurve.Path; bool reshaped = Reshape(geometryToReshape, reshapePath); reshaped |= remainderAtFrom != null && Reshape(geometryToReshape, remainderAtFrom); reshaped |= remainderAtTo != null && Reshape(geometryToReshape, remainderAtTo); if (reshaped) { // to avoid incorrect relational operator results in for the next path on target ((ISegmentCollection)geometryToReshape).SegmentsChanged(); } NotificationCollection reshapeNotifications = null; // TODO: get from out ReshapeInfo; // move adding notfications to caller? if (reshaped && !reshapedGeometries.ContainsKey(geometryToReshape)) { reshapedGeometries.Add(geometryToReshape, reshapeNotifications); } else { if (reshapeNotifications != null) { NotificationUtils.Add(notifications, reshapeNotifications.Concatenate(" ")); } } return(reshaped); }
public static AdjustedCutSubcurve CalculateAdjustedPath( [NotNull] IPath adjustLine, [NotNull] ICurve sourcePart, [NotNull] IConnectLineCalculator connectCalculator) { Stopwatch watch = _msg.DebugStartTiming(); AdjustedCutSubcurve adjustedSubcurve = null; IPath startFallback; IPath endFallback; IPath startSourceConnection = connectCalculator.FindConnection( sourcePart, adjustLine, true, out startFallback); IPath endSourceConnection = connectCalculator.FindConnection( sourcePart, adjustLine, false, out endFallback); if (_msg.IsVerboseDebugEnabled) { _msg.DebugFormat("CalculatedAdjustedPath start connection: {0}", GeometryUtils.ToString(startSourceConnection)); _msg.DebugFormat("CalculateAdjustedPath: end connection: {0}", GeometryUtils.ToString(endSourceConnection)); } // Possible criteria to consider the path valid: // - length of connections vs. length of path // - angle between path and connections // - size of the reshape-area (difference before/after reshape) vs. length of path (sliver condition?) ValidateConnectLines(sourcePart, ref startSourceConnection, ref endSourceConnection, startFallback, endFallback); if (startSourceConnection != null && endSourceConnection != null) { adjustedSubcurve = CreateAdjustedCutSubcurve(adjustLine, startSourceConnection, endSourceConnection); } _msg.DebugStopTiming(watch, "Calculated adjusted subcurve including connection lines to target"); return(adjustedSubcurve); }
private static IPath SplitConnectline(AdjustedCutSubcurve adjustCurve, IGeometry geometryToReshape, bool atFrom, out IPath remainder) { IPath connectLine = atFrom ? adjustCurve.ConnectLineAtFromPoint : adjustCurve.ConnectLineAtToPoint; remainder = null; if (connectLine == null) { return(null); } if (((IPointCollection)connectLine).PointCount > 2) { // it's along a real geometry return(connectLine); } var highLevelConnectline = (IPolyline)GeometryUtils.GetHighLevelGeometry(connectLine); IPointCollection crossingPoints = GetCrossingPoints(highLevelConnectline, geometryToReshape); if (crossingPoints.PointCount == 0) { return(connectLine); } IPoint connectPoint = atFrom ? adjustCurve.PathOnTarget.FromPoint : adjustCurve.PathOnTarget.ToPoint; IPoint nearestPoint = ((IProximityOperator)crossingPoints).ReturnNearestPoint( connectPoint, esriSegmentExtension.esriNoExtension); bool splitHappened; int newPartIdx; int newSegmentIdx; highLevelConnectline.SplitAtPoint(nearestPoint, false, true, out splitHappened, out newPartIdx, out newSegmentIdx); var connectLineParts = ((IGeometryCollection)highLevelConnectline); IPath result; if (GeometryUtils.AreEqualInXY(highLevelConnectline.FromPoint, connectPoint)) { result = (IPath)connectLineParts.get_Geometry(0); remainder = (IPath)connectLineParts.get_Geometry(1); } else { result = (IPath)connectLineParts.get_Geometry(1); remainder = (IPath)connectLineParts.get_Geometry(0); } return(result); }
/// <summary> /// o /// ___\_________ /// | \ | | /// | \| | /// | | | /// | | | /// | | | /// | | | /// |______|___ __| /// /// Reshape line: o /// \ /// \ /// Applies the relevant reshapes of the provided adjust curves at the outer boundary of two adjacent polygons /// by connecting the last vertex before the cut-off (in this case the top left corner of the left polygon) /// with the target intersection point. /// </summary> /// <param name="connectLinesAtCutOffs"></param> /// <param name="reshapedGeometries"></param> /// <param name="notifications"></param> private void ApplySharedBoundaryOuterEndReshape( Dictionary <AdjustedCutSubcurve, IGeometry> connectLinesAtCutOffs, IDictionary <IGeometry, NotificationCollection> reshapedGeometries, NotificationCollection notifications) { IList <IGeometry> allGeometriesToReshape = _reshapeGeometryCloneByOriginal.Values.ToList(); foreach ( KeyValuePair <AdjustedCutSubcurve, IGeometry> connectLinesAtCutOff in connectLinesAtCutOffs) { AdjustedCutSubcurve adjustedCurve = connectLinesAtCutOff.Key; IGeometry geometryToReshape = connectLinesAtCutOff.Value; IPath connectLine = adjustedCurve.ConnectLineAtFromPoint ?? adjustedCurve.ConnectLineAtToPoint; IGeometry highLevelPathOnTarget = GeometryUtils.GetHighLevelGeometry(adjustedCurve.PathOnTarget, true); IPoint targetPointOnSketch = GeometryUtils.Touches(connectLine.FromPoint, highLevelPathOnTarget) ? connectLine.FromPoint : connectLine.ToPoint; Marshal.ReleaseComObject(highLevelPathOnTarget); // it must connect to an actual target intersection point: if (!GeometryUtils.Intersects( targetPointOnSketch, (IGeometry)_stickyIntersections.GetTargetPointCollection())) { continue; } if (PointIntersectsBoundary(targetPointOnSketch, geometryToReshape)) { // no need to connect, target point already on polygon boundary (it could only destroy a nicely reshaped geometry) continue; } var highLevelAdjustLine = (IPolyline)GeometryUtils.GetHighLevelGeometry(adjustedCurve.Path); var boundaryCutBackWithTargetPointOutsideCount = 0; foreach (IGeometry geometry in allGeometriesToReshape) { bool interiorIntersects = GeometryUtils.InteriorIntersects(geometry, highLevelAdjustLine); bool boundaryConnectWithTargetOutside = geometry == geometryToReshape && !interiorIntersects && !GeometryUtils.Intersects(targetPointOnSketch, geometry) && BothEndsTouchGeometry(highLevelAdjustLine, (IPolygon)geometry); if (boundaryConnectWithTargetOutside) { boundaryCutBackWithTargetPointOutsideCount++; } } // To connect to a target intersection point at the outer boundary of a set of polygons it must be // if (boundaryCutBackWithTargetPointOutsideCount == 1 && !IsWithinAnyOriginalPolygon(targetPointOnSketch)) { ApplyReshapePath(geometryToReshape, adjustedCurve.Path, notifications, null, reshapedGeometries); } } }
/// <summary> /// _____________ /// | | /// | | /// |_____________| /// | \ | / | /// | \ | / | /// | o-|- | /// | | | /// | | | /// | | | /// |______|___ __| /// /// Reshape line: /// \ / /// \ / /// o--- /// Applies the relevant reshapes of the provided adjust curves at a cut-back of a shared boundary in a border /// triangle situation by connecting the last vertex before the cut-off (in this case the lower end of the /// shared boundary between the two lower polygons) with the target intersection point. /// </summary> /// <param name="connectLinesAtCutOffs"></param> /// <param name="sourceTargetPairs"></param> /// <param name="reshapedGeometries"></param> /// <param name="notifications"></param> private void ApplySharedBoundaryCutBackConnectAtTriangleIntersections( Dictionary <AdjustedCutSubcurve, IGeometry> connectLinesAtCutOffs, IList <KeyValuePair <IPoint, IPoint> > sourceTargetPairs, IDictionary <IGeometry, NotificationCollection> reshapedGeometries, NotificationCollection notifications) { foreach ( KeyValuePair <AdjustedCutSubcurve, AdjustedCutSubcurve> keyValuePair in CollectionUtils.GetAllTuples(connectLinesAtCutOffs.Keys)) { AdjustedCutSubcurve adjustCurve1 = keyValuePair.Key; AdjustedCutSubcurve adjustCurve2 = keyValuePair.Value; IGeometry geometry1 = connectLinesAtCutOffs[adjustCurve1]; IGeometry geometry2 = connectLinesAtCutOffs[adjustCurve2]; // Two (different) neighbouring geometries if (geometry1 == geometry2) { continue; } // must have the same connect line geometry IPath connectLine1 = adjustCurve1.ConnectLineAtFromPoint ?? adjustCurve1.ConnectLineAtToPoint; IPath connectLine2 = adjustCurve2.ConnectLineAtFromPoint ?? adjustCurve2.ConnectLineAtToPoint; // compare in xy only, they can be flipped IGeometry highLevelConnectLine1 = GeometryUtils.GetHighLevelGeometry( connectLine1, true); IGeometry highLevelConnectLine2 = GeometryUtils.GetHighLevelGeometry( connectLine2, true); if (!GeometryUtils.AreEqualInXY(highLevelConnectLine1, highLevelConnectLine2)) { continue; } // The connect lines are equal. Now analyze the geometries with respect to the relevant source-target pair KeyValuePair <IPoint, IPoint> sourceTargetPair = sourceTargetPairs.FirstOrDefault( pair => GeometryUtils.AreEqualInXY(connectLine1.ToPoint, pair.Value) || GeometryUtils.AreEqualInXY(connectLine1.FromPoint, pair.Value)); IPoint sourcePoint = sourceTargetPair.Key; if (sourcePoint == null) { continue; } // It must have been a shared-boundary cut-back, i.e. none of the geometries was extended // and now contains the original source intersection point: if (GeometryUtils.Intersects(geometry1, sourcePoint) || GeometryUtils.Intersects(geometry2, sourcePoint)) { continue; } // The cut back shared boundary must be *between* the two geometries // i.e. the following must be the case // - One geometry touches the target intersection point. The original geometry contained the point. // - AND the other geometry must not touch the target intersection point (but be disjoint). The original geometry is also disjoint. if (GeometryUtils.Touches(geometry1, sourceTargetPair.Value) ^ GeometryUtils.Touches(geometry2, sourceTargetPair.Value)) { ApplyReshapePath(geometry1, adjustCurve1, notifications, reshapedGeometries); ApplyReshapePath(geometry2, adjustCurve2, notifications, reshapedGeometries); } } }