fix: preserve canonical color semantics after QBIN decode

This commit is contained in:
tei
2026-09-21 15:52:22 +09:00
parent a4cb727b16
commit 3f7ec99f85
6 changed files with 177 additions and 68 deletions

View File

@@ -124,8 +124,8 @@ particle::ResolvedFieldProfileContract windContract() {
result.profileVersion = 1;
result.requiredPlanes = {
{1, particle::FieldValueClass::VelocityComponent, 1, 1, particle::FieldComponent::UEast, true},
{2, particle::FieldValueClass::VelocityComponent, 1, 1, particle::FieldComponent::VNorth, true},
{3, particle::FieldValueClass::Validity, 0, 0, particle::FieldComponent::None, true}};
{2, particle::FieldValueClass::VelocityComponent, 1, 1, particle::FieldComponent::VNorth, true},
{3, particle::FieldValueClass::Validity, 0, 0, particle::FieldComponent::None, true}};
return result;
}
particle::ResolvedFieldProfileContract waveContract() {
@@ -414,6 +414,7 @@ TEST(Source, RasterArrayV4RendererStateMaterializesBindingsAndMixWithoutTouching
EXPECT_EQ(particles.t0->vector, (std::vector<float>{1, 5, 2, 6, 3, 7, 4, 8}));
EXPECT_EQ(particles.t0->valid, (std::vector<uint8_t>{1, 1, 1, 1}));
EXPECT_EQ(color.t0->scalar, (std::vector<float>{1, 2, 3, 4}));
EXPECT_EQ(color.valueSource, particle::MaterializedColorValueSource::Scalar);
const auto particleIdentity = particles.contentIdentity;
const auto retainedParticleT0 = particles.t0;
ASSERT_TRUE(source.setRequestedForecastTime(Base + Hour / 4));
@@ -429,6 +430,46 @@ TEST(Source, RasterArrayV4RendererStateMaterializesBindingsAndMixWithoutTouching
EXPECT_FALSE(source.impl().v4RendererState.particle);
EXPECT_FALSE(source.impl().v4RendererState.color);
}
TEST(Source, RasterArrayV4VelocityColorReconstructsDecodedUVIndependentlyOfParticles) {
style::RasterArraySource source{"v4-vector-color"};
submit(source, "wind", "wind", Base, 1);
submit(source, "wind", "wind", Base + Hour, 2);
source.setColorBinding(
{"wind", {1, 1, particle::FieldComponent::UEast}, particle::FieldValueClass::VelocityComponent, 1});
ASSERT_TRUE(source.setRequestedForecastTime(Base + Hour / 2));
ASSERT_TRUE(source.impl().v4RendererState.color);
EXPECT_FALSE(source.impl().v4RendererState.particle);
const auto& colorWithoutParticles = *source.impl().v4RendererState.color;
EXPECT_EQ(colorWithoutParticles.valueSource, particle::MaterializedColorValueSource::VectorMagnitude);
ASSERT_TRUE(colorWithoutParticles.t0);
ASSERT_TRUE(colorWithoutParticles.t1);
EXPECT_TRUE(colorWithoutParticles.t0->hasVector());
EXPECT_FALSE(colorWithoutParticles.t0->hasScalar());
EXPECT_EQ(colorWithoutParticles.t0->vector, (std::vector<float>{1, 5, 2, 6, 3, 7, 4, 8}));
EXPECT_EQ(colorWithoutParticles.t0->valid, (std::vector<uint8_t>{1, 1, 1, 1}));
EXPECT_FLOAT_EQ(colorWithoutParticles.sampleState.mix, 0.5f);
source.setParticleBinding({particle::ParticleBindingMode::VectorComponents,
"wind",
{1, 1, particle::FieldComponent::UEast},
{2, 1, particle::FieldComponent::VNorth},
{},
1});
ASSERT_TRUE(source.impl().v4RendererState.color);
ASSERT_TRUE(source.impl().v4RendererState.particle);
const auto& color = *source.impl().v4RendererState.color;
const auto& particles = *source.impl().v4RendererState.particle;
EXPECT_EQ(color.valueSource, particle::MaterializedColorValueSource::VectorMagnitude);
EXPECT_EQ(color.t0->vector, particles.t0->vector);
EXPECT_EQ(color.t1->vector, particles.t1->vector);
EXPECT_EQ(color.t0->valid, particles.t0->valid);
EXPECT_FALSE(source.impl().field);
EXPECT_FALSE(source.impl().nextField);
EXPECT_FALSE(source.impl().timeline);
}
TEST(Source, RasterArrayV4MaterializerUsesExplicitLittleEndianScaleOffsetAndStableRevision) {
const auto t0 = materializerFrame("scalar",
Base,
@@ -457,8 +498,8 @@ TEST(Source, RasterArrayV4MaterializerUsesExplicitLittleEndianScaleOffsetAndStab
particle::ColorBinding binding{
"scalar", {99, 0, particle::FieldComponent::None}, particle::FieldValueClass::Scalar, 77};
particle::ColorProjection projection;
projection.contentIdentity =
{binding, {"scalar", Base}, {"scalar", Base + Hour}, "plane-t0", "plane-t1", "validity-t0", "validity-t1"};
projection.contentIdentity = {
binding, {"scalar", Base}, {"scalar", Base + Hour}, "plane-t0", "plane-t1", "validity-t0", "validity-t1"};
projection.sampleState = {Base + Hour / 2, 0.5f};
projection.t0Frame = t0;
projection.t1Frame = t1;