class_name SonarSampleBuffer extends RefCounted const SonarMaterial = preload("res://game/domain/sonar/sonar_material.gd") const SonarSample = preload("res://game/domain/sonar/sonar_sample.gd") var columns: int var rows: int var _positions := PackedVector3Array() var _normals := PackedVector3Array() var _intensities := PackedFloat32Array() var _elevations_m := PackedFloat32Array() var _materials := PackedInt32Array() var _observed := PackedByteArray() var _sealed := false func _init(p_columns: int, p_rows: int) -> void: columns = p_columns rows = p_rows var sample_count := maxi(columns, 0) * maxi(rows, 0) _positions.resize(sample_count) _normals.resize(sample_count) _intensities.resize(sample_count) _elevations_m.resize(sample_count) _materials.resize(sample_count) _observed.resize(sample_count) for index in sample_count: _normals[index] = Vector3.UP _materials[index] = SonarMaterial.Value.UNKNOWN func set_sample(row: int, column: int, sample: SonarSample) -> String: if _sealed: return "sonar sample buffer is sealed" var index := _index_of(row, column) if index < 0: return "sonar sample coordinates are outside the buffer" if sample == null: return "sonar sample is missing" var error := sample.validation_error() if not error.is_empty(): return error if not sample.observed: return "cannot write an unobserved sonar sample" if _observed[index] == 1: return "sonar sample coordinates are duplicated" _positions[index] = sample.position_m _normals[index] = sample.normal.normalized() _intensities[index] = sample.intensity _elevations_m[index] = sample.elevation_m _materials[index] = sample.material _observed[index] = 1 return "" func seal() -> String: if columns < 2 or rows < 2: return "sonar sample buffer dimensions must be at least 2 by 2" _sealed = true return "" func is_sealed() -> bool: return _sealed func sample_count() -> int: return columns * rows func observed_count() -> int: var count := 0 for value: int in _observed: count += value return count func sample_at(row: int, column: int) -> SonarSample: var index := _index_of(row, column) if index < 0 or _observed[index] == 0: return SonarSample.unknown() return SonarSample.new( _positions[index], _normals[index], _intensities[index], _elevations_m[index], _materials[index], true ) func validation_error() -> String: if columns < 2 or rows < 2: return "sonar sample buffer dimensions must be at least 2 by 2" if not _sealed: return "sonar sample buffer must be sealed before publication" var expected_size := columns * rows if ( _positions.size() != expected_size or _normals.size() != expected_size or _intensities.size() != expected_size or _elevations_m.size() != expected_size or _materials.size() != expected_size or _observed.size() != expected_size ): return "sonar sample buffer arrays are not aligned" return "" func _index_of(row: int, column: int) -> int: if row < 0 or row >= rows or column < 0 or column >= columns: return -1 return row * columns + column