Initial commit

This commit is contained in:
4DBug
2026-10-07 13:03:05 -05:00
commit c2cf349390
17 changed files with 34877 additions and 0 deletions
+444
View File
@@ -0,0 +1,444 @@
if _G.__IMGUI_ENV then setfenv(1, _G.__IMGUI_ENV) end -- [Roblox] shared env, see init.lua
--- ImGui Sincerely
-- This is a Lua port of original `imstb_rectpack.h`
-- ALL TABLES IN THIS FILE ARE 1-BASED!
-- Avoids table creation when possible
--- The `prev_link` can be stbrp_context or stbrp_node
--- @diagnostic disable
local bit32 = bit32
local STBRP_SORT = table.sort
local STBRP_ASSERT = assert
local STBRP__NOTUSED = function(_) end
local STBRP__MAXVAL = 0x7fffffff
--- @enum STBRP_HEURISTIC_Skyline
local STBRP_HEURISTIC_Skyline = {
default = 0,
BL_sortHeight = 0,
BF_sortHeight = 1
}
local STBRP__INIT_skyline = 1
--- @enum STBRP__PREV_LINK_TYPE
local STBRP__PREV_LINK_TYPE = {
ACTIVE_HEAD = 0,
NEXT = 1
}
--- @alias stbrp_coord int
--- @class stbrp_rect
--- @field id int
--- @field w stbrp_coord
--- @field h stbrp_coord
--- @field x stbrp_coord
--- @field y stbrp_coord
--- @field was_packed int
--- @return stbrp_rect
--- @nodiscard
local function stbrp_rect()
return {
id = 0,
w = 0,
h = 0,
x = 0,
y = 0,
was_packed = 0
}
end
--- @class stbrp_node
--- @field x stbrp_coord
--- @field y stbrp_coord
--- @field next stbrp_node
--- @return stbrp_node
--- @nodiscard
local function stbrp_node()
return {
x = 0,
y = 0,
next = nil
}
end
--- @class stbrp_context
--- @field width int
--- @field height int
--- @field align int
--- @field init_mode int
--- @field heuristic int
--- @field num_nodes int
--- @field active_head stbrp_node
--- @field free_head stbrp_node
--- @field extra stbrp_node[]
--- @return stbrp_context
--- @nodiscard
local function stbrp_context()
return {
width = 0,
height = 0,
align = 0,
init_mode = 0,
heuristic = 0,
num_nodes = 0,
active_head = nil,
free_head = nil,
extra = {stbrp_node(), stbrp_node()}
}
end
--- @class stbrp__findresult
--- @field x int
--- @field y int
--- @field prev_link? table
--- @field prev_link_type? int
--- @return stbrp__findresult
local function stbrp__findresult()
return {
x = 0,
y = 0,
prev_link = nil,
prev_link_type = nil
}
end
--------------------------
--------------------------
---
--- IMPLEMENTATION SECTION
---
---
--- @param context stbrp_context
--- @param heuristic int
local function stbrp_setup_heuristic(context, heuristic)
if context.init_mode == STBRP__INIT_skyline then
STBRP_ASSERT(heuristic == STBRP_HEURISTIC_Skyline.BL_sortHeight or heuristic == STBRP_HEURISTIC_Skyline.BF_sortHeight)
context.heuristic = heuristic
else
STBRP_ASSERT(false)
end
end
--- @param context stbrp_context
--- @param allow_out_of_mem boolean
local function stbrp_setup_allow_out_of_mem(context, allow_out_of_mem)
if allow_out_of_mem then
context.align = 1
else
context.align = (context.width + context.num_nodes - 1) / context.num_nodes
end
end
--- @param context stbrp_context
--- @param width int
--- @param height int
--- @param nodes stbrp_node[]
--- @param num_nodes int
function stbrp_init_target(context, width, height, nodes, num_nodes)
for i = 1, num_nodes - 1 do
nodes[i].next = nodes[i + 1]
end
nodes[num_nodes].next = nil
context.init_mode = STBRP__INIT_skyline
context.heuristic = STBRP_HEURISTIC_Skyline.default
context.free_head = nodes[1]
context.active_head = context.extra[1]
context.width = width
context.height = height
context.num_nodes = num_nodes
stbrp_setup_allow_out_of_mem(context, false)
context.extra[1].x = 0
context.extra[1].y = 0
context.extra[1].next = context.extra[2]
context.extra[2].x = width
context.extra[2].y = bit32.lshift(1, 30)
context.extra[2].next = nil
end
--- @param c stbrp_context
--- @param first stbrp_node
--- @param x0 int
--- @param width int
--- @return int, int
local function stbrp__skyline_find_min_y(c, first, x0, width)
local node = first
local x1 = x0 + width
STBRP__NOTUSED(c)
STBRP_ASSERT(first.x <= x0)
STBRP_ASSERT(node.next.x > x0)
STBRP_ASSERT(node.x <= x0)
local min_y = 0
local waste_area = 0
local visited_width = 0
while node.x < x1 do
if node.y > min_y then
waste_area = waste_area + visited_width * (node.y - min_y)
min_y = node.y
if node.x < x0 then
visited_width = visited_width + node.next.x - x0
else
visited_width = visited_width + node.next.x - node.x
end
else
local under_width = node.next.x - node.x
if under_width + visited_width > width then
under_width = width - visited_width
end
waste_area = waste_area + under_width * (min_y - node.y)
visited_width = visited_width + under_width
end
node = node.next
end
return min_y, waste_area
end
--- @param c stbrp_context
--- @param width int
--- @param height int
--- @return stbrp__findresult
local function stbrp__skyline_find_best_pos(c, width, height)
local best_waste = bit32.lshift(1, 30)
local best_x = 0
local best_y = bit32.lshift(1, 30)
local fr = stbrp__findresult()
local best_prev_link = nil
local best_prev_link_type = nil
width = width + c.align - 1
width = width - width % c.align
STBRP_ASSERT(width % c.align == 0)
if width > c.width or height > c.height then
fr.prev_link = nil
fr.prev_link_type = nil
fr.x = 0
fr.y = 0
return fr
end
local prev_link = c
local prev_link_type = STBRP__PREV_LINK_TYPE.ACTIVE_HEAD
local node = c.active_head
while (node.x + width <= c.width) do
local y, waste = stbrp__skyline_find_min_y(c, node, node.x, width)
if c.heuristic == STBRP_HEURISTIC_Skyline.BL_sortHeight then
if y < best_y then
best_y = y
best_prev_link = prev_link
best_prev_link_type = prev_link_type
best_x = node.x
end
else
if y + height <= c.height then
if y < best_y or (y == best_y and waste < best_waste) then
best_y = y
best_waste = waste
best_prev_link = prev_link
best_prev_link_type = prev_link_type
best_x = node.x
end
end
end
prev_link = node
prev_link_type = STBRP__PREV_LINK_TYPE.NEXT
node = node.next
end
if c.heuristic == STBRP_HEURISTIC_Skyline.BF_sortHeight then
local tail = c.active_head
local node_scan = c.active_head
local prev_link_scan = c
local prev_link_scan_type = STBRP__PREV_LINK_TYPE.ACTIVE_HEAD
while tail and tail.x < width do
tail = tail.next
end
while tail do
local xpos = tail.x - width
STBRP_ASSERT(xpos >= 0)
while node_scan.next and node_scan.next.x <= xpos do
prev_link_scan = node_scan
prev_link_scan_type = STBRP__PREV_LINK_TYPE.NEXT
node_scan = node_scan.next
end
if node_scan then
STBRP_ASSERT(node_scan.next.x > xpos and node_scan.x <= xpos)
local y, waste = stbrp__skyline_find_min_y(c, node_scan, xpos, width)
if y + height <= c.height then
if y <= best_y then
if y < best_y or waste < best_waste or (waste == best_waste and xpos < best_x) then
best_x = xpos
best_y = y
best_waste = waste
best_prev_link = prev_link_scan
best_prev_link_type = prev_link_scan_type
end
end
end
end
tail = tail.next
end
end
fr.prev_link = best_prev_link
fr.prev_link_type = best_prev_link_type
fr.x = best_x
fr.y = best_y
return fr
end
--- @param context stbrp_context
--- @param width int
--- @param height int
--- @return stbrp__findresult
local function stbrp__skyline_pack_rectangle(context, width, height)
local res = stbrp__skyline_find_best_pos(context, width, height)
if res.prev_link == nil or res.y + height > context.height or context.free_head == nil then
res.prev_link = nil
res.prev_link_type = nil
return res
end
local node = context.free_head
node.x = res.x
node.y = res.y + height
context.free_head = node.next
local cur
if res.prev_link_type == STBRP__PREV_LINK_TYPE.ACTIVE_HEAD then
cur = res.prev_link.active_head
else
cur = res.prev_link.next
end
if cur.x < res.x then
local next = cur.next
cur.next = node
cur = next
else
if res.prev_link_type == STBRP__PREV_LINK_TYPE.ACTIVE_HEAD then
res.prev_link.active_head = node
else
res.prev_link.next = node
end
end
while cur.next and cur.next.x <= res.x + width do
local next = cur.next
cur.next = context.free_head
context.free_head = cur
cur = next
end
node.next = cur
if cur.x < res.x + width then
cur.x = res.x + width
end
return res
end
--- @param a stbrp_rect
--- @param b stbrp_rect
--- @return bool
--- @package
local function rect_height_compare(a, b)
if a.h > b.h then
return true
elseif a.h < b.h then
return false
else
return a.w > b.w
end
end
--- @param a stbrp_rect
--- @param b stbrp_rect
--- @return bool
--- @package
local function rect_original_order(a, b)
return a.was_packed < b.was_packed
end
--- @param context stbrp_context
--- @param rects stbrp_rect[]
--- @param num_rects int
--- @return int
local function stbrp_pack_rects(context, rects, num_rects)
local all_rects_packed = 1
for i = 1, num_rects do
rects[i].was_packed = (i - 1)
end
STBRP_SORT(rects, rect_height_compare)
for i = 1, num_rects do
if rects[i].w == 0 or rects[i].h == 0 then
rects[i].x = 0
rects[i].y = 0
else
local fr = stbrp__skyline_pack_rectangle(context, rects[i].w, rects[i].h)
if fr.prev_link then
rects[i].x = fr.x
rects[i].y = fr.y
else
rects[i].x = STBRP__MAXVAL
rects[i].y = STBRP__MAXVAL
end
end
end
STBRP_SORT(rects, rect_original_order)
for i = 1, num_rects do
rects[i].was_packed = (rects[i].x == STBRP__MAXVAL and rects[i].y == STBRP__MAXVAL) and 0 or 1
if rects[i].was_packed == 0 then
all_rects_packed = 0
end
end
return all_rects_packed
end
return {
context = stbrp_context,
rect = stbrp_rect,
node = stbrp_node,
pack_rects = stbrp_pack_rects,
init_target = stbrp_init_target,
setup_allow_out_of_mem = stbrp_setup_allow_out_of_mem,
setup_heuristic = stbrp_setup_heuristic
}