public void Execute() { Debug.Assert(States.State != PartitionState.Finished, "Trying to partition when it's already completed"); if (States.Splitter == null) { throw new NullReferenceException("Unexpected null splitter"); } BspSegment splitter = States.Splitter; BspSegment segToSplit = States.SegsToSplit[States.CurrentSegToPartitionIndex]; States.CurrentSegToPartitionIndex++; bool doneAllSplitting = (States.CurrentSegToPartitionIndex >= States.SegsToSplit.Count); States.State = (doneAllSplitting ? PartitionState.Finished : PartitionState.Working); if (ReferenceEquals(segToSplit, splitter)) { HandleSplitter(splitter); return; } if (segToSplit.Parallel(splitter)) { HandleParallelSegment(splitter, segToSplit); return; } bool splits = splitter.IntersectionAsLine(segToSplit, out double splitterTime, out double segmentTime); Debug.Assert(splits, "A non-parallel line should intersect"); // Note that it is possible for two segments that share endpoints // to calculate intersections to each other outside of the normal // range due (ex: D2M29 has one at approx t = 1.0000000000000002). // Because they share a point and aren't parallel, then the line // must be on one of the sides and isn't intersecting. This will // also avoid very small cuts as well since we definitely do not // want a cut happening at the pathological time seen above! if (splitter.SharesAnyEndpoints(segToSplit)) { HandleSegmentOnSide(splitter, segToSplit); } else if (segmentTime.InNormalRange()) { HandleSplit(splitter, segToSplit, segmentTime, splitterTime); } else { HandleSegmentOnSide(splitter, segToSplit); } }
private int CalculateScore(BspSegment splitter) { SplitWeights splitWeights = bspConfig.SplitWeights; int score = 0; if (!IsAxisAligned(splitter)) { score += splitWeights.NotAxisAlignedScore; } int splitCount = 0; int linesOnLeft = 0; int linesOnRight = 0; foreach (BspSegment segment in States.Segments) { if (ReferenceEquals(segment, splitter)) { continue; } if (splitter.Parallel(segment)) { if (!splitter.Collinear(segment)) { if (IsEffectivelyRightOfSplitter(splitter, segment)) { linesOnRight++; } else { linesOnLeft++; } } continue; } bool intersects = segment.IntersectionAsLine(splitter, out double tSegment); Debug.Assert(intersects, "Non-parallel lines for split calculations must intersect"); double nearestDistance = CalculateDistanceToNearestEndpoint(segment, tSegment); if (tSegment.InNormalRange()) { if (SplitOccursAtEndpoint(nearestDistance)) { if (IsEffectivelyRightOfSplitter(splitter, segment)) { linesOnRight++; } else { linesOnLeft++; } } else { splitCount++; } } else if (IsEffectivelyRightOfSplitter(splitter, segment)) { linesOnRight++; } else { linesOnLeft++; } // Even though we may have missed, we want to see how close it // was to the splitter. If we pick something like t = 1.0001, // that's very close to the line and will result in likely a // very small seg being generated later on, so avoid this. if (IntersectionNearButNotAtEndpoint(nearestDistance)) { score += splitWeights.NearEndpointSplitScore; } } score += Math.Abs(linesOnLeft - linesOnRight) * splitWeights.LeftRightSplitImbalanceScore; // We do not want to select a splitter that is on the edge of the // map and has no intersections. The only cases for this are if we // pick an edge on the outside hull of the map (which we do not // want as a splitter ever unless it splits something) or a // degenerate/convex hull which we don't care about as those // shapes should never be passed into this function. Therefore, if // it's zero or more collinear traversals with no partitions, that // is always the worst case. if (NoSplitsAndLinesAllOnOneSide(splitCount, linesOnLeft, linesOnRight)) { score = int.MaxValue; } else { score += splitCount * splitWeights.SplitScoreFactor; } return(score); }