private static void DiscoverAndAddTurns(Polygon inputPolygon,
                                                long neighborhood,
                                                CandidateGroup candidateGroup,
                                                Func <double, bool> validDelta)
        {
            var polygon2 = inputPolygon.Select(i => new Vector2(i.X, i.Y)).ToList();
            var count    = inputPolygon.Count;

            for (var i = 0; i < count; i++)
            {
                var position      = polygon2[i];
                var prevPosition  = polygon2[(count + i - 1) % count];
                var nextPosition  = polygon2[(count + i + 1) % count];
                var angle         = position.GetTurnAmount(prevPosition, nextPosition);
                var lengthToPoint = polygon2.LengthTo(i);

                var leftPosition    = polygon2.GetPositionAt(lengthToPoint - neighborhood);
                var rightPosition   = polygon2.GetPositionAt(lengthToPoint + neighborhood);
                var nearAngle       = position.GetTurnAmount(leftPosition, rightPosition);
                var directionNormal = (rightPosition - leftPosition).GetNormal().GetPerpendicularRight();
                var delta           = Vector2.Dot(directionNormal, position - leftPosition);

                var currentPoint = inputPolygon[i];
                if (validDelta(delta))
                {
                    candidateGroup.ConditionalAdd(new CandidatePoint(delta, i, currentPoint));
                }
            }
        }
Beispiel #2
0
        /// <summary>
        /// This will find the largest turn in a given models. It prefers concave turns to convex turns.
        /// If turn amount is the same bias towards the smallest y position.
        /// </summary>
        /// <param name="inputPolygon"></param>
        /// <param name="considerAsSameY">Range to treat y positions as the same value.</param>
        /// <returns></returns>
        public static IntPoint FindGreatestTurnPosition(this Polygon inputPolygon, long considerAsSameY, int layerIndex, IntPoint?startPosition = null)
        {
            Polygon currentPolygon = Clipper.CleanPolygon(inputPolygon, considerAsSameY / 8);

            // collect & bucket options and then choose the closest
            if (currentPolygon.Count == 0)
            {
                return(inputPolygon[0]);
            }

            double         totalTurns    = 0;
            CandidateGroup positiveGroup = new CandidateGroup(DegreesToRadians(35));
            CandidateGroup negativeGroup = new CandidateGroup(DegreesToRadians(10));

            IntPoint currentFurthestBack = new IntPoint(long.MaxValue, long.MinValue);
            int      furthestBackIndex   = 0;

            double minTurnToChoose = DegreesToRadians(1);
            long   minSegmentLengthToConsiderSquared = 50 * 50;

            int pointCount = currentPolygon.Count;

            for (int pointIndex = 0; pointIndex < pointCount; pointIndex++)
            {
                int      prevIndex    = ((pointIndex + pointCount - 1) % pointCount);
                int      nextIndex    = ((pointIndex + 1) % pointCount);
                IntPoint prevPoint    = currentPolygon[prevIndex];
                IntPoint currentPoint = currentPolygon[pointIndex];
                IntPoint nextPoint    = currentPolygon[nextIndex];

                if (currentPoint.Y >= currentFurthestBack.Y)
                {
                    if (currentPoint.Y > currentFurthestBack.Y ||
                        currentPoint.X < currentFurthestBack.X)
                    {
                        furthestBackIndex   = pointIndex;
                        currentFurthestBack = currentPoint;
                    }
                }

                long lengthPrevToCurSquared = (prevPoint - currentPoint).LengthSquared();
                long lengthCurToNextSquared = (nextPoint - currentPoint).LengthSquared();
                bool distanceLongeEnough    = lengthCurToNextSquared > minSegmentLengthToConsiderSquared && lengthPrevToCurSquared > minSegmentLengthToConsiderSquared;

                double turnAmount = currentPoint.GetTurnAmount(prevPoint, nextPoint);

                totalTurns += turnAmount;

                if (turnAmount < 0)
                {
                    // threshold angles, don't pick angles that are too shallow
                    // threshold line lengths, don't pick big angles hiding in TINY lines
                    if (Math.Abs(turnAmount) > minTurnToChoose &&
                        distanceLongeEnough)
                    {
                        negativeGroup.ConditionalAdd(new CandidatePoint(turnAmount, pointIndex, currentPoint));
                    }
                }
                else
                {
                    if (Math.Abs(turnAmount) > minTurnToChoose &&
                        distanceLongeEnough)
                    {
                        positiveGroup.ConditionalAdd(new CandidatePoint(turnAmount, pointIndex, currentPoint));
                    }
                }
            }

            if (negativeGroup.Count > 0)
            {
                if (positiveGroup.Count > 0
                    // the negative group is a small turn and the positive group is a big turn
                    && ((Math.Abs(negativeGroup[0].turnAmount) < Math.PI / 4 &&
                         Math.Abs(positiveGroup[0].turnAmount) > Math.PI / 4)
                        // the negative turn amount is very small
                        || Math.Abs(negativeGroup[0].turnAmount) < Math.PI / 8))
                {
                    // return the positive rather than the negative turn
                    return(currentPolygon[positiveGroup.GetBestIndex(layerIndex, startPosition)]);
                }

                return(currentPolygon[negativeGroup.GetBestIndex(layerIndex, startPosition)]);
            }
            else if (positiveGroup.Count > 0)
            {
                return(currentPolygon[positiveGroup.GetBestIndex(layerIndex, startPosition)]);
            }
            else
            {
                // If can't find good candidate go with vertex most in a single direction
                return(currentPolygon[furthestBackIndex]);
            }
        }
        public static IntPoint GetBestPosition(Polygon inputPolygon, long lineWidth)
        {
            IntPoint currentFurthestBackActual = new IntPoint(long.MaxValue, long.MinValue);
            {
                int actualFurthestBack = 0;
                for (int pointIndex = 0; pointIndex < inputPolygon.Count; pointIndex++)
                {
                    IntPoint currentPoint = inputPolygon[pointIndex];

                    if (currentPoint.Y >= currentFurthestBackActual.Y)
                    {
                        if (currentPoint.Y > currentFurthestBackActual.Y ||
                            currentPoint.X < currentFurthestBackActual.X)
                        {
                            actualFurthestBack        = pointIndex;
                            currentFurthestBackActual = currentPoint;
                        }
                    }
                }
            }

            Polygon currentPolygon = Clipper.CleanPolygon(inputPolygon, lineWidth / 4);

            // TODO: other considerations
            // collect & bucket options and then choose the closest

            if (currentPolygon.Count == 0)
            {
                return(inputPolygon[0]);
            }

            double         totalTurns    = 0;
            CandidateGroup positiveGroup = new CandidateGroup(DegreesToRadians(35));
            CandidateGroup negativeGroup = new CandidateGroup(DegreesToRadians(10));

            IntPoint currentFurthestBack = new IntPoint(long.MaxValue, long.MinValue);
            int      furthestBackIndex   = 0;

            double minTurnToChoose = DegreesToRadians(1);
            long   minSegmentLengthToConsiderSquared = 50 * 50;

            int pointCount = currentPolygon.Count;

            for (int pointIndex = 0; pointIndex < pointCount; pointIndex++)
            {
                int      prevIndex    = ((pointIndex + pointCount - 1) % pointCount);
                int      nextIndex    = ((pointIndex + 1) % pointCount);
                IntPoint prevPoint    = currentPolygon[prevIndex];
                IntPoint currentPoint = currentPolygon[pointIndex];
                IntPoint nextPoint    = currentPolygon[nextIndex];

                if (currentPoint.Y >= currentFurthestBack.Y)
                {
                    if (currentPoint.Y > currentFurthestBack.Y ||
                        currentPoint.X < currentFurthestBack.X)
                    {
                        furthestBackIndex   = pointIndex;
                        currentFurthestBack = currentPoint;
                    }
                }

                long lengthPrevToCurSquared = (prevPoint - currentPoint).LengthSquared();
                long lengthCurToNextSquared = (nextPoint - currentPoint).LengthSquared();
                bool distanceLongeEnough    = lengthCurToNextSquared > minSegmentLengthToConsiderSquared && lengthPrevToCurSquared > minSegmentLengthToConsiderSquared;

                double turnAmount = GetTurnAmount(prevPoint, currentPoint, nextPoint);

                totalTurns += turnAmount;

                if (turnAmount < 0)
                {
                    // threshold angles, don't pick angles that are too shallow
                    // threshold line lengths, don't pick big angles hiding in TINY lines
                    if (Math.Abs(turnAmount) > minTurnToChoose &&
                        distanceLongeEnough)
                    {
                        negativeGroup.ConditionalAdd(new CandidatePoint(turnAmount, pointIndex, currentPoint));
                    }
                }
                else
                {
                    if (Math.Abs(turnAmount) > minTurnToChoose &&
                        distanceLongeEnough)
                    {
                        positiveGroup.ConditionalAdd(new CandidatePoint(turnAmount, pointIndex, currentPoint));
                    }
                }
            }

            IntPoint positionToReturn = new IntPoint();

            if (totalTurns > 0)             // ccw
            {
                if (negativeGroup.Count > 0)
                {
                    positionToReturn = currentPolygon[negativeGroup.BestIndex];
                }
                else if (positiveGroup.Count > 0)
                {
                    positionToReturn = currentPolygon[positiveGroup.BestIndex];
                }
                else
                {
                    // If can't find good candidate go with vertex most in a single direction
                    positionToReturn = currentPolygon[furthestBackIndex];
                }
            }
            else             // cw
            {
                if (negativeGroup.Count > 0)
                {
                    positionToReturn = currentPolygon[negativeGroup.BestIndex];
                }
                else if (positiveGroup.Count > 0)
                {
                    positionToReturn = currentPolygon[positiveGroup.BestIndex];
                }
                else
                {
                    // If can't find good candidate go with vertex most in a single direction
                    positionToReturn = currentPolygon[furthestBackIndex];
                }
            }

            if (Math.Abs(currentFurthestBackActual.Y - positionToReturn.Y) < lineWidth)
            {
                return(currentFurthestBackActual);
            }

            return(positionToReturn);
        }