/// <summary> /// See IMapper. /// </summary> /// <param name="images"></param> /// <returns></returns> public override S Mapping(IEnumerable <IImageInfo> images, IMapperStats mapperStats) { int candidateSpriteFails = 0; int candidateSpritesGenerated = 0; int canvasRectangleAddAttempts = 0; int canvasNbrCellsGenerated = 0; // Sort the images by height descending IOrderedEnumerable <IImageInfo> imageInfosHighestFirst = images.OrderByDescending(p => p.Height); int totalAreaAllImages = (from a in imageInfosHighestFirst select a.Width * a.Height).Sum(); int widthWidestImage = (from a in imageInfosHighestFirst select a.Width).Max(); int heightHighestImage = imageInfosHighestFirst.First().Height; S bestSprite = null; int canvasMaxWidth = Canvas.UnlimitedSize; int canvasMaxHeight = heightHighestImage; while (canvasMaxWidth >= widthWidestImage) { CanvasStats canvasStats = new CanvasStats(); int lowestFreeHeightDeficitTallestRightFlushedImage; S spriteInfo = MappingRestrictedBox(imageInfosHighestFirst, canvasMaxWidth, canvasMaxHeight, canvasStats, out lowestFreeHeightDeficitTallestRightFlushedImage); canvasRectangleAddAttempts += canvasStats.RectangleAddAttempts; canvasNbrCellsGenerated += canvasStats.NbrCellsGenerated; if (spriteInfo == null) { // Failure - Couldn't generate a SpriteInfo with the given maximum canvas dimensions candidateSpriteFails++; // Try again with a greater max height. Add enough height so that // you don't get the same rectangle placement as this time. if (canvasStats.LowestFreeHeightDeficit == Int32.MaxValue) { canvasMaxHeight++; } else { canvasMaxHeight += canvasStats.LowestFreeHeightDeficit; } } else { // Success - Managed to generate a SpriteInfo with the given maximum canvas dimensions candidateSpritesGenerated++; // Find out if the new SpriteInfo is better than the current best one if ((bestSprite == null) || (bestSprite.Area > spriteInfo.Area)) { bestSprite = spriteInfo; float bestEfficiency = (float)totalAreaAllImages / spriteInfo.Area; if (bestEfficiency >= CutoffEfficiency) { break; } } if (candidateSpritesGenerated >= MaxNbrCandidateSprites) { break; } // Try again with a reduce maximum canvas width, to see if we can squeeze out a smaller sprite // Note that in this algorithm, the maximum canvas width is never increased, so a new sprite // always has the same or a lower width than an older sprite. canvasMaxWidth = bestSprite.Width - 1; // Now that we've decreased the width of the canvas to 1 pixel less than the width // taken by the images on the canvas, we know for sure that the images whose // right borders are most to the right will have to move up. // // To make sure that the next try is not automatically a failure, increase the height of the // canvas sufficiently for the tallest right flushed image to be placed. Note that when // images are placed sorted by highest first, it will be the tallest right flushed image // that will fail to be placed if we don't increase the height of the canvas sufficiently. if (lowestFreeHeightDeficitTallestRightFlushedImage == Int32.MaxValue) { canvasMaxHeight++; } else { canvasMaxHeight += lowestFreeHeightDeficitTallestRightFlushedImage; } } // --------------------- // Adjust max canvas width and height to cut out sprites that we'll never accept int bestSpriteArea = bestSprite.Area; bool candidateBiggerThanBestSprite; bool candidateSmallerThanCombinedImages; while ( (canvasMaxWidth >= widthWidestImage) && (!CandidateCanvasFeasable( canvasMaxWidth, canvasMaxHeight, bestSpriteArea, totalAreaAllImages, out candidateBiggerThanBestSprite, out candidateSmallerThanCombinedImages))) { if (candidateBiggerThanBestSprite) { canvasMaxWidth--; } if (candidateSmallerThanCombinedImages) { canvasMaxHeight++; } } } if (mapperStats != null) { mapperStats.CandidateSpriteFails = candidateSpriteFails; mapperStats.CandidateSpritesGenerated = candidateSpritesGenerated; mapperStats.CanvasNbrCellsGenerated = canvasNbrCellsGenerated; mapperStats.CanvasRectangleAddAttempts = canvasRectangleAddAttempts; } return(bestSprite); }
/// <summary> /// See IMapping /// </summary> /// <param name="images"></param> /// <returns></returns> public abstract S Mapping(IEnumerable <IImageInfo> images, IMapperStats mapperStats);