Exemplo n.º 1
0
        /// <summary>
        /// Computes the approximate convex hull.
        /// </summary>
        public void Compute()
        {
            if (!this.Source.AnyElement())
            {
                this.result    = new Coordinate[0];
                this.hasResult = true;
                return;
            }

            if (this.Source.Count < 4)
            {
                this.result    = this.Source.Distinct().ToArray();
                this.hasResult = true;
                return;
            }

            Int32      minMin = 0, minMax = 0;
            Int32      maxMin = 0, maxMax = 0;
            Coordinate first = this.Source.FirstOrDefaultElement();

            Double     minX = first.X, maxX = first.X;
            Int32      numberOfBins = this.Source.Count / 2;
            Int32      bottomOfStack = 0, topOfStack = -1; // indexes for bottom and top of the stack
            Coordinate currentCoordinate;                  // the current coordinate being considered

            Coordinate[] hullStack = new Coordinate[this.Source.Count];

            // get the coordinates with min-max X, and min-max Y
            for (Int32 sourceIndex = 1; sourceIndex < this.Source.Count; sourceIndex++)
            {
                if (this.Source[sourceIndex] == null)
                {
                    continue;
                }

                if (this.Source[sourceIndex].X <= minX)
                {
                    if (this.Source[sourceIndex].X < minX)
                    {
                        minX   = this.Source[sourceIndex].X;
                        minMin = minMax = sourceIndex;
                    }
                    else
                    {
                        if (this.Source[sourceIndex].Y < this.Source[minMin].Y)
                        {
                            minMin = sourceIndex;
                        }
                        else if (this.Source[sourceIndex].Y > this.Source[minMax].Y)
                        {
                            minMax = sourceIndex;
                        }
                    }
                }

                if (this.Source[sourceIndex].X >= maxX)
                {
                    if (this.Source[sourceIndex].X > maxX)
                    {
                        maxX   = this.Source[sourceIndex].X;
                        maxMin = maxMax = sourceIndex;
                    }
                    else
                    {
                        if (this.Source[sourceIndex].Y < this.Source[maxMin].Y)
                        {
                            maxMin = sourceIndex;
                        }
                        else if (this.Source[sourceIndex].Y > this.Source[maxMax].Y)
                        {
                            maxMax = sourceIndex;
                        }
                    }
                }
            }

            // degenerate case: all x coordinates are equal to minX
            if (minX == maxX)
            {
                if (minMax != minMin)
                {
                    this.result = new Coordinate[] { this.Source[minMin], this.Source[minMax] }
                }
                ;
                else
                {
                    this.result = new Coordinate[] { this.Source[minMin] }
                };
            }

            // get the max and min coordinates in the range bins
            RangeBin[] binArray = new RangeBin[numberOfBins + 2];

            binArray[0].Min = minMin;
            binArray[0].Max = minMax;
            binArray[numberOfBins + 1].Min = maxMin;
            binArray[numberOfBins + 1].Max = maxMax;

            for (Int32 binIndex = 1; binIndex <= numberOfBins; binIndex++)
            {
                binArray[binIndex].Min = binArray[binIndex].Max = null;
            }

            for (Int32 sourceIndex = 0; sourceIndex < this.Source.Count; sourceIndex++)
            {
                if (this.Source[sourceIndex] == null)
                {
                    continue;
                }

                if (this.Source[sourceIndex].X == minX || this.Source[sourceIndex].X == maxX)
                {
                    continue;
                }

                Int32 binIndex;
                if (Coordinate.Orientation(this.Source[minMin], this.Source[maxMin], this.Source[sourceIndex], this.PrecisionModel) == Orientation.Clockwise)
                {
                    // below lower line
                    binIndex = Convert.ToInt32(numberOfBins * (this.Source[sourceIndex].X - minX) / (maxX - minX)) + 1;
                    if (binArray[binIndex].Min == null || this.Source[sourceIndex].Y < this.Source[binArray[binIndex].Min.Value].Y)
                    {
                        binArray[binIndex].Min = sourceIndex;
                    }
                }
                else if (Coordinate.Orientation(this.Source[minMin], this.Source[maxMin], this.Source[sourceIndex], this.PrecisionModel) == Orientation.Counterclockwise)
                {
                    // above upper line
                    binIndex = Convert.ToInt32(numberOfBins * (this.Source[sourceIndex].X - minX) / (maxX - minX)) + 1;
                    if (binArray[binIndex].Max == null || this.Source[sourceIndex].Y > this.Source[binArray[binIndex].Max.Value].Y)
                    {
                        binArray[binIndex].Max = sourceIndex;
                    }
                }
            }

            // use the chain algorithm to get the lower and upper hulls

            // compute the lower hull on the stack
            for (Int32 binIndex = 0; binIndex <= numberOfBins + 1; ++binIndex)
            {
                if (binArray[binIndex].Min == null)
                {
                    continue;
                }

                currentCoordinate = this.Source[binArray[binIndex].Min.Value];

                while (topOfStack > 0)
                {
                    // there are at least 2 points on the stack
                    if (Coordinate.Orientation(hullStack[topOfStack - 1], hullStack[topOfStack], currentCoordinate, this.PrecisionModel) == Orientation.Counterclockwise)
                    {
                        break;
                    }
                    else
                    {
                        --topOfStack;
                    }
                }

                topOfStack++;
                hullStack[topOfStack] = currentCoordinate;
            }

            // compute the upper hull on the stack above the bottom hull
            if (maxMax != maxMin)
            {
                topOfStack++;
                hullStack[topOfStack] = this.Source[maxMax];
            }

            bottomOfStack = topOfStack;

            for (Int32 binIndex = numberOfBins; binIndex >= 0; --binIndex)
            {
                if (binArray[binIndex].Max == null)
                {
                    continue;
                }

                currentCoordinate = this.Source[binArray[binIndex].Max.Value];

                while (topOfStack > bottomOfStack)
                {
                    // there are at least 2 points on the upper stack
                    if (Coordinate.Orientation(hullStack[topOfStack - 1], hullStack[topOfStack], currentCoordinate, this.PrecisionModel) == Orientation.Counterclockwise)
                    {
                        break;
                    }
                    else
                    {
                        topOfStack--;
                    }
                }

                topOfStack++;
                hullStack[topOfStack] = currentCoordinate;
            }

            // push joining endpoint onto stack
            if (minMax != minMin)
            {
                topOfStack++;
                hullStack[topOfStack] = this.Source[minMin];
            }

            // generate result from stack
            hullStack[topOfStack] = hullStack[0];

            this.result    = hullStack.GetRange(topOfStack + 1);
            this.hasResult = true;
        }
        /// <summary>
        /// Computes the approximate convex hull.
        /// </summary>
        public void Compute()
        {
            if (_source.Count < 4)
            {
                _result    = _source.Distinct().ToArray();
                _hasResult = true;
                return;
            }

            // source: http://geomalgorithms.com/a11-_hull-2.html

            Int32      minMin = 0, minMax = 0;
            Int32      maxMin = 0, maxMax = 0;
            Double     xMin = _source[0].X, xMax = _source[0].X;
            Int32      numberOfContainers = _source.Count / 2;
            Int32      bottomOfStack = 0, topOfStack = -1; // indices for bottom and top of the stack
            Coordinate currentCoordinate;                  // the current coordinate being considered

            Coordinate[] hullStack = new Coordinate[_source.Count];

            // get the coordinates with min-max X, and min-max Y
            for (Int32 i = 1; i < _source.Count; i++)
            {
                if (_source[i].X <= xMin)
                {
                    if (_source[i].X < xMin)
                    {
                        xMin   = _source[i].X;
                        minMin = minMax = i;
                    }
                    else
                    {
                        if (_source[i].Y < _source[minMin].Y)
                        {
                            minMin = i;
                        }
                        else if (_source[i].Y > _source[minMax].Y)
                        {
                            minMax = i;
                        }
                    }
                }
                if (_source[i].X >= xMax)
                {
                    if (_source[i].X > xMax)
                    {
                        xMax   = _source[i].X;
                        maxMin = maxMax = i;
                    }
                    else
                    {
                        if (_source[i].Y < _source[maxMin].Y)
                        {
                            maxMin = i;
                        }
                        else if (_source[i].Y > _source[maxMax].Y)
                        {
                            maxMax = i;
                        }
                    }
                }
            }

            // degenerate case: all x coordinates are equal to xmin
            if (xMin == xMax)
            {
                if (minMax != minMin)
                {
                    _result = new Coordinate[] { _source[minMin], _source[minMax] }
                }
                ;
                else
                {
                    _result = new Coordinate[] { _source[minMin] }
                };
            }

            // get the max and min coordinates in the range bins
            RangeBin[] binArray = new RangeBin[numberOfContainers + 2];

            binArray[0].Min = minMin; binArray[0].Max = minMax;
            binArray[numberOfContainers + 1].Min = maxMin; binArray[numberOfContainers + 1].Max = maxMax;
            for (Int32 b = 1; b <= numberOfContainers; b++)
            {
                binArray[b].Min = binArray[b].Max = null;
            }

            for (Int32 b, i = 0; i < _source.Count; i++)
            {
                if (_source[i].X == xMin || _source[i].X == xMax)
                {
                    continue;
                }

                if (Coordinate.Orientation(_source[minMin], _source[maxMin], _source[i], PrecisionModel) == Orientation.Clockwise) // below lower line
                {
                    b = Convert.ToInt32(numberOfContainers * (_source[i].X - xMin) / (xMax - xMin)) + 1;
                    if (binArray[b].Min == null || _source[i].Y < _source[binArray[b].Min.Value].Y)
                    {
                        binArray[b].Min = i;
                    }
                }
                else if (Coordinate.Orientation(_source[minMin], _source[maxMin], _source[i], PrecisionModel) == Orientation.CounterClockwise) // above upper line
                {
                    b = Convert.ToInt32(numberOfContainers * (_source[i].X - xMin) / (xMax - xMin)) + 1;
                    if (binArray[b].Max == null || _source[i].Y > _source[binArray[b].Max.Value].Y)
                    {
                        binArray[b].Max = i;
                    }
                }
            }

            // use the chain algorithm to get the lower and upper hulls

            // compute the lower hull on the stack
            for (Int32 i = 0; i <= numberOfContainers + 1; ++i)
            {
                if (binArray[i].Min == null)
                {
                    continue;
                }

                currentCoordinate = _source[binArray[i].Min.Value];

                while (topOfStack > 0) // there are at least 2 points on the stack
                {
                    if (Coordinate.Orientation(hullStack[topOfStack - 1], hullStack[topOfStack], currentCoordinate, PrecisionModel) == Orientation.CounterClockwise)
                    {
                        break;
                    }
                    else
                    {
                        --topOfStack;
                    }
                }
                topOfStack++;
                hullStack[topOfStack] = currentCoordinate;
            }

            // compute the upper hull on the stack above the bottom hull
            if (maxMax != maxMin)
            {
                topOfStack++;
                hullStack[topOfStack] = _source[maxMax];
            }

            bottomOfStack = topOfStack;

            for (Int32 i = numberOfContainers; i >= 0; --i)
            {
                if (binArray[i].Max == null)
                {
                    continue;
                }

                currentCoordinate = _source[binArray[i].Max.Value];

                while (topOfStack > bottomOfStack) // there are at least 2 points on the upper stack
                {
                    if (Coordinate.Orientation(hullStack[topOfStack - 1], hullStack[topOfStack], currentCoordinate, PrecisionModel) == Orientation.CounterClockwise)
                    {
                        break;
                    }
                    else
                    {
                        topOfStack--;
                    }
                }

                topOfStack++;
                hullStack[topOfStack] = currentCoordinate;
            }

            // push joining endpoint onto stack
            if (minMax != minMin)
            {
                topOfStack++;
                hullStack[topOfStack] = _source[minMin];
            }

            // generate result from stack
            _result = new Coordinate[topOfStack + 1];
            Array.Copy(hullStack, _result, topOfStack);
            _result[topOfStack] = _result[0];

            _hasResult = true;
        }