///|
/// Canonical encoding recomputes derived counts, bounds, width and point offset.
/// Unknown VLR payloads, gap bytes and per-point extra bytes remain opaque.
pub fn encode(
doc : Document,
limits~ : Limits = Limits::default(),
) -> Bytes raise LasError {
doc.validate(limits~)
let canonical = doc.with_points(doc.points, limits~)
let h = canonical.header
let out : Array[Byte] = []
append(out, b"LASF")
put16(out, h.file_source_id)
put16(out, h.global_encoding)
append(out, h.project_id)
put8(out, 1)
put8(out, h.minor)
put_text(out, h.system, 32)
put_text(out, h.software, 32)
put16(out, h.day)
put16(out, h.year)
put16(out, if h.minor == 2 { 227 } else { 375 })
put32(out, h.point_offset)
put32(out, h.vlr_count)
put8(out, h.format)
put16(out, h.record_length)
put32(out, if h.minor == 2 { h.point_count } else { 0 })
for i in 0..<5 {
put32(out, if h.minor == 2 { h.by_return[i] } else { 0 })
}
let t = h.transform
for value in [t.sx, t.sy, t.sz, t.ox, t.oy, t.oz] {
putf64(out, value)
}
let (xmin, ymin, zmin, xmax, ymax, zmax) = h.bounds
for value in [xmax, xmin, ymax, ymin, zmax, zmin] {
putf64(out, value)
}
if h.minor == 4 {
put64(out, 0)
put64(out, 0)
put32(out, 0)
put64(out, h.point_count)
for count in h.by_return {
put64(out, count)
}
}
append(out, encode_vlrs(canonical.vlrs))
append(out, canonical.gap)
for p in canonical.points {
append(out, write_point(p, h.format))
}
bytes_of(out)
}
///|
fn validate_header_fields(h : Header) -> Unit raise LasError {
require(
h.file_source_id >= 0 && h.file_source_id <= 65535,
"invalid file source ID",
)
require(
h.day >= 0 && h.day <= 366 && h.year >= 0 && h.year <= 65535,
"invalid creation date",
)
let allowed = if h.minor == 2 { 1 } else { 25 }
require(
h.global_encoding >= 0 && (h.global_encoding & allowed) == h.global_encoding,
"unsupported global encoding bits",
)
require(
h.by_return.length() == (if h.minor == 2 { 5 } else { 15 }),
"invalid return histogram length",
)
for n in h.by_return {
require(n >= 0, "negative return histogram")
}
let probe : Array[Byte] = []
put_text(probe, h.system, 32)
put_text(probe, h.software, 32)
let (x, y, z, xx, yy, zz) = h.bounds
for v in [x, y, z, xx, yy, zz] {
require(finite(v), "nonfinite header bounds")
}
require(x <= xx && y <= yy && z <= zz, "reversed header bounds")
}