FloorMaterialAnalyzer/internal/validation/validation.go

71 lines
2.8 KiB
Go
Raw Normal View History

2026-07-27 03:03:51 +00:00
package validation
import (
"fmt"
"time"
"materialanalyzer/internal/model"
)
func ValidateAsset(asset model.MaterialAsset, hist model.ColorHistogram) model.ValidationReport {
report := model.ValidationReport{
Status: "valid",
CheckedAt: time.Now().UTC().Format(time.RFC3339),
}
checkRange(&report, "visual.brightness", asset.Visual.Brightness, 0, 1)
checkRange(&report, "visual.contrast", asset.Visual.Contrast, 0, 1)
checkRange(&report, "visual.saturation", asset.Visual.Saturation, 0, 1)
checkRange(&report, "texture.orientation", asset.Texture.PrimaryOrientationDeg, 0, 180)
if asset.Texture.Entropy <= 0 {
report.Errors = append(report.Errors, "texture.entropy must be > 0")
}
if len(hist.Values) != 256 {
report.Errors = append(report.Errors, fmt.Sprintf("histogram length = %d, want 256", len(hist.Values)))
}
for _, vector := range asset.Embeddings.Vectors {
if vector.Dimensions <= 0 {
report.Errors = append(report.Errors, "embedding "+vector.Name+" has invalid dimension")
}
if vector.ByteLength != int64(vector.Dimensions*4) {
report.Errors = append(report.Errors, fmt.Sprintf("embedding %s byte length = %d, want %d", vector.Name, vector.ByteLength, vector.Dimensions*4))
}
if vector.Model == "dinov2-large" && vector.Dimensions != 1024 {
report.Errors = append(report.Errors, "dinov2-large dimension must be 1024")
}
if vector.Model == "ViT-L/14" && vector.Dimensions != 768 {
report.Errors = append(report.Errors, "ViT-L/14 dimension must be 768")
}
}
if asset.Semantic.Description == "" {
report.Errors = append(report.Errors, "semantic.description is required")
}
if asset.Semantic.MaterialType == "" {
report.Errors = append(report.Errors, "semantic.material_type is required")
}
if asset.Semantic.SurfaceFinish == "" {
report.Errors = append(report.Errors, "semantic.surface_finish is required")
}
if asset.Semantic.ColorFamily == "" {
report.Errors = append(report.Errors, "semantic.color_family is required")
}
if asset.Semantic.Confidence <= 0 || asset.Semantic.Confidence > 1 {
report.Errors = append(report.Errors, "semantic.confidence must be in (0,1]")
}
if len(asset.Canonical.BrightnessDistribution) != asset.Canonical.BrightnessHistogramBins {
report.Warnings = append(report.Warnings, "canonical brightness histogram bin count mismatch")
}
if len(asset.Canonical.GradientHistogram) != asset.Canonical.GradientHistogramBins {
report.Warnings = append(report.Warnings, "canonical gradient histogram bin count mismatch")
}
if len(report.Errors) > 0 {
report.Status = "invalid"
}
return report
}
func checkRange(report *model.ValidationReport, name string, value, min, max float64) {
if value < min || value > max {
report.Errors = append(report.Errors, fmt.Sprintf("%s = %.4f, want [%.2f,%.2f]", name, value, min, max))
}
}