private IEnumerable<PlacementPosition> GetPossibleNodePlacementsForCorner(Point corner, TextureAtlasNode node, List<TextureAtlasNode> placedNodes, CalculatorSettings settings) { var boundaryArea = settings.Size.ToRectangle(); var possiblePlacementRects = new[] { new Rectangle(corner.X, corner.Y, node.Size.Width, node.Size.Height), new Rectangle(corner.X, corner.Y, -node.Size.Width, node.Size.Height).Normalize(), new Rectangle(corner.X, corner.Y, -node.Size.Width, -node.Size.Height).Normalize(), new Rectangle(corner.X, corner.Y, node.Size.Width, -node.Size.Height).Normalize() }; var validPlacements = possiblePlacementRects .Where(r => r.IsEntirelyContainedBy(boundaryArea)).ToArray(); var xx = validPlacements.Where(r => !placedNodes.Any(n => n.GetBounds().IntersectsWith(r))).ToArray(); foreach(var rect in xx) { var result = new PlacementPosition(rect.X, rect.Y, false, rect.Width, rect.Height); yield return result; } // No point rotating a square! if (!settings.IsRotationEnabled || node.Size.Width == node.Size.Height) yield break; foreach (var rect in new[] { new Rectangle(corner.X, corner.Y, node.Size.Height, node.Size.Width), new Rectangle(corner.X, corner.Y, -node.Size.Height, node.Size.Width).Normalize(), new Rectangle(corner.X, corner.Y, -node.Size.Height, -node.Size.Width).Normalize(), new Rectangle(corner.X, corner.Y, node.Size.Height, -node.Size.Width).Normalize(), } .Where(r => r.IsEntirelyContainedBy(boundaryArea)) .Where(r => placedNodes.Any(n => n.GetBounds().IntersectsWith(r))) ) { var result = new PlacementPosition(rect.X, rect.Y, true, rect.Width, rect.Height); yield return result; } }
public PlacementNode(TextureAtlasNode sourceNode) { SourceNode = sourceNode; Score = new PlacementScore(); Placement = new PlacementPosition(0, 0, false, 0, 0); }
/// <summary> /// Find the best possible place for the current node and gibe /// </summary> private IEnumerable<PlacementNode> GetScoredPlacements(TextureAtlasNode node, List<TextureAtlasNode> placedNodes, CalculatorSettings settings) { var result = new PlacementNode(node); //// If this is the first node to be placed, just put it in a corner //// TODO: which is better - long size on long size or long on short side? //// Look for minimum waste placement //if (!placedNodes.Any()) //{ // result.Score.IsVaildPlacement = true; // result.Score.UtilizationScore = node.Size.Width + node.Size.Height; // result.Score.WastageScore = 0; // yield return result; // yield break; //} var usedSpace = placedNodes.Select(n=>n.GetBounds()).ToArray(); var availableCorners = GetCorners(settings.Size, placedNodes) .Distinct() .Where(c => !c.IsSurroundedBy(usedSpace)); foreach (var corner in availableCorners) { foreach (var placement in GetPossibleNodePlacementsForCorner(corner, node, placedNodes, settings)) { var score = Score(corner, placement, placedNodes, settings.Size); if (score.IsVaildPlacement) { yield return new PlacementNode(node) { Score = score, Placement = placement, }; } } } }