UnrealisticReactors/scripts/heat/debug.lua

380 lines
11 KiB
Lua

local rpath = (...):match("(.-)[^%.]+$")
local rroot = rpath:match("^([^%.]+%.)")
local isempty = require(rroot .. "util").isempty
local distance = require(rpath .. "math").distance
local cell_nodes = require(rpath .. "math").cell_nodes
local sameposition = require(rpath .. "util").sameposition
local PRETTYCHECK = "%s (%s)%s"
local function check(value,actual)
return value, actual, (value == actual and "" or " FAIL")
end
local function Coordinates(entity)
local x,y,z = entity.position.x,entity.position.y,entity.surface.index
return math.floor(x), math.floor(y), z
end
local function Vector(x,y,z) return {
x = x,
y = y,
z = z,
} end
-- debugging pretty prints
function dbgLinked(others)
local strings = {}
for _,cells in pairs(others) do
local str = {}
for _,cell in pairs(cells) do
table.insert(str, string.format("{entity = %s}", cell.entity.unit_number))
end
table.insert(strings, string.format("{%s}", table.concat(str, ", ")))
end
return string.format("{%s}", table.concat(strings, ", "))
end
function dbgVector(vector) return "Vector" .. serpent.line(vector) end
function dbgChunkArea(area) return "ChunkArea" .. serpent.line(area) end
function dbgReactorPosition(reactor)
local strings = {}
table.insert(strings, "id = " .. tostring(reactor.id))
table.insert(strings, "position = " .. dbgVector(reactor.position))
if reactor.cell and reactor.cell.chunk and reactor.cell.chunk.network then
table.insert(strings, "network = " .. tostring(reactor.cell.chunk.network.id))
end
table.insert(strings, "name = " .. tostring(reactor.name))
return string.format("ReactorPosition{%s}", table.concat(strings, ", "))
end
function dbgHeatNetworkCell(cell)
local strings = {}
table.insert(strings, "\tnetwork = " .. tostring(cell.chunk.network.id))
local entities = {}
local counter = {entity = 0, reactor = 0, outlet = 0}
for x,xs in pairs(cell.entities) do
for y,entity in pairs(xs) do
local str = {}
if not entity.valid then
table.insert(str, "\t\t\tvalid = false")
else
table.insert(str, "\t\t\tname = " .. entity.name)
table.insert(str, "\t\t\ttype = " .. entity.type)
table.insert(str, "\t\t\tposition = " .. serpent.line(entity.position))
table.insert(str, "\t\t\tunit_number = " .. entity.unit_number)
if entity.type ~= 'reactor' then
counter.entity = counter.entity + 1
elseif sameposition(Vector(x,y),Vector(Coordinates(entity))) then
counter.reactor = counter.reactor + 1
else
counter.outlet = counter.outlet + 1
end
end
table.insert(entities, string.format("\t\t[%s,%s] = Entity{\n%s\n\t\t}", x,y, table.concat(str, ",\n")))
end
end
local reactors = {}
for _,reactor in pairs(cell.reactors) do
table.insert(reactors, "\t\t" .. dbgReactorPosition(reactor))
end
local counts = {}
for k,count in pairs(cell.count) do
actual = 0
if k == 'entity' then
actual = counter.entity
elseif k == 'reactor' then
actual = counter.reactor
elseif k == 'outlet' then
actual = counter.outlet
end
table.insert(counts, string.format("\t\t%s = "..PRETTYCHECK, k, check(count, actual)))
end
if isempty(entities) then
table.insert(strings, "\tentities = {}")
else
table.insert(strings, "\tentities = {\n" .. table.concat(entities, ",\n") .. "\n\t} " .. string.format(PRETTYCHECK, check(#entities, counter.entity + counter.reactor + counter.outlet)))
end
if isempty(reactors) then
table.insert(strings, "\treactors = {}")
else
table.insert(strings, "\treactors = {\n" .. table.concat(reactors, ",\n") .. "\n\t} " .. string.format(PRETTYCHECK, check(#reactors, counter.reactor)))
end
table.insert(strings, "\tcount = {\n" .. table.concat(counts, ",\n") .. "\n\t}")
return "HeatNetworkCell{\n" .. table.concat(strings, ",\n") .. "\n}"
end
function dbgHeatChunk(chunk)
local direction = {[N]="north", [S]="south", [E]="east", [W]="west"}
local strings = {}
table.insert(strings, "\tnetwork = " .. tostring(chunk.network.id))
table.insert(strings, "\tposition = " .. dbgVector(chunk.position))
table.insert(strings, "\tarea = " .. dbgChunkArea(chunk.area))
local cells,lookup = {},{}
for cell in pairs(chunk.cells) do
table.insert(cells, "\t\t" .. string.gsub(dbgHeatNetworkCell(cell), "\n", "\n\t\t"))
lookup[cell] = #cells
end
if isempty(cells) then
table.insert(strings, "\tcells = {}")
else
table.insert(strings, "\tcells = {\n" .. table.concat(cells, ",\n") .. "\n\t}")
end
local border = {}
for d,border_cells in pairs(chunk.border) do
if not isempty(border_cells) then
local cells = {}
for c,cell in pairs(border_cells) do
table.insert(cells, string.format("[%s] = %s", c,tostring(lookup[cell])))
end
table.insert(border, string.format("\t\t%s = {%s}", direction[d], table.concat(cells, ", ")))
end
end
if isempty(border) then
table.insert(strings, "\tborder = {}")
else
table.insert(strings, "\tborder = {\n" .. table.concat(border, ",\n") .. "\n\t}")
end
local counts = {}
for k,count in pairs(chunk.count) do
actual = 0
if k == 'cell' then
actual = #cells
else
for _,cell in pairs(chunk.border[k]) do
actual = actual + (cell and 1 or 0)
end
end
table.insert(counts, string.format("\t\t%s = "..PRETTYCHECK, direction[k] or k, check(count, actual)))
end
table.insert(strings, "\tcount = {\n" .. table.concat(counts, ",\n") .. "\n\t}")
return "HeatChunk{\n" .. table.concat(strings, ",\n") .. "\n}"
end
function dbgHeatNetwork(network)
local strings = {}
table.insert(strings, "\tid = " .. network.id)
local chunks = {}
local cell_count = 0
for x,xs in pairs(network.chunks) do
for z,chunk in pairs(xs) do
table.insert(chunks, "\t\t" .. string.gsub(dbgHeatChunk(chunk), "\n", "\n\t\t"))
for _,cell in pairs(chunk.cells) do
cell_count = cell_count + 1
end
end
end
local reactors = {}
for _,reactor in pairs(network.reactors) do
table.insert(reactors, "\t\t" .. dbgReactorPosition(reactor))
end
local counts = {}
for k,count in pairs(network.count) do
actual = 0
if k == 'chunk' then
actual = #chunks
elseif k == 'cell' then
actual = cell_count
elseif k == 'reactor' then
actual = #reactors
end
table.insert(counts, string.format("\t\t%s = "..PRETTYCHECK, k, check(count, actual)))
end
if isempty(chunks) then
table.insert(strings, "\tchunks = {}")
else
table.insert(strings, "\tchunks = {\n" .. table.concat(chunks, ",\n") .. "\n\t}")
end
if isempty(reactors) then
table.insert(strings, "\treactors = {}")
else
table.insert(strings, "\treactors = {\n" .. table.concat(reactors, ",\n") .. "\n\t}")
end
table.insert(strings, "\tcount = {\n" .. table.concat(counts, ",\n") .. "\n\t}")
return "HeatNetwork{\n" .. table.concat(strings, ",\n") .. "\n}"
end
-- debug rendering
local WIDTH = 2 -- in pixels
local F = 0.5 - WIDTH/32 -- in tiles
local function debug_entity(cell, x,y, entity, color)
local ids = {}
if not entity.valid then return ids end
local p = entity.position
local surface = entity.surface
cell_nodes(cell.entities)(entity,function (other,q)
local o = {x=q.x+0.5, y=q.y+0.5}
if o.x - p.x > 0 or o.y - p.y > 0 or entity.type == 'reactor' then
table.insert(ids, rendering.draw_line{
surface = surface,
color = color,
width = WIDTH,
from = p,
to = o,
})
end
end)
table.insert(ids, rendering.draw_text{
surface = entity.surface,
color = color,
target = {p.x-0.3, p.y-0.2},
text = tostring(cell.chunk.network.id),
})
return ids
end
local function debug_cell(cell, color)
local ids = {}
for x,xs in pairs(cell.entities) do
for y,entity in pairs(xs) do
for _,id in ipairs(debug_entity(cell, x,y, entity, color)) do
table.insert(ids, id)
end
end
end
return ids
end
local function debug_chunk(chunk, color)
local surface = game.surfaces[chunk.position.z]
local ids = {}
for cell in pairs(chunk.cells) do
for _,id in ipairs(debug_cell(cell, color)) do
table.insert(ids, id)
end
end
for d,border in pairs(chunk.border) do
local corner,k = next(M[d])
local a = chunk.area[corner][k]
local o = O[d]
for c,cell in pairs(border) do
local p = {[k]=a+0.5, [V[k]]=c+0.5}
table.insert(ids, rendering.draw_line{
surface = surface,
color = color,
width = WIDTH,
from = {p.x-o.y*F+o.x*F, p.y-o.x*F+o.y*F},
to = {p.x+o.y*F+o.x*F, p.y+o.x*F+o.y*F},
})
end
end
return ids
end
local function debug_reactor(reactor, color)
local p = reactor.position
local surface = game.surfaces[reactor.cell.chunk.position.z]
local entity = surface.find_entity(reactor.name, p)
local area = entity and entity.bounding_box
if not area then return {} end
local ids = {rendering.draw_rectangle{
surface = surface,
color = color,
filled = false,
left_top = area.left_top,
right_bottom = area.right_bottom,
}}
table.insert(ids, rendering.draw_text{
surface = surface,
color = color,
target = {p.x-0.3, p.y-0.2},
text = tostring(reactor.cell.chunk.network.id),
})
return ids
end
local function debug_network(network, color)
if HEAT.debug_logging ~= false then log(dbgHeatNetwork(network)) end
local ids = {}
for x,xs in pairs(network.chunks) do
for y,chunk in pairs(xs) do
for _,id in ipairs(debug_chunk(chunk, color)) do
table.insert(ids, id)
end
end
end
for _,reactor in pairs(network.reactors) do
for _,id in ipairs(debug_reactor(reactor, color)) do
table.insert(ids, id)
end
end
return ids
end
local function HSL(h, s, l, a)
if s<=0 then return l,l,l,a end
h, s, l = h*6, s, l
local c = (1-math.abs(2*l-1))*s
local x = (1-math.abs(h%2-1))*c
local m,r,g,b = (l-.5*c), 0,0,0
if h < 1 then r,g,b = c,x,0
elseif h < 2 then r,g,b = x,c,0
elseif h < 3 then r,g,b = 0,c,x
elseif h < 4 then r,g,b = 0,x,c
elseif h < 5 then r,g,b = x,0,c
else r,g,b = c,0,x
end return (r+m),(g+m),(b+m),a
end
local function random_color(id)
-- return {r=global.random(),g=global.random(),b=global.random(),a=1}
local r,g,b = HSL(((id * 5) % 23) / 23, 0.9, 0.5)
return {r=r,g=g,b=b,a=1}
end
local function remove_debug(id, debug)
HEAT.debug[id] = nil
for _,id in ipairs(debug.ids) do
rendering.destroy(id)
end
debug.ids = {}
end
local function create_debug(network)
local id = network.id
if not HEAT.network[id] then return end
local debug = HEAT.debug[id] or {color=random_color(id)}
debug.ids = debug_network(network, debug.color)
HEAT.debug[id] = debug
end
local function debug_cleanup()
if not HEAT.debug then return end
for id,debug in pairs(HEAT.debug) do
remove_debug(id, debug)
end
HEAT.debug = nil
end
local function debug_heat()
if HEAT.debug then debug_cleanup() end
HEAT.debug = {}
for _,network in pairs(HEAT.network) do
create_debug(network)
end
end
local function update_debug()
if not HEAT.debug then return end
if isempty(HEAT.network) then log("no networks") end
debug_heat()
end
local function debug_log(enabled)
HEAT.debug_logging = enabled
debug_heat()
end
return { -- exports
-- create = create_debug,
update = update_debug,
-- remove = remove_debug,
-- toggle
enable = debug_heat,
disable = debug_cleanup,
logging = debug_log,
}