private void AddGroupIntersectionsToRestrictedRay(Obstacle obstacle, IList <IntersectionInfo> intersections) { // We'll let the lines punch through any intersections with groups, but track the location so we can enable/disable crossing. foreach (var intersectionInfo in intersections) { var intersect = SpliceUtility.RawIntersection(intersectionInfo, currentRestrictedRay.Start); // Skip intersections that are past the end of the restricted segment (though there may still be some // there if we shorten it later, but we'll skip them later). var distSquared = (intersect - currentRestrictedRay.Start).LengthSquared; if (distSquared > restrictedRayLengthSquared) { continue; } var dirTowardIntersect = PointComparer.GetPureDirection(currentRestrictedRay.Start, currentRestrictedRay.End); var polyline = (Polyline)intersectionInfo.Segment1; // this is the second arg to GetAllIntersections var dirsOfSide = CompassVector.VectorDirection(polyline.Derivative(intersectionInfo.Par1)); // The derivative is always clockwise, so if the side contains the rightward rotation of the // direction from the ray origin, then we're hitting it from the inside; otherwise from the outside. var dirToInsideOfGroup = dirTowardIntersect; if (0 != (dirsOfSide & CompassVector.RotateRight(dirTowardIntersect))) { dirToInsideOfGroup = CompassVector.OppositeDir(dirToInsideOfGroup); } CurrentGroupBoundaryCrossingMap.AddIntersection(intersect, obstacle, dirToInsideOfGroup); } }
internal ObstaclePortEntrance(ObstaclePort oport, Point unpaddedBorderIntersect, Direction outDir, ObstacleTree obstacleTree) { ObstaclePort = oport; UnpaddedBorderIntersect = unpaddedBorderIntersect; OutwardDirection = outDir; // Get the padded intersection. var lineSeg = new LineSegment(UnpaddedBorderIntersect, StaticGraphUtility.RectangleBorderIntersect( oport.Obstacle.VisibilityBoundingBox, UnpaddedBorderIntersect, outDir)); IList <IntersectionInfo> xxs = Curve.GetAllIntersections(lineSeg, oport.Obstacle.VisibilityPolyline, true /*liftIntersections*/); Debug.Assert(1 == xxs.Count, "Expected one intersection"); this.VisibilityBorderIntersect = ApproximateComparer.Round(SpliceUtility.RawIntersection(xxs[0], UnpaddedBorderIntersect)); this.MaxVisibilitySegment = obstacleTree.CreateMaxVisibilitySegment(this.VisibilityBorderIntersect, this.OutwardDirection, out this.pointAndCrossingsList); // Groups are never in a clump (overlapped) but they may still have their port entrance overlapped. if (this.Obstacle.IsOverlapped || (this.Obstacle.IsGroup && !this.Obstacle.IsInConvexHull)) { this.IsOverlapped = obstacleTree.IntersectionIsInsideAnotherObstacle(/*sideObstacle:*/ null, this.Obstacle , this.VisibilityBorderIntersect, ScanDirection.GetInstance(OutwardDirection)); if (!this.Obstacle.IsGroup || this.IsOverlapped || this.InteriorEdgeCrossesObstacle(obstacleTree)) { unpaddedToPaddedBorderWeight = ScanSegment.OverlappedWeight; } } if (this.Obstacle.IsInConvexHull && (unpaddedToPaddedBorderWeight == ScanSegment.NormalWeight)) { SetUnpaddedToPaddedBorderWeightFromHullSiblingOverlaps(obstacleTree); } }
HitTestBehavior InsideObstacleHitTest(Point location, Obstacle obstacle) { if ((obstacle == insideHitTestIgnoreObstacle1) || (obstacle == insideHitTestIgnoreObstacle2)) { // It's one of the two obstacles we already know about. return(HitTestBehavior.Continue); } if (obstacle.IsGroup) { // Groups are handled differently from overlaps; we create ScanSegments (overlapped // if within a non-group obstacle, else non-overlapped), and turn on/off access across // the Group boundary vertices. return(HitTestBehavior.Continue); } if (!StaticGraphUtility.PointIsInRectangleInterior(location, obstacle.VisibilityBoundingBox)) { // The point is on the obstacle boundary, not inside it. return(HitTestBehavior.Continue); } // Note: There are rounding issues using Curve.PointRelativeToCurveLocation at angled // obstacle boundaries, hence this function. Point high = StaticGraphUtility.RectangleBorderIntersect(obstacle.VisibilityBoundingBox, location , insideHitTestScanDirection.Direction) + insideHitTestScanDirection.DirectionAsPoint; Point low = StaticGraphUtility.RectangleBorderIntersect(obstacle.VisibilityBoundingBox, location , insideHitTestScanDirection.OppositeDirection) - insideHitTestScanDirection.DirectionAsPoint; var testSeg = new LineSegment(low, high); IList <IntersectionInfo> xxs = Curve.GetAllIntersections(testSeg, obstacle.VisibilityPolyline, true /*liftIntersections*/); // If this is an extreme point it can have one intersection, in which case we're either on the border // or outside; if it's a collinear flat boundary, there can be 3 intersections to this point which again // means we're on the border (and 3 shouldn't happen anymore with the curve intersection fixes and // PointIsInsideRectangle check above). So the interesting case is that we have 2 intersections. if (2 == xxs.Count) { Point firstInt = SpliceUtility.RawIntersection(xxs[0], location); Point secondInt = SpliceUtility.RawIntersection(xxs[1], location); // If we're on either intersection, we're on the border rather than inside. if (!PointComparer.Equal(location, firstInt) && !PointComparer.Equal(location, secondInt) && (location.CompareTo(firstInt) != location.CompareTo(secondInt))) { // We're inside. However, this may be an almost-flat side, in which case rounding // could have reported the intersection with the start or end of the same side and // a point somewhere on the interior of that side. Therefore if both intersections // are on the same side (integral portion of the parameter), we consider location // to be on the border. testSeg is always xxs[*].Segment0. Debug.Assert(testSeg == xxs[0].Segment0, "incorrect parameter ordering to GetAllIntersections"); if (!ApproximateComparer.Close(Math.Floor(xxs[0].Par1), Math.Floor(xxs[1].Par1))) { return(HitTestBehavior.Stop); } } } return(HitTestBehavior.Continue); }
private void LookForCloserNonGroupIntersectionToRestrictRay(IList <IntersectionInfo> intersections) { int numberOfGoodIntersections = 0; IntersectionInfo closestIntersectionInfo = null; var localLeastDistSquared = this.restrictedRayLengthSquared; var testDirection = PointComparer.GetDirections(restrictedIntersectionTestSegment.Start, restrictedIntersectionTestSegment.End); foreach (var intersectionInfo in intersections) { var intersect = SpliceUtility.RawIntersection(intersectionInfo, currentRestrictedRay.Start); var dirToIntersect = PointComparer.GetDirections(currentRestrictedRay.Start, intersect); if (dirToIntersect == CompassVector.OppositeDir(testDirection)) { continue; } ++numberOfGoodIntersections; if (Directions.None == dirToIntersect) { localLeastDistSquared = 0.0; closestIntersectionInfo = intersectionInfo; continue; } var distSquared = (intersect - currentRestrictedRay.Start).LengthSquared; if (distSquared < localLeastDistSquared) { // Rounding may falsely report two intersections as different when they are actually "Close", // e.g. a horizontal vs. vertical intersection on a slanted edge. var rawDistSquared = (intersectionInfo.IntersectionPoint - currentRestrictedRay.Start).LengthSquared; if (rawDistSquared < ApproximateComparer.SquareOfDistanceEpsilon) { continue; } localLeastDistSquared = distSquared; closestIntersectionInfo = intersectionInfo; } } if (null != closestIntersectionInfo) { // If there was only one intersection and it is quite close to an end, ignore it. // If there is more than one intersection, we have crossed the obstacle so we want it. if (numberOfGoodIntersections == 1) { var intersect = SpliceUtility.RawIntersection(closestIntersectionInfo, currentRestrictedRay.Start); if (ApproximateComparer.CloseIntersections(intersect, this.currentRestrictedRay.Start) || ApproximateComparer.CloseIntersections(intersect, this.currentRestrictedRay.End)) { return; } } this.restrictedRayLengthSquared = localLeastDistSquared; currentRestrictedRay.End = SpliceUtility.MungeClosestIntersectionInfo(currentRestrictedRay.Start, closestIntersectionInfo , !StaticGraphUtility.IsVertical(currentRestrictedRay.Start, currentRestrictedRay.End)); } }