///|
/// Stable export helpers for track audit and downstream analytics.
pub fn track_observation_csv(observation : TrackObservation) -> String {
observation.point_id.to_string() +
"," +
observation.view_id.to_string() +
"," +
observation.pixel.x.to_string() +
"," +
observation.pixel.y.to_string()
}
///|
pub fn track_observations_csv(
observations : ArrayView[TrackObservation],
) -> String {
let mut result = "point_id,view_id,x,y"
for observation in observations {
result += "\n" + track_observation_csv(observation)
}
result
}
///|
pub fn track_observation_checksum(observation : TrackObservation) -> Double {
observation.point_id.to_double() * 31.0 +
observation.view_id.to_double() * 17.0 +
observation.pixel.x * 7.0 +
observation.pixel.y * 13.0
}
///|
pub fn track_checksum(observations : ArrayView[TrackObservation]) -> Double {
let mut result = 0.0
for i in 0.. Int {
if observations.length() == 0 {
0
} else {
let mut result = observations[0].view_id
for observation in observations {
result = result.min(observation.view_id)
}
result
}
}
///|
pub fn track_view_last(observations : ArrayView[TrackObservation]) -> Int {
let mut result = 0
for observation in observations {
result = result.max(observation.view_id)
}
result
}
///|
pub fn track_point_first(observations : ArrayView[TrackObservation]) -> Int {
if observations.length() == 0 {
0
} else {
let mut result = observations[0].point_id
for observation in observations {
result = result.min(observation.point_id)
}
result
}
}
///|
pub fn track_point_last(observations : ArrayView[TrackObservation]) -> Int {
let mut result = 0
for observation in observations {
result = result.max(observation.point_id)
}
result
}
///|
pub fn track_view_span(observations : ArrayView[TrackObservation]) -> Int {
track_view_last(observations) - track_view_first(observations)
}
///|
pub fn track_point_span(observations : ArrayView[TrackObservation]) -> Int {
track_point_last(observations) - track_point_first(observations)
}
///|
pub fn track_density(observations : ArrayView[TrackObservation]) -> Double {
let views = track_view_span(observations) + 1
let points = track_point_span(observations) + 1
if views <= 0 || points <= 0 {
0.0
} else {
observations.length().to_double() / (views * points).to_double()
}
}
///|
pub fn track_has_view(
observations : ArrayView[TrackObservation],
view : Int,
) -> Bool {
for observation in observations {
if observation.view_id == view {
return true
}
}
false
}
///|
pub fn track_has_point(
observations : ArrayView[TrackObservation],
point : Int,
) -> Bool {
for observation in observations {
if observation.point_id == point {
return true
}
}
false
}
///|
pub fn track_count_view(
observations : ArrayView[TrackObservation],
view : Int,
) -> Int {
let mut result = 0
for observation in observations {
if observation.view_id == view {
result += 1
}
}
result
}
///|
pub fn track_count_point(
observations : ArrayView[TrackObservation],
point : Int,
) -> Int {
let mut result = 0
for observation in observations {
if observation.point_id == point {
result += 1
}
}
result
}
///|
pub fn track_duplicate_pair_count(
observations : ArrayView[TrackObservation],
) -> Int {
let mut result = 0
for i in 0.. Double {
if observations.length() == 0 {
0.0
} else {
1.0 -
track_duplicate_pair_count(observations).to_double() /
observations.length().to_double()
}
}
///|
pub fn track_pixel_centroid(
observations : ArrayView[TrackObservation],
) -> @core.Point2 {
if observations.length() == 0 {
@core.Point2::new(x=0.0, y=0.0)
} else {
let mut x = 0.0
let mut y = 0.0
for observation in observations {
x += observation.pixel.x
y += observation.pixel.y
}
@core.Point2::new(
x=x / observations.length().to_double(),
y=y / observations.length().to_double(),
)
}
}
///|
pub fn track_pixel_radius(observations : ArrayView[TrackObservation]) -> Double {
let center = track_pixel_centroid(observations)
let mut result = 0.0
for observation in observations {
result = result.max(observation.pixel.minus(center).norm())
}
result
}
///|
pub fn track_pixel_density(
observations : ArrayView[TrackObservation],
) -> Double {
let radius = track_pixel_radius(observations)
if radius <= 0.0 {
0.0
} else {
observations.length().to_double() / (3.141592653589793 * radius * radius)
}
}
///|
pub fn track_export_quality(
observations : ArrayView[TrackObservation],
minimum_views : Int,
maximum_motion : Double,
) -> Double {
track_unique_pair_ratio(observations) *
track_view_density(observations, track_view_span(observations) + 1) *
track_quality_batch_score(observations, minimum_views, maximum_motion)
}