| 155 | /// <returns>The node that contains inserted an item or null if failed to find a free space.</returns> |
| 156 | template<class... Args> |
| 157 | NodeType* Insert(Size width, Size height, Args&&... args) |
| 158 | { |
| 159 | NodeType* result = nullptr; |
| 160 | const Size paddedWidth = width + BordersPadding; |
| 161 | const Size paddedHeight = height + BordersPadding; |
| 162 | |
| 163 | // Search free nodes from back to front and find the one that fits requested item size |
| 164 | // TODO: FreeNodes are sorted so use Binary Search to quickly find the first tile that might have enough space for insert |
| 165 | for (int32 i = FreeNodes.Count() - 1; i >= 0; i--) |
| 166 | { |
| 167 | NodeType* freeNode = FreeNodes.Get()[i]; |
| 168 | if (paddedWidth > freeNode->Width || paddedHeight > freeNode->Height) |
| 169 | { |
| 170 | // Not enough space |
| 171 | continue; |
| 172 | } |
| 173 | |
| 174 | // Check if there will be some remaining space left in this node |
| 175 | if (freeNode->Width != width || freeNode->Height != height) |
| 176 | { |
| 177 | // Subdivide this node into up to 2 additional nodes |
| 178 | const Size remainingWidth = freeNode->Width - paddedWidth; |
| 179 | const Size remainingHeight = freeNode->Height - paddedHeight; |
| 180 | |
| 181 | // Split the remaining area around this node into two children |
| 182 | SizeRect bigger, smaller; |
| 183 | if (remainingHeight <= remainingWidth) |
| 184 | { |
| 185 | // Split vertically |
| 186 | smaller = SizeRect(freeNode->X, freeNode->Y + paddedHeight, width, remainingHeight); |
| 187 | bigger = SizeRect(freeNode->X + paddedWidth, freeNode->Y, remainingWidth, freeNode->Height); |
| 188 | } |
| 189 | else |
| 190 | { |
| 191 | // Split horizontally |
| 192 | smaller = SizeRect(freeNode->X + paddedWidth, freeNode->Y, remainingWidth, height); |
| 193 | bigger = SizeRect(freeNode->X, freeNode->Y + paddedHeight, freeNode->Width, remainingHeight); |
| 194 | } |
| 195 | if (smaller.W * smaller.H > bigger.W * bigger.H) |
| 196 | Swap(bigger, smaller); |
| 197 | if (bigger.W * bigger.H > BordersPadding) |
| 198 | AddFreeNode(Nodes.Add(NodeType(bigger.X, bigger.Y, bigger.W, bigger.H))); |
| 199 | if (smaller.W * smaller.H > BordersPadding) |
| 200 | AddFreeNode(Nodes.Add(NodeType(smaller.X, smaller.Y, smaller.W, smaller.H))); |
| 201 | |
| 202 | // Shrink to the actual area |
| 203 | freeNode->Width = width; |
| 204 | freeNode->Height = height; |
| 205 | } |
| 206 | |
| 207 | // Insert into this node |
| 208 | result = freeNode; |
| 209 | FreeNodes.RemoveAtKeepOrder(i); |
| 210 | result->OnInsert(Forward<Args>(args)...); |
| 211 | break; |
| 212 | } |
| 213 | |
| 214 | return result; |