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); }
/// <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); } } }