#include "rect_atlas.h"
rect_atlas_t rect_atlas_create(int32_t width, int32_t height) {
rect_atlas_t result = {};
result.free_space.add({0,0,width,height});
result.w = width;
result.h = height;
return result;
}
void rect_atlas_destroy(rect_atlas_t *atlas) {
atlas->free_space.free();
atlas->packed .free();
*atlas = {};
}
int32_t _rect_atlas_fit(rect_area_t src, const rect_area_t &r) {
if (r.w < src.w || r.h < src.h) return 0x7FFFFFFF;
int16_t w_diff = (int16_t)(r.w - src.w);
int16_t h_diff = (int16_t)(r.h - src.h);
return w_diff << 16 | h_diff;
}
int32_t rect_atlas_add(rect_atlas_t *atlas, int32_t width, int32_t height) {
int32_t idx = atlas->free_space.index_best_small_with({ 0,0,width,height }, _rect_atlas_fit);
if (idx == -1 || atlas->free_space[idx].w < width || atlas->free_space[idx].h < height)
return -1;
const rect_area_t parent = atlas->free_space[idx];
if (parent.h == height && parent.w == width) {
atlas->free_space.remove(idx);
} else if (parent.h == height) {
atlas->free_space[idx].x += width;
atlas->free_space[idx].w -= width;
} else if (parent.w == width) {
atlas->free_space[idx].y += height;
atlas->free_space[idx].h -= height;
} else {
rect_area_t new_free = {};
if (parent.h - height < parent.w - width) {
atlas->free_space[idx].y += height;
atlas->free_space[idx].h -= height;
atlas->free_space[idx].w = width;
new_free.x = parent.x + width;
new_free.y = parent.y;
new_free.w = parent.w - width;
new_free.h = parent.h;
} else {
atlas->free_space[idx].x += width;
atlas->free_space[idx].w -= width;
atlas->free_space[idx].h = height;
new_free.x = parent.x;
new_free.y = parent.y + height;
new_free.w = parent.w;
new_free.h = parent.h - height;
}
atlas->free_space.add(new_free);
}
rect_area_t new_rect = {};
new_rect.x = parent.x;
new_rect.y = parent.y;
new_rect.w = width;
new_rect.h = height;
atlas->used_area += width * height;
return atlas->packed.add(new_rect);
}
int32_t _rect_atlas_add_free_space(rect_atlas_t *atlas, rect_area_t rect, int32_t idx) {
int32_t rect_r = rect.x + rect.w;
int32_t rect_b = rect.y + rect.h;
int32_t result = -1;
for (int32_t i = 0; i < atlas->free_space.count; i++) {
if (i == idx) continue;
rect_area_t cell = atlas->free_space[i];
if (cell.h == rect.h && cell.y == rect.y) {
int32_t cell_r = cell.x + cell.w;
if (cell.x == rect_r) { atlas->free_space[i].x = rect.x;
atlas->free_space[i].w += rect.w;
result = i;
break;
} else if (cell_r == rect.x) { atlas->free_space[i].w += rect.w;
result = i;
break;
}
} else if (cell.w == rect.w && cell.x == rect.x) {
int32_t cell_b = cell.y + cell.h;
if (cell.y == rect_b) { atlas->free_space[i].y = rect.y;
atlas->free_space[i].h += rect.h;
result = i;
break;
} else if (cell_b == rect.y) { atlas->free_space[i].h += rect.h;
result = i;
break;
}
}
}
if (idx != -1 && result != -1) {
atlas->free_space.remove(idx);
if (result > idx)
result -= 1;
} else if (idx == -1 && result == -1) {
atlas->free_space.add(rect);
}
return result;
}
void rect_atlas_remove(rect_atlas_t *atlas, int32_t idx) {
rect_area_t free_space = atlas->packed[idx];
atlas->packed.remove(idx);
atlas->used_area -= free_space.w * free_space.h;
int32_t new_rect = _rect_atlas_add_free_space(atlas, free_space, -1);
while (new_rect != -1) {
new_rect = _rect_atlas_add_free_space(atlas, atlas->free_space[new_rect], new_rect);
}
}