///|
/// Generates a custom fiducial marker dictionary using a greedy packing search algorithm.
/// Guarantees that the minimum Hamming distance across all 4 rotations between any two markers is >= min_distance.
pub fn generate_custom_dictionary(
name : String,
grid_size : Int,
num_markers : Int,
min_hamming_distance : Int,
seed? : Int = 42,
) -> MarkerDictionary {
let payload_bits = grid_size * grid_size
let max_val = 1L << payload_bits
let codewords : Array[Int64] = []
let mut rng_state = seed.to_int64()
let next_rand = fn() -> Int64 {
rng_state = rng_state * 6364136223846793005L + 1442695040888963407L
if rng_state < 0L {
-rng_state
} else {
rng_state
}
}
let is_valid_codeword = fn(candidate : Int64) -> Bool {
// Avoid completely all-0 or all-1 patterns
if candidate == 0L || candidate == max_val - 1L {
return false
}
// Check self-rotational symmetry: distance between candidate and its rotations must be >= min_distance
let mut rot = candidate
for _r in 1..<4 {
rot = rotate_bits_90(rot, grid_size)
if compute_hamming_distance(candidate, rot) < min_hamming_distance {
return false
}
}
// Check cross-codeword distance against all existing codewords in all 4 rotations
for c in codewords {
let mut c_rot = c
for _r in 0..<4 {
if compute_hamming_distance(candidate, c_rot) < min_hamming_distance {
return false
}
c_rot = rotate_bits_90(c_rot, grid_size)
}
}
true
}
let max_trials = 100000
let mut trials = 0
while codewords.length() < num_markers && trials < max_trials {
trials += 1
let rand_bits = next_rand() % max_val
if is_valid_codeword(rand_bits) {
codewords.push(rand_bits)
}
}
let max_correction = (min_hamming_distance - 1) / 2
{
name,
grid_size,
payload_bits,
max_correction_bits: max_correction,
codewords,
}
}