private int FindClosestPoint(Polygon polygon,
                                     INearestNeighbours <int> accelerator,
                                     IntPoint currentPosition,
                                     bool doSeamHiding,
                                     SEAM_PLACEMENT seamPlacement,
                                     bool canTravelForwardOrBackward,
                                     int layerIndex,
                                     long lineWidth_um,
                                     out double bestDistSquared,
                                     out IntPoint endPosition)
        {
            int bestPoint;

            if (canTravelForwardOrBackward || polygon.Count == 2)
            {
                int endIndex = polygon.Count - 1;

                bestDistSquared = (polygon[0] - currentPosition).LengthSquared();
                bestPoint       = 0;
                endPosition     = polygon[endIndex];

                // check if the end is better
                double distSquared = (polygon[endIndex] - currentPosition).LengthSquared();
                if (distSquared < bestDistSquared)
                {
                    bestDistSquared = distSquared;
                    bestPoint       = endIndex;
                    endPosition     = polygon[0];
                }
            }
            else
            {
                if (doSeamHiding)
                {
                    bestPoint = polygon.FindGreatestTurnIndex(layerIndex, lineWidth_um, seamPlacement, currentPosition);
                }
                else
                {
                    bestPoint = polygon.FindClosestPositionIndex(currentPosition, accelerator);
                }

                bestDistSquared = (polygon[bestPoint] - currentPosition).LengthSquared();

                endPosition = polygon[bestPoint];
            }

            return(bestPoint);
        }
        private OptimizedPath FindClosestPolyAndPoint(IntPoint currentPosition,
                                                      QuadTree <int> polygonAccelerator,
                                                      HashSet <int> completedPolygons,
                                                      bool doSeamHiding,
                                                      SEAM_PLACEMENT seamPlacement,
                                                      int layerIndex,
                                                      long lineWidth_um,
                                                      bool canTravelForwardOrBackward,
                                                      out IntPoint endPosition)
        {
            endPosition = currentPosition;
            var bestDistSquared = double.MaxValue;
            var bestResult      = new OptimizedPath();

            foreach (var indexAndDistance in polygonAccelerator.IterateClosest(currentPosition, () => bestDistSquared))
            {
                var index = indexAndDistance.Item1;
                if (completedPolygons.Contains(Indices[index]))
                {
                    // skip this polygon it has been processed
                    continue;
                }

                int pointIndex = FindClosestPoint(Polygons[index],
                                                  Accelerator[index],
                                                  currentPosition,
                                                  doSeamHiding,
                                                  seamPlacement,
                                                  canTravelForwardOrBackward,
                                                  layerIndex,
                                                  lineWidth_um,
                                                  out double distanceSquared,
                                                  out IntPoint polyEndPosition);

                if (distanceSquared < bestDistSquared)
                {
                    bestDistSquared = distanceSquared;
                    endPosition     = polyEndPosition;
                    // the actual lookup in the input data needs to map to the source indices
                    bestResult = new OptimizedPath(Indices[index], pointIndex, true, false);
                }
            }

            return(bestResult);
        }
Beispiel #3
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">The polygon to analyze</param>
        /// <param name="considerAsSameY">Range to treat y positions as the same value.</param>
        /// <param name="startPosition">If two or more angles are similar, choose the one close to the start</param>
        /// <returns>The position that has the largest turn angle</returns>
        public static int FindGreatestTurnIndex(this Polygon inputPolygon,
                                                int layerIndex               = 0,
                                                long extrusionWidth_um       = 3,
                                                SEAM_PLACEMENT seamPlacement = SEAM_PLACEMENT.FURTHEST_BACK,
                                                IntPoint?startPosition       = null)
        {
            var count = inputPolygon.Count;

            if (seamPlacement == SEAM_PLACEMENT.ALWAYS_CENTERED_IN_BACK)
            {
                return(inputPolygon.GetCenteredInBackIndex(out _));
            }

            var positiveGroup = new CandidateGroup(extrusionWidth_um);
            var negativeGroup = new CandidateGroup(extrusionWidth_um / 4);

            // look for relatively big concave turns
            DiscoverAndAddTurns(inputPolygon,
                                extrusionWidth_um * 4,
                                negativeGroup,
                                delta => delta < -extrusionWidth_um / 2);

            if (negativeGroup.Count == 0)
            {
                // look for relatively big convex turns
                DiscoverAndAddTurns(inputPolygon,
                                    extrusionWidth_um * 4,
                                    positiveGroup,
                                    delta => delta > extrusionWidth_um * 2);

                if (positiveGroup.Count == 0)
                {
                    negativeGroup.SameDelta = extrusionWidth_um / 16;
                    // look for small concave turns
                    DiscoverAndAddTurns(inputPolygon,
                                        extrusionWidth_um,
                                        negativeGroup,
                                        delta => delta < -extrusionWidth_um / 8);

                    if (seamPlacement == SEAM_PLACEMENT.RANDOMIZED)
                    {
                        // look for very small concave turns
                        DiscoverAndAddTurns(inputPolygon,
                                            extrusionWidth_um,
                                            negativeGroup,
                                            delta => delta < -extrusionWidth_um / 32);

                        // choose at random which point to pick
                        if (negativeGroup.Count > 1)
                        {
                            var selectedPoint = (int)(inputPolygon.GetLongHashCode(layerIndex.GetLongHashCode()) % (ulong)negativeGroup.Count);
                            var singlePoint   = negativeGroup[selectedPoint];
                            // remove every point except the random one we want
                            negativeGroup.Clear();
                            negativeGroup.Add(singlePoint);
                        }
                    }
                }
            }

            if (negativeGroup.Count > 0)
            {
                return(negativeGroup.GetBestIndex(startPosition));
            }
            else if (positiveGroup.Count > 0)
            {
                return(positiveGroup.GetBestIndex(startPosition));
            }
            else             // there is no really good candidate
            {
                switch (seamPlacement)
                {
                case SEAM_PLACEMENT.CENTERED_IN_BACK:
                    return(inputPolygon.GetCenteredInBackIndex(out _));

                case SEAM_PLACEMENT.RANDOMIZED:
                    return((int)(inputPolygon.GetLongHashCode(layerIndex.GetLongHashCode()) % (ulong)inputPolygon.Count));

                case SEAM_PLACEMENT.FURTHEST_BACK:
                default:
                    var currentFurthestBack = new IntPoint(long.MaxValue, long.MinValue);
                    int furthestBackIndex   = 0;
                    // get the furthest back index
                    for (var i = 0; i < count; i++)
                    {
                        var currentPoint = inputPolygon[i];

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

                    // If can't find good candidate go with vertex most in a single direction
                    return(furthestBackIndex);

                case SEAM_PLACEMENT.FASTEST:
                    if (startPosition != null)
                    {
                        return(inputPolygon.FindClosestIndex(startPosition.Value));
                    }
                    return(0);
                }
            }
        }