Esempio n. 1
0
        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);
            }
        }
Esempio n. 2
0
        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);
        }