Skip to content
Permalink
Browse files
Fix hsl/hsv interpolation when hue is not available
  • Loading branch information
nyalldawson committed Nov 10, 2021
1 parent cb1a5e8 commit 19984d1c6f3b8c5ee365aac326aa04a235f68ecf
Showing with 102 additions and 40 deletions.
  1. +54 −40 src/core/qgscolorrampimpl.cpp
  2. +48 −0 tests/src/python/test_qgscolorramp.py
@@ -63,30 +63,37 @@ static QColor _interpolateHsv( const QColor &c1, const QColor &c2, const double
qreal hue1 = c1.hsvHueF();
qreal hue2 = c2.hsvHueF();
qreal hue;
switch ( direction )
if ( hue1 == -1 )
hue = hue2;
else if ( hue2 == -1 )
hue = hue1;
else
{
case Qgis::AngularDirection::Clockwise:
switch ( direction )
{
if ( hue1 < hue2 )
hue1 += 1;

hue = hue1 - value * ( hue1 - hue2 );
if ( hue < 0 )
hue += 1;
if ( hue > 1 )
hue -= 1;
break;
}
case Qgis::AngularDirection::Clockwise:
{
if ( hue1 < hue2 )
hue1 += 1;

hue = hue1 - value * ( hue1 - hue2 );
if ( hue < 0 )
hue += 1;
if ( hue > 1 )
hue -= 1;
break;
}

case Qgis::AngularDirection::CounterClockwise:
{
if ( hue2 < hue1 )
hue2 += 1;
case Qgis::AngularDirection::CounterClockwise:
{
if ( hue2 < hue1 )
hue2 += 1;

hue = hue1 + value * ( hue2 - hue1 );
if ( hue > 1 )
hue -= 1;
break;
hue = hue1 + value * ( hue2 - hue1 );
if ( hue > 1 )
hue -= 1;
break;
}
}
}

@@ -113,30 +120,37 @@ static QColor _interpolateHsl( const QColor &c1, const QColor &c2, const double
qreal hue1 = c1.hslHueF();
qreal hue2 = c2.hslHueF();
qreal hue;
switch ( direction )
if ( hue1 == -1 )
hue = hue2;
else if ( hue2 == -1 )
hue = hue1;
else
{
case Qgis::AngularDirection::Clockwise:
switch ( direction )
{
if ( hue1 < hue2 )
hue1 += 1;

hue = hue1 - value * ( hue1 - hue2 );
if ( hue < 0 )
hue += 1;
if ( hue > 1 )
hue -= 1;
break;
}
case Qgis::AngularDirection::Clockwise:
{
if ( hue1 < hue2 )
hue1 += 1;

hue = hue1 - value * ( hue1 - hue2 );
if ( hue < 0 )
hue += 1;
if ( hue > 1 )
hue -= 1;
break;
}

case Qgis::AngularDirection::CounterClockwise:
{
if ( hue2 < hue1 )
hue2 += 1;
case Qgis::AngularDirection::CounterClockwise:
{
if ( hue2 < hue1 )
hue2 += 1;

hue = hue1 + value * ( hue2 - hue1 );
if ( hue > 1 )
hue -= 1;
break;
hue = hue1 + value * ( hue2 - hue1 );
if ( hue > 1 )
hue -= 1;
break;
}
}
}

@@ -201,6 +201,48 @@ def testQgsGradientColorRamp(self):
self.assertEqual(r.color(0.95), QColor(30, 150, 60))
self.assertEqual(r.color(1), QColor(0, 200, 0))

# HSV based interpolation, invalid hues
rr = QgsGradientColorRamp(QColor.fromHsvF(-1, 0, 0.6, 1), QColor.fromHsvF(0.2, 0.7, 0.8, .5))
rr.setColorSpec(QColor.Hsv)
self.assertAlmostEqual(rr.color(0.5).hsvHueF(), 0.2, 3) # should take either avialable hue
self.assertAlmostEqual(rr.color(0.5).hsvSaturationF(), 0.350, 3)
self.assertAlmostEqual(rr.color(0.5).valueF(), 0.7, 3)
self.assertAlmostEqual(rr.color(0.5).alphaF(), 0.75, 3)
rr = QgsGradientColorRamp(QColor.fromHsvF(0.2, 0.7, 0.8, .5), QColor.fromHsvF(-1, 0, 0.6, 1))
rr.setColorSpec(QColor.Hsv)
self.assertAlmostEqual(rr.color(0.5).hsvHueF(), 0.2, 3) # should take either avialable hue
self.assertAlmostEqual(rr.color(0.5).hsvSaturationF(), 0.350, 3)
self.assertAlmostEqual(rr.color(0.5).valueF(), 0.7, 3)
self.assertAlmostEqual(rr.color(0.5).alphaF(), 0.75, 3)
# both invalid hue
rr = QgsGradientColorRamp(QColor.fromHsvF(-1, 0.7, 0.8, .5), QColor.fromHsvF(-1, 0, 0.6, 1))
rr.setColorSpec(QColor.Hsv)
self.assertEqual(rr.color(0.5).hsvHueF(), -1)
self.assertAlmostEqual(rr.color(0.5).hsvSaturationF(), 0.350, 3)
self.assertAlmostEqual(rr.color(0.5).valueF(), 0.7, 3)
self.assertAlmostEqual(rr.color(0.5).alphaF(), 0.75, 3)

# HSL based interpolation, invalid hues
rr = QgsGradientColorRamp(QColor.fromHslF(-1, 0, 0.6, 1), QColor.fromHslF(0.2, 0.7, 0.8, .5))
rr.setColorSpec(QColor.Hsl)
self.assertAlmostEqual(rr.color(0.5).hslHueF(), 0.2, 3) # should take either avialable hue
self.assertAlmostEqual(rr.color(0.5).hslSaturationF(), 0.350, 3)
self.assertAlmostEqual(rr.color(0.5).lightnessF(), 0.7, 3)
self.assertAlmostEqual(rr.color(0.5).alphaF(), 0.75, 3)
rr = QgsGradientColorRamp(QColor.fromHslF(0.2, 0.7, 0.8, .5), QColor.fromHslF(-1, 0, 0.6, 1))
rr.setColorSpec(QColor.Hsl)
self.assertAlmostEqual(rr.color(0.5).hslHueF(), 0.2, 3) # should take either avialable hue
self.assertAlmostEqual(rr.color(0.5).hslSaturationF(), 0.350, 3)
self.assertAlmostEqual(rr.color(0.5).lightnessF(), 0.7, 3)
self.assertAlmostEqual(rr.color(0.5).alphaF(), 0.75, 3)
# both invalid hue
rr = QgsGradientColorRamp(QColor.fromHslF(-1, 0.7, 0.8, .5), QColor.fromHslF(-1, 0, 0.6, 1))
rr.setColorSpec(QColor.Hsl)
self.assertEqual(rr.color(0.5).hslHueF(), -1)
self.assertAlmostEqual(rr.color(0.5).hslSaturationF(), 0.350, 3)
self.assertAlmostEqual(rr.color(0.5).lightnessF(), 0.7, 3)
self.assertAlmostEqual(rr.color(0.5).alphaF(), 0.75, 3)

# test setters
r.setColor1(QColor(0, 0, 200))
self.assertEqual(r.color1(), QColor(0, 0, 200))
@@ -223,6 +265,8 @@ def testQgsGradientColorRamp(self):
self.assertEqual(r.info()['key2'], 'val2')

# test creating from properties
r.setColorSpec(QColor.Hsv)
r.setDirection(Qgis.AngularDirection.Clockwise)
props = r.properties()
fromProps = QgsGradientColorRamp.create(props)
self.assertEqual(fromProps.color1(), QColor(0, 0, 200))
@@ -237,6 +281,8 @@ def testQgsGradientColorRamp(self):
self.assertEqual(fromProps.info()['key1'], 'val1')
self.assertEqual(fromProps.info()['key2'], 'val2')
self.assertEqual(fromProps.isDiscrete(), False)
self.assertEqual(fromProps.colorSpec(), QColor.Hsv)
self.assertEqual(fromProps.direction(), Qgis.AngularDirection.Clockwise)

# test cloning ramp
cloned = r.clone()
@@ -252,6 +298,8 @@ def testQgsGradientColorRamp(self):
self.assertEqual(cloned.info()['key1'], 'val1')
self.assertEqual(cloned.info()['key2'], 'val2')
self.assertEqual(cloned.isDiscrete(), False)
self.assertEqual(cloned.colorSpec(), QColor.Hsv)
self.assertEqual(cloned.direction(), Qgis.AngularDirection.Clockwise)

# test discrete ramps
# first with no stops

0 comments on commit 19984d1

Please sign in to comment.