///|
pub fn require_non_empty_points(points : Array[Point]) -> Bool {
points.length() > 0
}
///|
pub fn require_line_points(points : Array[Point]) -> Bool {
points.length() >= 2 && distance(points[0], points[points.length() - 1]) > 0.0
}
///|
pub fn require_circle_points(points : Array[Point]) -> Bool {
points.length() >= 3 && polygon_area(points) != 0.0
}
///|
pub fn require_image(image : GrayImage) -> Bool {
image.width > 0 &&
image.height > 0 &&
image.pixels.length() == image.width * image.height
}
///|
pub fn require_mask(mask : BinaryMask) -> Bool {
mask.width > 0 &&
mask.height > 0 &&
mask.values.length() == mask.width * mask.height
}
///|
pub fn require_tolerance(tolerance : Tolerance) -> Bool {
tolerance.lower <= tolerance.nominal && tolerance.nominal <= tolerance.upper
}
///|
pub fn require_calibration(fit : CalibrationFit) -> Bool {
calibration_is_usable(fit, 1.0) &&
fit.transform.determinant().abs() > 0.0000001
}
///|
pub fn contract_diagnostics_image(
image : GrayImage,
) -> Array[PipelineDiagnostic] {
if require_image(image) {
[diagnostic_ok("contract", "image shape is valid")]
} else {
[diagnostic_error("contract", "image shape is invalid")]
}
}
///|
pub fn contract_diagnostics_mask(
mask : BinaryMask,
) -> Array[PipelineDiagnostic] {
if require_mask(mask) {
[diagnostic_ok("contract", "mask shape is valid")]
} else {
[diagnostic_error("contract", "mask shape is invalid")]
}
}
///|
pub fn contract_diagnostics_points(
points : Array[Point],
minimum : Int,
) -> Array[PipelineDiagnostic] {
if points.length() >= minimum {
[diagnostic_ok("contract", "point count is sufficient")]
} else {
[diagnostic_error("contract", "point count is insufficient")]
}
}
///|
pub fn contract_diagnostics_tolerance(
tolerance : Tolerance,
) -> Array[PipelineDiagnostic] {
if require_tolerance(tolerance) {
[diagnostic_ok("contract", "tolerance contains its nominal")]
} else {
[diagnostic_error("contract", "tolerance does not contain its nominal")]
}
}
///|
pub fn contract_diagnostics_calibration(
fit : CalibrationFit,
) -> Array[PipelineDiagnostic] {
if require_calibration(fit) {
[diagnostic_ok("contract", "calibration is usable")]
} else {
[diagnostic_error("contract", "calibration is degenerate or inaccurate")]
}
}
///|
pub fn safe_fit_line(points : Array[Point]) -> Line? {
if require_line_points(points) {
Some(fit_line(points))
} else {
None
}
}
///|
pub fn safe_fit_circle(points : Array[Point]) -> Circle? {
if require_circle_points(points) {
Some(detect_circle_from_points(points))
} else {
None
}
}
///|
pub fn safe_fit_affine(
source : Array[Point],
target : Array[Point],
) -> CalibrationFit? {
if source.length() >= 3 && source.length() == target.length() {
Some(calibration_fit(source, target))
} else {
None
}
}
///|
pub fn safe_measure_caliper(
image : GrayImage,
config : CaliperConfig,
tolerance : Tolerance,
) -> PipelineResult {
let diagnostics = contract_diagnostics_image(image)
diagnostics.append(validate_caliper_config(config))
diagnostics.append(contract_diagnostics_tolerance(tolerance))
if diagnostics_ok(diagnostics) {
measure_caliper(image, config, tolerance)
} else {
{ measurements: [], diagnostics, accepted: false, confidence: 0.0 }
}
}
///|
pub fn safe_label_components(mask : BinaryMask) -> Array[Component] {
if require_mask(mask) {
label_components(mask)
} else {
[]
}
}
///|
pub fn safe_contour_metrics(mask : BinaryMask) -> PolygonMetrics {
polygon_metrics(convex_hull(contour_points(mask)))
}
///|
pub fn safe_calibration_report(fit : CalibrationFit) -> String {
if require_calibration(fit) {
calibration_report(fit)
} else {
"invalid calibration"
}
}
///|
pub fn contract_summary(diagnostics : Array[PipelineDiagnostic]) -> String {
decision_name(diagnostic_severity(diagnostics)) +
":" +
diagnostics.length().to_string()
}
///|
pub fn contract_merge(
first : Array[PipelineDiagnostic],
second : Array[PipelineDiagnostic],
) -> Array[PipelineDiagnostic] {
let result = first
result.append(second)
result
}
///|
pub fn contract_passed(diagnostics : Array[PipelineDiagnostic]) -> Bool {
diagnostics_ok(diagnostics)
}
///|
pub fn stable_checksum(values : Array[Double]) -> Double {
let mut result = 0.0
for i in 0.. Double {
stable_checksum(image.pixels)
}
///|
pub fn stable_checksum_measurements(
measurements : Array[Measurement],
) -> Double {
stable_checksum(measurement_values(measurements))
}
///|
pub fn stable_checksum_batch(batch : BatchRun) -> Double {
stable_checksum_measurements(batch.records)
}
///|
pub fn deterministic_sort_values(values : Array[Double]) -> Array[Double] {
let result : Array[Double] = []
for value in values {
let mut index = 0
while index < result.length() && result[index] <= value {
index += 1
}
result.insert(index, value)
}
result
}
///|
pub fn deterministic_sort_measurements(
measurements : Array[Measurement],
) -> Array[Measurement] {
stable_measurement_order(measurements)
}
///|
pub fn deterministic_sort_components(
components : Array[Component],
) -> Array[Component] {
let result : Array[Component] = []
for component in components {
let mut index = 0
while index < result.length() && result[index].label <= component.label {
index += 1
}
result.insert(index, component)
}
result
}
///|
pub fn api_surface_summary() -> String {
"visual-caliper: image, profile, geometry, calibration, tolerance, quality, reports"
}