///|
fn json_escape(text : String) -> String {
let out = StringBuilder()
for ch in text {
match ch {
'"' => out.write_string("\\\"")
'\\' => out.write_string("\\\\")
'\n' => out.write_string("\\n")
'\r' => out.write_string("\\r")
'\t' => out.write_string("\\t")
_ => out.write_char(ch)
}
}
out.to_string()
}
///|
/// Quote CSV fields only when their content requires it.
fn csv_escape(text : String) -> String {
let needs_quotes = text.contains(",") ||
text.contains("\"") ||
text.contains("\n") ||
text.contains("\r")
if !needs_quotes {
return text
}
let out = StringBuilder()
out.write_char('"')
for ch in text {
if ch == '"' {
out.write_string("\"\"")
} else {
out.write_char(ch)
}
}
out.write_char('"')
out.to_string()
}
///|
fn algorithm_name(algorithm : Algorithm) -> String {
match algorithm {
ShelfFirstFit => "shelf-first-fit"
GuillotineBestArea => "guillotine-best-area"
SkylineBottomLeft => "skyline-bottom-left"
}
}
///|
/// Export a deterministic JSON manifest. The output avoids external JSON
/// dependencies so it works in small WASM examples.
pub fn to_json_manifest(layout : Layout) -> String {
let out = StringBuilder()
out.write_string("{\n")
out.write_string(
" \"bin\": { \"width\": \{layout.bin.width}, \"height\": \{layout.bin.height} },\n",
)
out.write_string(
" \"algorithm\": \"\{algorithm_name(layout.algorithm)}\",\n",
)
out.write_string(" \"occupancy_permille\": \{layout.occupancy_permille},\n")
out.write_string(" \"waste_area\": \{layout.waste_area},\n")
out.write_string(" \"placements\": [\n")
for i in 0.. String {
let out = StringBuilder()
out.write_string(
"rectloom layout \{layout.bin.width}x\{layout.bin.height} using \{algorithm_name(layout.algorithm)}\n",
)
out.write_string(
"placed=\{layout.placements.length()} unpacked=\{layout.unpacked.length()} occupancy=\{layout.occupancy_permille}/1000 waste=\{layout.waste_area}\n",
)
for p in layout.placements {
let marker = if p.rotated { " rotated" } else { "" }
out.write_string(
"- \{p.id}: x=\{p.x} y=\{p.y} w=\{p.width} h=\{p.height}\{marker}\n",
)
}
if !layout.unpacked.is_empty() {
out.write_string("unpacked:\n")
for r in layout.unpacked {
out.write_string("- \{r.id}: \{r.width}x\{r.height}\n")
}
}
out.to_string()
}