Report the number of colors that had to be clipped.
authorMartin Lambers <marlam@marlam.de>
Fri, 12 Feb 2016 14:54:18 +0000 (15:54 +0100)
committerMartin Lambers <marlam@marlam.de>
Fri, 12 Feb 2016 14:54:18 +0000 (15:54 +0100)
cmdline.cpp
colormap.cpp
colormap.hpp
colormapwidgets.cpp
colormapwidgets.hpp
gui.cpp
gui.hpp

index e49702f..beef679 100644 (file)
@@ -364,53 +364,55 @@ int main(int argc, char* argv[])
     }
 
     std::vector<unsigned char> colormap(3 * n);
+    int clipped;
     switch (type) {
     case brewer_seq:
-        ColorMap::BrewerSequential(n, &(colormap[0]), hue, contrast, saturation, brightness, warmth);
+        clipped = ColorMap::BrewerSequential(n, &(colormap[0]), hue, contrast, saturation, brightness, warmth);
         break;
     case brewer_div:
-        ColorMap::BrewerDiverging(n, &(colormap[0]), hue, divergence, contrast, saturation, brightness, warmth);
+        clipped = ColorMap::BrewerDiverging(n, &(colormap[0]), hue, divergence, contrast, saturation, brightness, warmth);
         break;
     case brewer_qual:
-        ColorMap::BrewerQualitative(n, &(colormap[0]), hue, divergence, contrast, saturation, brightness);
+        clipped = ColorMap::BrewerQualitative(n, &(colormap[0]), hue, divergence, contrast, saturation, brightness);
         break;
     case plseq_lightness:
-        ColorMap::PLSequentialLightness(n, &(colormap[0]), saturation, hue);
+        clipped = ColorMap::PLSequentialLightness(n, &(colormap[0]), saturation, hue);
         break;
     case plseq_saturation:
-        ColorMap::PLSequentialSaturation(n, &(colormap[0]), lightness, saturation, hue);
+        clipped = ColorMap::PLSequentialSaturation(n, &(colormap[0]), lightness, saturation, hue);
         break;
     case plseq_rainbow:
-        ColorMap::PLSequentialRainbow(n, &(colormap[0]), hue, rotations, saturation);
+        clipped = ColorMap::PLSequentialRainbow(n, &(colormap[0]), hue, rotations, saturation);
         break;
     case plseq_blackbody:
-        ColorMap::PLSequentialBlackBody(n, &(colormap[0]), temperature, range, saturation);
+        clipped = ColorMap::PLSequentialBlackBody(n, &(colormap[0]), temperature, range, saturation);
         break;
     case pldiv_lightness:
-        ColorMap::PLDivergingLightness(n, &(colormap[0]), lightness, saturation, hue, divergence);
+        clipped = ColorMap::PLDivergingLightness(n, &(colormap[0]), lightness, saturation, hue, divergence);
         break;
     case pldiv_saturation:
-        ColorMap::PLDivergingSaturation(n, &(colormap[0]), lightness, saturation, hue, divergence);
+        clipped = ColorMap::PLDivergingSaturation(n, &(colormap[0]), lightness, saturation, hue, divergence);
         break;
     case plqual_hue:
-        ColorMap::PLQualitativeHue(n, &(colormap[0]), lightness, saturation, hue);
+        clipped = ColorMap::PLQualitativeHue(n, &(colormap[0]), lightness, saturation, hue);
         break;
     case cubehelix:
-        ColorMap::CubeHelix(n, &(colormap[0]), hue, rotations, saturation, gamma);
+        clipped = ColorMap::CubeHelix(n, &(colormap[0]), hue, rotations, saturation, gamma);
         break;
     case moreland:
-        ColorMap::Moreland(n, &(colormap[0]),
+        clipped = ColorMap::Moreland(n, &(colormap[0]),
                 color0[0], color0[1], color0[2],
                 color1[0], color1[1], color1[2]);
         break;
     case mcnames:
-        ColorMap::McNames(n, &(colormap[0]), periods);
+        clipped = ColorMap::McNames(n, &(colormap[0]), periods);
         break;
     }
 
     for (int i = 0; i < n; i++) {
         printf("%d, %d, %d\n", colormap[3 * i + 0], colormap[3 * i + 1], colormap[3 * i + 2]);
     }
+    fprintf(stderr, "%d color(s) were clipped\n", clipped);
 
     return 0;
 }
index 5e87ac7..2307e1d 100644 (file)
@@ -223,43 +223,38 @@ static triplet rgb_to_xyz(triplet rgb)
             (0.0193f * rgb.r + 0.1192f * rgb.g + 0.9505f * rgb.b));
 }
 
-static triplet xyz_to_rgb(triplet xyz, bool constrain = true, bool* constrained = NULL)
+static triplet xyz_to_rgb(triplet xyz)
 {
-    triplet rgb = 0.01f * triplet(
+    return 0.01f * triplet(
             (+3.2406255f * xyz.x - 1.5372080f * xyz.y - 0.4986286f * xyz.z),
             (-0.9689307f * xyz.x + 1.8757561f * xyz.y + 0.0415175f * xyz.z),
             (+0.0557101f * xyz.x - 0.2040211f * xyz.y + 1.0569959f * xyz.z));
-    if (constrain) {
-        float mini = std::min(rgb.r, std::min(rgb.g, rgb.b));
-        if (mini < 0.0f) {
-            rgb.r -= mini;
-            rgb.g -= mini;
-            rgb.b -= mini;
-            if (constrained)
-                *constrained = true;
-        }
-        float maxi = std::max(rgb.r, std::max(rgb.g, rgb.b));
-        if (maxi > 1.0f) {
-            rgb.r -= maxi - 1.0f;
-            rgb.g -= maxi - 1.0f;
-            rgb.b -= maxi - 1.0f;
-            if (constrained)
-                *constrained = true;
-        }
-        if (rgb.r < 0.0f || rgb.g < 0.0f || rgb.b < 0.0f
-                || rgb.r > 1.0f || rgb.g > 1.0f || rgb.b > 1.0f) {
-            rgb.r = clamp(rgb.r, 0.0f, 1.0f);
-            rgb.g = clamp(rgb.g, 0.0f, 1.0f);
-            rgb.b = clamp(rgb.b, 0.0f, 1.0f);
-            if (constrained)
-                *constrained = true;
-        }
-    }
-    return rgb;
 }
 
 /* Color space conversion: RGB <-> sRGB */
 
+static triplet clip_rgb(triplet rgb, bool* clipped = NULL)
+{
+    if (clipped)
+        *clipped = false;
+    if (rgb.r < 0.0f || rgb.r > 1.0f) {
+        rgb.r = clamp(rgb.r, 0.0f, 1.0f);
+        if (clipped)
+            *clipped = true;
+    }
+    if (rgb.g < 0.0f || rgb.g > 1.0f) {
+        rgb.g = clamp(rgb.g, 0.0f, 1.0f);
+        if (clipped)
+            *clipped = true;
+    }
+    if (rgb.b < 0.0f || rgb.b > 1.0f) {
+        rgb.b = clamp(rgb.b, 0.0f, 1.0f);
+        if (clipped)
+            *clipped = true;
+    }
+    return rgb;
+}
+
 static float rgb_to_srgb_helper(float x)
 {
     return (x <= 0.0031308f ? (x * 12.92f) : (1.055f * std::pow(x, 1.0f / 2.4f) - 0.055f));
@@ -282,22 +277,29 @@ static triplet srgb_to_rgb(triplet srgb)
 
 /* Helpers for the conversion to colormap entries */
 
-static void xyz_to_colormap(triplet xyz, unsigned char* colormap)
+static bool xyz_to_colormap(triplet xyz, unsigned char* colormap)
 {
-    triplet srgb = rgb_to_srgb(xyz_to_rgb(xyz));
+    bool clipped;
+    triplet srgb = rgb_to_srgb(clip_rgb(xyz_to_rgb(xyz), &clipped));
     colormap[0] = float_to_uchar(srgb.r);
     colormap[1] = float_to_uchar(srgb.g);
     colormap[2] = float_to_uchar(srgb.b);
+    return clipped;
+}
+
+static bool luv_to_colormap(triplet luv, unsigned char* colormap)
+{
+    return xyz_to_colormap(luv_to_xyz(luv), colormap);
 }
 
-static void luv_to_colormap(triplet luv, unsigned char* colormap)
+static bool lch_to_colormap(triplet lch, unsigned char* colormap)
 {
-    xyz_to_colormap(luv_to_xyz(luv), colormap);
+    return luv_to_colormap(lch_to_luv(lch), colormap);
 }
 
-static void lch_to_colormap(triplet lch, unsigned char* colormap)
+static bool lab_to_colormap(triplet lab, unsigned char* colormap)
 {
-    luv_to_colormap(lch_to_luv(lch), colormap);
+    return xyz_to_colormap(lab_to_xyz(lab), colormap);
 }
 
 /* Various helpers */
@@ -449,7 +451,7 @@ float BrewerSequentialDefaultContrastForSmallN(int n)
     return std::min(0.88f, 0.34f + 0.06f * n);
 }
 
-void BrewerSequential(int n, unsigned char* colormap, float hue,
+int BrewerSequential(int n, unsigned char* colormap, float hue,
         float contrast, float saturation, float brightness, float warmth)
 {
     triplet pb, p0, p1, p2, q0, q1, q2;
@@ -458,11 +460,14 @@ void BrewerSequential(int n, unsigned char* colormap, float hue,
     float pbs = lch_saturation(pb_lch.l, pb_lch.c);
     get_color_points(hue, saturation, warmth, pb, pb_lch.h, pbs, &p0, &p1, &p2, &q0, &q1, &q2);
 
+    int clipped = 0;
     for (int i = 0; i < n; i++) {
         float t = i / (n - 1.0f);
         triplet c = get_colormap_entry(t, p0, p2, q0, q1, q2, contrast, brightness);
-        luv_to_colormap(c, colormap + 3 * i);
+        if (luv_to_colormap(c, colormap + 3 * i))
+            clipped++;
     }
+    return clipped;
 }
 
 float BrewerDivergingDefaultContrastForSmallN(int n)
@@ -470,7 +475,7 @@ float BrewerDivergingDefaultContrastForSmallN(int n)
     return std::min(0.88f, 0.34f + 0.06f * n);
 }
 
-void BrewerDiverging(int n, unsigned char* colormap, float hue, float divergence,
+int BrewerDiverging(int n, unsigned char* colormap, float hue, float divergence,
         float contrast, float saturation, float brightness, float warmth)
 {
     float hue1 = hue + divergence;
@@ -486,6 +491,7 @@ void BrewerDiverging(int n, unsigned char* colormap, float hue, float divergence
     get_color_points(hue,  saturation, warmth, pb, pb_lch.h, pbs, &p00, &p01, &p02, &q00, &q01, &q02);
     get_color_points(hue1, saturation, warmth, pb, pb_lch.h, pbs, &p10, &p11, &p12, &q10, &q11, &q12);
 
+    int clipped = 0;
     for (int i = 0; i < n; i++) {
         triplet c;
         if (n % 2 == 1 && i == n / 2) {
@@ -514,11 +520,13 @@ void BrewerDiverging(int n, unsigned char* colormap, float hue, float divergence
                 c = get_colormap_entry(tt, p10, p12, q10, q11, q12, contrast, brightness);
             }
         }
-        luv_to_colormap(c, colormap + 3 * i);
+        if (luv_to_colormap(c, colormap + 3 * i))
+            clipped++;
     }
+    return clipped;
 }
 
-void BrewerQualitative(int n, unsigned char* colormap, float hue, float divergence,
+int BrewerQualitative(int n, unsigned char* colormap, float hue, float divergence,
         float contrast, float saturation, float brightness)
 {
     // Get all information about yellow
@@ -541,6 +549,7 @@ void BrewerQualitative(int n, unsigned char* colormap, float hue, float divergen
     float l1 = (1.0f - contrast) * l0;
 
     // Generate colors
+    int clipped = 0;
     for (int i = 0; i < n; i++) {
         float t = i / (n - 1.0f);
         float ch = std::fmod(twopi * (eps + t * r), twopi);
@@ -548,47 +557,58 @@ void BrewerQualitative(int n, unsigned char* colormap, float hue, float divergen
         float cl = (1.0f - alpha) * l0 + alpha * l1;
         float cs = std::min(s_max(cl, ch), saturation * rs);
         triplet c = lch_to_luv(triplet(cl, lch_chroma(cl, cs), ch));
-        luv_to_colormap(c, colormap + 3 * i);
+        if (luv_to_colormap(c, colormap + 3 * i))
+            clipped++;
     }
+    return clipped;
 }
 
 /* Perceptually linear (PL) */
 
-void PLSequentialLightness(int n, unsigned char* colormap, float saturation, float hue)
+int PLSequentialLightness(int n, unsigned char* colormap, float saturation, float hue)
 {
     triplet lch;
     lch.h = hue;
+    int clipped = 0;
     for (int i = 0; i < n; i++) {
         float t = i / (n - 1.0f);
         lch.l = t * 100.0f;
         lch.c = lch_chroma(lch.l, saturation * 5.0f * (1.0f - t));
-        lch_to_colormap(lch, colormap + 3 * i);
+        if (lch_to_colormap(lch, colormap + 3 * i))
+            clipped++;
     }
+    return clipped;
 }
 
-void PLSequentialSaturation(int n, unsigned char* colormap,
+int PLSequentialSaturation(int n, unsigned char* colormap,
         float lightness, float saturation, float hue)
 {
     triplet lch;
     lch.l = lightness * 100.0f;
     lch.h = hue;
+    int clipped = 0;
     for (int i = 0; i < n; i++) {
         float t = i / (n - 1.0f);
         lch.c = lch_chroma(lch.l, saturation * 5.0f * (1.0f - t));
-        lch_to_colormap(lch, colormap + 3 * i);
+        if (lch_to_colormap(lch, colormap + 3 * i))
+            clipped++;
     }
+    return clipped;
 }
 
-void PLSequentialRainbow(int n, unsigned char* colormap, float hue, float rotations, float saturation)
+int PLSequentialRainbow(int n, unsigned char* colormap, float hue, float rotations, float saturation)
 {
-    triplet luv, lch;
+    triplet lch;
+    int clipped = 0;
     for (int i = 0; i < n; i++) {
         float t = i / (n - 1.0f);
         lch.l = t * 100.0f;
         lch.c = lch_chroma(lch.l, (1.0f - t) * saturation);
         lch.h = hue + t * rotations * twopi;
-        lch_to_colormap(lch, colormap + 3 * i);
+        if (lch_to_colormap(lch, colormap + 3 * i))
+            clipped++;
     }
+    return clipped;
 }
 
 static float plancks_law(float temperature, float lambda)
@@ -722,8 +742,9 @@ static triplet color_matching_function(int lambda /* in nanometers */)
     return xyz;
 }
 
-void PLSequentialBlackBody(int n, unsigned char* colormap, float temperature, float range, float saturation)
+int PLSequentialBlackBody(int n, unsigned char* colormap, float temperature, float range, float saturation)
 {
+    int clipped = 0;
     for (int i = 0; i < n; i++) {
         float fract = i / (n - 1.0f);
         // Black body temperature for this color map entry
@@ -742,49 +763,60 @@ void PLSequentialBlackBody(int n, unsigned char* colormap, float temperature, fl
         triplet lch = luv_to_lch(xyz_to_luv(adjust_y(xyz, 10.0f)));
         lch.l = fract * 100.0f;
         lch.c = lch_chroma(lch.l, (1.0f - fract) * saturation);
-        lch_to_colormap(lch, colormap + 3 * i);
+        if (lch_to_colormap(lch, colormap + 3 * i))
+            clipped++;
     }
+    return clipped;
 }
 
-void PLDivergingLightness(int n, unsigned char* colormap, float lightness, float saturation, float hue, float divergence)
+int PLDivergingLightness(int n, unsigned char* colormap, float lightness, float saturation, float hue, float divergence)
 {
     triplet lch;
+    int clipped = 0;
     for (int i = 0; i < n; i++) {
         float t = i / (n - 1.0f);
         lch.l = 100.0f * lightness + 100.0f * (1.0f - lightness) * (t <= 0.5f ? 2.0f * t : 2.0f * (1.0f - t));
         float s = saturation * 5.0f * (t <= 0.5f ? 2.0f * (0.5f - t) : 2.0f * (t - 0.5f));
         lch.c = lch_chroma(lch.l, s);
         lch.h = (t <= 0.5f ? hue : hue + divergence);
-        lch_to_colormap(lch, colormap + 3 * i);
+        if (lch_to_colormap(lch, colormap + 3 * i))
+            clipped++;
     }
+    return clipped;
 }
 
-void PLDivergingSaturation(int n, unsigned char* colormap,
+int PLDivergingSaturation(int n, unsigned char* colormap,
         float lightness, float saturation, float hue, float divergence)
 {
     triplet lch;
     lch.l = lightness * 100.0f;
+    int clipped = 0;
     for (int i = 0; i < n; i++) {
         float t = i / (n - 1.0f);
         float s = saturation * 5.0f * (t <= 0.5f ? 2.0f * (0.5f - t) : 2.0f * (t - 0.5f));
         lch.c = lch_chroma(lch.l, s);
         lch.h = (t <= 0.5f ? hue : hue + divergence);
-        lch_to_colormap(lch, colormap + 3 * i);
+        if (lch_to_colormap(lch, colormap + 3 * i))
+            clipped++;
     }
+    return clipped;
 }
 
-void PLQualitativeHue(int n, unsigned char* colormap,
+int PLQualitativeHue(int n, unsigned char* colormap,
         float lightness, float saturation, float hue)
 {
     float divergence = twopi * (n - 1.0f) / n;
     triplet lch;
     lch.l = lightness * 100.0f;
     lch.c = lch_chroma(lch.l, saturation * 5.0f);
+    int clipped = 0;
     for (int i = 0; i < n; i++) {
         float t = i / (n - 1.0f);
         lch.h = hue + t * divergence;
-        lch_to_colormap(lch, colormap + 3 * i);
+        if (lch_to_colormap(lch, colormap + 3 * i))
+            clipped++;
     }
+    return clipped;
 }
 
 /* CubeHelix */
@@ -792,7 +824,7 @@ void PLQualitativeHue(int n, unsigned char* colormap,
 int CubeHelix(int n, unsigned char* colormap, float hue,
         float rot, float saturation, float gamma)
 {
-    int clippings = 0;
+    int clipped = 0;
     for (int i = 0; i < n; i++) {
         float fract = i / (n - 1.0f);
         float angle = twopi * (hue / 3.0f + 1.0f + rot * fract);
@@ -800,38 +832,19 @@ int CubeHelix(int n, unsigned char* colormap, float hue,
         float amp = saturation * fract * (1.0f - fract) / 2.0f;
         float s = std::sin(angle);
         float c = std::cos(angle);
-        float r = fract + amp * (-0.14861f * c + 1.78277f * s);
-        float g = fract + amp * (-0.29227f * c - 0.90649f * s);
-        float b = fract + amp * (1.97294f * c);
-        bool clipped = false;
-        if (r < 0.0f) {
-            r = 0.0f;
-            clipped = true;
-        } else if (r > 1.0f) {
-            r = 1.0f;
-            clipped = true;
-        }
-        if (g < 0.0f) {
-            g = 0.0f;
-            clipped = true;
-        } else if (g > 1.0f) {
-            g = 1.0f;
-            clipped = true;
-        }
-        if (b < 0.0f) {
-            b = 0.0f;
-            clipped = true;
-        } else if (b > 1.0f) {
-            b = 1.0f;
-            clipped = true;
-        }
-        if (clipped)
-            clippings++;
-        colormap[3 * i + 0] = float_to_uchar(r);
-        colormap[3 * i + 1] = float_to_uchar(g);
-        colormap[3 * i + 2] = float_to_uchar(b);
+        triplet srgb(
+                fract + amp * (-0.14861f * c + 1.78277f * s),
+                fract + amp * (-0.29227f * c - 0.90649f * s),
+                fract + amp * (1.97294f * c));
+        bool clipped_;
+        srgb = clip_rgb(srgb, &clipped_);
+        if (clipped_)
+            clipped++;
+        colormap[3 * i + 0] = float_to_uchar(srgb.r);
+        colormap[3 * i + 1] = float_to_uchar(srgb.g);
+        colormap[3 * i + 2] = float_to_uchar(srgb.b);
     }
-    return clippings;
+    return clipped;
 }
 
 /* Moreland */
@@ -867,7 +880,7 @@ static float adjust_hue(triplet msh, float unsaturated_m)
     }
 }
 
-void Moreland(int n, unsigned char* colormap,
+int Moreland(int n, unsigned char* colormap,
         unsigned char sr0, unsigned char sg0, unsigned char sb0,
         unsigned char sr1, unsigned char sg1, unsigned char sb1)
 {
@@ -878,6 +891,7 @@ void Moreland(int n, unsigned char* colormap,
     bool place_white = (omsh0.s >= 0.05f && omsh1.s >= 0.05f && hue_diff(omsh0.h, omsh1.h) > pi / 3.0f);
     float mmid = std::max(std::max(omsh0.m, omsh1.m), 88.0f);
 
+    int clipped = 0;
     for (int i = 0; i < n; i++) {
         triplet msh0 = omsh0;
         triplet msh1 = omsh1;
@@ -901,11 +915,10 @@ void Moreland(int n, unsigned char* colormap,
             msh1.h = adjust_hue(msh0, msh1.m);
         }
         triplet msh = (1.0f - t) * msh0 + t * msh1;
-        triplet srgb = rgb_to_srgb(xyz_to_rgb(lab_to_xyz(msh_to_lab(msh))));
-        colormap[3 * i + 0] = float_to_uchar(srgb.r);
-        colormap[3 * i + 1] = float_to_uchar(srgb.g);
-        colormap[3 * i + 2] = float_to_uchar(srgb.b);
+        if (lab_to_colormap(msh_to_lab(msh), colormap + 3 * i))
+            clipped++;
     }
+    return clipped;
 }
 
 /* McNames */
@@ -940,12 +953,13 @@ static float windowfunc(float t)
 #endif
 }
 
-void McNames(int n, unsigned char* colormap, float periods)
+int McNames(int n, unsigned char* colormap, float periods)
 {
     static const float sqrt3 = std::sqrt(3.0f);
     static const float a12 = std::asin(1.0f / sqrt3);
     static const float a23 = pi / 4.0f;
 
+    int clipped = 0;
     for (int i = 0; i < n; i++) {
         float t = 1.0f - i / (n - 1.0f);
         float w = windowfunc(t);
@@ -964,10 +978,15 @@ void McNames(int n, unsigned char* colormap, float periods)
         pol2cart(ag + a23, rd, &r2, &b2);
         g2 = g1;
 
-        colormap[3 * i + 0] = float_to_uchar(clamp(r2, 0.0f, 1.0f));
-        colormap[3 * i + 1] = float_to_uchar(clamp(g2, 0.0f, 1.0f));
-        colormap[3 * i + 2] = float_to_uchar(clamp(b2, 0.0f, 1.0f));
+        bool clipped_;
+        triplet srgb = clip_rgb(triplet(r2, g2, b2), &clipped_);
+        if (clipped_)
+            clipped++;
+        colormap[3 * i + 0] = float_to_uchar(srgb.r);
+        colormap[3 * i + 1] = float_to_uchar(srgb.g);
+        colormap[3 * i + 2] = float_to_uchar(srgb.b);
     }
+    return clipped;
 }
 
 }
index d9eb98e..2aeeaf7 100644 (file)
@@ -31,6 +31,8 @@
  *   contain.
  * - Allocate memory for you color map (3 * unsigned char for each color entry).
  * - Call the function that generates your color map.
+ *   The return value is always the number of colors that had to be clipped
+ *   to fit into sRGB; you want to keep that number low by adjusting parameters.
  *
  * All colors are represented as unsigned char sRGB triplets, with each value in
  * [0,255].
@@ -54,7 +56,7 @@ const float BrewerSequentialDefaultSaturation = 0.6f;
 const float BrewerSequentialDefaultBrightness = 0.75f;
 const float BrewerSequentialDefaultWarmth = 0.15f;
 
-void BrewerSequential(int n, unsigned char* srgb_colormap,
+int BrewerSequential(int n, unsigned char* srgb_colormap,
         float hue = BrewerSequentialDefaultHue,
         float contrast = BrewerSequentialDefaultContrast,
         float saturation = BrewerSequentialDefaultSaturation,
@@ -74,7 +76,7 @@ const float BrewerDivergingDefaultSaturation = 0.6f;
 const float BrewerDivergingDefaultBrightness = 0.75f;
 const float BrewerDivergingDefaultWarmth = 0.15f;
 
-void BrewerDiverging(int n, unsigned char* srgb_colormap,
+int BrewerDiverging(int n, unsigned char* srgb_colormap,
         float hue = BrewerDivergingDefaultHue,
         float divergence = BrewerDivergingDefaultDivergence,
         float contrast = BrewerDivergingDefaultContrast,
@@ -93,7 +95,7 @@ const float BrewerQualitativeDefaultContrast = 0.5f;
 const float BrewerQualitativeDefaultSaturation = 0.5f;
 const float BrewerQualitativeDefaultBrightness = 0.8f;
 
-void BrewerQualitative(int n, unsigned char* colormap,
+int BrewerQualitative(int n, unsigned char* colormap,
         float hue = BrewerQualitativeDefaultHue,
         float divergence = BrewerQualitativeDefaultDivergence,
         float contrast = BrewerQualitativeDefaultContrast,
@@ -118,7 +120,7 @@ void BrewerQualitative(int n, unsigned char* colormap,
 const float PLSequentialLightnessDefaultSaturation = 0.5f;
 const float PLSequentialLightnessDefaultHue = 0.349065850399f; // 20 deg
 
-void PLSequentialLightness(int n, unsigned char* colormap,
+int PLSequentialLightness(int n, unsigned char* colormap,
         float saturation = PLSequentialLightnessDefaultSaturation,
         float hue = PLSequentialLightnessDefaultHue);
 
@@ -128,7 +130,7 @@ const float PLSequentialSaturationDefaultLightness = 0.5f;
 const float PLSequentialSaturationDefaultSaturation = 0.5f;
 const float PLSequentialSaturationDefaultHue = 0.349065850399f; // 20 deg
 
-void PLSequentialSaturation(int n, unsigned char* colormap,
+int PLSequentialSaturation(int n, unsigned char* colormap,
         float lightness = PLSequentialSaturationDefaultLightness,
         float saturation = PLSequentialSaturationDefaultSaturation,
         float hue = PLSequentialSaturationDefaultHue);
@@ -139,7 +141,7 @@ const float PLSequentialRainbowDefaultHue = 0.0f;
 const float PLSequentialRainbowDefaultRotations = -1.5f;
 const float PLSequentialRainbowDefaultSaturation = 1.2f;
 
-void PLSequentialRainbow(int n, unsigned char* colormap,
+int PLSequentialRainbow(int n, unsigned char* colormap,
         float hue = PLSequentialRainbowDefaultHue,
         float rotations = PLSequentialRainbowDefaultRotations,
         float saturation = PLSequentialRainbowDefaultSaturation);
@@ -153,7 +155,7 @@ const float PLSequentialBlackBodyDefaultTemperature = 250.0f;
 const float PLSequentialBlackBodyDefaultRange = 6250.0f;
 const float PLSequentialBlackBodyDefaultSaturation = 2.5f;
 
-void PLSequentialBlackBody(int n, unsigned char* colormap,
+int PLSequentialBlackBody(int n, unsigned char* colormap,
         float temperature = PLSequentialBlackBodyDefaultTemperature,
         float range = PLSequentialBlackBodyDefaultRange,
         float saturation = PLSequentialBlackBodyDefaultSaturation);
@@ -167,7 +169,7 @@ const float PLDivergingLightnessDefaultSaturation = 0.5f;
 const float PLDivergingLightnessDefaultHue = 0.349065850399f; // 20 deg
 const float PLDivergingLightnessDefaultDivergence = 4.18879020479f; // 2/3 * 2PI
 
-void PLDivergingLightness(int n, unsigned char* colormap,
+int PLDivergingLightness(int n, unsigned char* colormap,
         float lightness = PLDivergingLightnessDefaultLightness,
         float saturation = PLDivergingLightnessDefaultSaturation,
         float hue = PLDivergingLightnessDefaultHue,
@@ -180,7 +182,7 @@ const float PLDivergingSaturationDefaultSaturation = 0.5f;
 const float PLDivergingSaturationDefaultHue = 0.349065850399f; // 20 deg
 const float PLDivergingSaturationDefaultDivergence = 4.18879020479f; // 2/3 * 2PI
 
-void PLDivergingSaturation(int n, unsigned char* colormap,
+int PLDivergingSaturation(int n, unsigned char* colormap,
         float lightness = PLDivergingSaturationDefaultLightness,
         float saturation = PLDivergingSaturationDefaultSaturation,
         float hue = PLDivergingSaturationDefaultHue,
@@ -192,7 +194,7 @@ const float PLQualitativeHueDefaultLightness = 0.55f;
 const float PLQualitativeHueDefaultSaturation = 0.35f;
 const float PLQualitativeHueDefaultHue = 0.0f;
 
-void PLQualitativeHue(int n, unsigned char* colormap,
+int PLQualitativeHue(int n, unsigned char* colormap,
         float lightness = PLQualitativeHueDefaultLightness,
         float saturation = PLQualitativeHueDefaultSaturation,
         float hue = PLQualitativeHueDefaultHue);
@@ -237,7 +239,7 @@ const unsigned char MorelandDefaultR1 = 59;
 const unsigned char MorelandDefaultG1 = 76;
 const unsigned char MorelandDefaultB1 = 192;
 
-void Moreland(int n, unsigned char* colormap,
+int Moreland(int n, unsigned char* colormap,
         unsigned char sr0 = MorelandDefaultR0,
         unsigned char sg0 = MorelandDefaultG0,
         unsigned char sb0 = MorelandDefaultB0,
@@ -258,7 +260,7 @@ void Moreland(int n, unsigned char* colormap,
 
 const float McNamesDefaultPeriods = 2.0f;
 
-void McNames(int n, unsigned char* colormap,
+int McNames(int n, unsigned char* colormap,
         float periods = McNamesDefaultPeriods);
 
 }
index 5bea625..2add96c 100644 (file)
@@ -153,9 +153,9 @@ ColorMapWidget::~ColorMapWidget()
 {
 }
 
-QImage ColorMapWidget::colorMapImage(int width, int height)
+QImage ColorMapWidget::colorMapImage(int width, int height, int* clipped)
 {
-    QVector<QColor> colormap = colorMap();
+    QVector<QColor> colormap = colorMap(clipped);
     if (width <= 0)
         width = colormap.size();
     if (height <= 0)
@@ -286,13 +286,15 @@ void ColorMapBrewerSequentialWidget::reset()
     update();
 }
 
-QVector<QColor> ColorMapBrewerSequentialWidget::colorMap() const
+QVector<QColor> ColorMapBrewerSequentialWidget::colorMap(int* clipped) const
 {
     int n;
     float h, c, s, b, w;
     parameters(n, h, c, s, b, w);
     QVector<unsigned char> colormap(3 * n);
-    ColorMap::BrewerSequential(n, colormap.data(), h, c, s, b, w);
+    int cl = ColorMap::BrewerSequential(n, colormap.data(), h, c, s, b, w);
+    if (clipped)
+        *clipped = cl;
     return toQColor(colormap);
 }
 
@@ -401,13 +403,15 @@ void ColorMapBrewerDivergingWidget::reset()
     update();
 }
 
-QVector<QColor> ColorMapBrewerDivergingWidget::colorMap() const
+QVector<QColor> ColorMapBrewerDivergingWidget::colorMap(int* clipped) const
 {
     int n;
     float h, d, c, s, b, w;
     parameters(n, h, d, c, s, b, w);
     QVector<unsigned char> colormap(3 * n);
-    ColorMap::BrewerDiverging(n, colormap.data(), h, d, c, s, b, w);
+    int cl = ColorMap::BrewerDiverging(n, colormap.data(), h, d, c, s, b, w);
+    if (clipped)
+        *clipped = cl;
     return toQColor(colormap);
 }
 
@@ -518,13 +522,15 @@ void ColorMapBrewerQualitativeWidget::reset()
     update();
 }
 
-QVector<QColor> ColorMapBrewerQualitativeWidget::colorMap() const
+QVector<QColor> ColorMapBrewerQualitativeWidget::colorMap(int* clipped) const
 {
     int n;
     float h, d, c, s, b;
     parameters(n, h, d, c, s, b);
     QVector<unsigned char> colormap(3 * n);
-    ColorMap::BrewerQualitative(n, colormap.data(), h, d, c, s, b);
+    int cl = ColorMap::BrewerQualitative(n, colormap.data(), h, d, c, s, b);
+    if (clipped)
+        *clipped = cl;
     return toQColor(colormap);
 }
 
@@ -601,13 +607,15 @@ void ColorMapPLSequentialLightnessWidget::reset()
     update();
 }
 
-QVector<QColor> ColorMapPLSequentialLightnessWidget::colorMap() const
+QVector<QColor> ColorMapPLSequentialLightnessWidget::colorMap(int* clipped) const
 {
     int n;
     float s, h;
     parameters(n, s, h);
     QVector<unsigned char> colormap(3 * n);
-    ColorMap::PLSequentialLightness(n, colormap.data(), s, h);
+    int cl = ColorMap::PLSequentialLightness(n, colormap.data(), s, h);
+    if (clipped)
+        *clipped = cl;
     return toQColor(colormap);
 }
 
@@ -688,13 +696,15 @@ void ColorMapPLSequentialSaturationWidget::reset()
     update();
 }
 
-QVector<QColor> ColorMapPLSequentialSaturationWidget::colorMap() const
+QVector<QColor> ColorMapPLSequentialSaturationWidget::colorMap(int* clipped) const
 {
     int n;
     float l, s, h;
     parameters(n, l, s, h);
     QVector<unsigned char> colormap(3 * n);
-    ColorMap::PLSequentialSaturation(n, colormap.data(), l, s, h);
+    int cl = ColorMap::PLSequentialSaturation(n, colormap.data(), l, s, h);
+    if (clipped)
+        *clipped = cl;
     return toQColor(colormap);
 }
 
@@ -776,13 +786,15 @@ void ColorMapPLSequentialRainbowWidget::reset()
     update();
 }
 
-QVector<QColor> ColorMapPLSequentialRainbowWidget::colorMap() const
+QVector<QColor> ColorMapPLSequentialRainbowWidget::colorMap(int* clipped) const
 {
     int n;
     float h, r, s;
     parameters(n, h, r, s);
     QVector<unsigned char> colormap(3 * n);
-    ColorMap::PLSequentialRainbow(n, colormap.data(), h, r, s);
+    int cl = ColorMap::PLSequentialRainbow(n, colormap.data(), h, r, s);
+    if (clipped)
+        *clipped = cl;
     return toQColor(colormap);
 }
 
@@ -865,13 +877,15 @@ void ColorMapPLSequentialBlackBodyWidget::reset()
     update();
 }
 
-QVector<QColor> ColorMapPLSequentialBlackBodyWidget::colorMap() const
+QVector<QColor> ColorMapPLSequentialBlackBodyWidget::colorMap(int* clipped) const
 {
     int n;
     float t, r, s;
     parameters(n, t, r, s);
     QVector<unsigned char> colormap(3 * n);
-    ColorMap::PLSequentialBlackBody(n, colormap.data(), t, r, s);
+    int cl = ColorMap::PLSequentialBlackBody(n, colormap.data(), t, r, s);
+    if (clipped)
+        *clipped = cl;
     return toQColor(colormap);
 }
 
@@ -961,13 +975,15 @@ void ColorMapPLDivergingLightnessWidget::reset()
     update();
 }
 
-QVector<QColor> ColorMapPLDivergingLightnessWidget::colorMap() const
+QVector<QColor> ColorMapPLDivergingLightnessWidget::colorMap(int* clipped) const
 {
     int n;
     float l, s, h, d;
     parameters(n, l, s, h, d);
     QVector<unsigned char> colormap(3 * n);
-    ColorMap::PLDivergingLightness(n, colormap.data(), l, s, h, d);
+    int cl = ColorMap::PLDivergingLightness(n, colormap.data(), l, s, h, d);
+    if (clipped)
+        *clipped = cl;
     return toQColor(colormap);
 }
 
@@ -1058,13 +1074,15 @@ void ColorMapPLDivergingSaturationWidget::reset()
     update();
 }
 
-QVector<QColor> ColorMapPLDivergingSaturationWidget::colorMap() const
+QVector<QColor> ColorMapPLDivergingSaturationWidget::colorMap(int* clipped) const
 {
     int n;
     float l, s, h, d;
     parameters(n, l, s, h, d);
     QVector<unsigned char> colormap(3 * n);
-    ColorMap::PLDivergingSaturation(n, colormap.data(), l, s, h, d);
+    int cl = ColorMap::PLDivergingSaturation(n, colormap.data(), l, s, h, d);
+    if (clipped)
+        *clipped = cl;
     return toQColor(colormap);
 }
 
@@ -1147,13 +1165,15 @@ void ColorMapPLQualitativeHueWidget::reset()
     update();
 }
 
-QVector<QColor> ColorMapPLQualitativeHueWidget::colorMap() const
+QVector<QColor> ColorMapPLQualitativeHueWidget::colorMap(int* clipped) const
 {
     int n;
     float l, s, h;
     parameters(n, l, s, h);
     QVector<unsigned char> colormap(3 * n);
-    ColorMap::PLQualitativeHue(n, colormap.data(), l, s, h);
+    int cl = ColorMap::PLQualitativeHue(n, colormap.data(), l, s, h);
+    if (clipped)
+        *clipped = cl;
     return toQColor(colormap);
 }
 
@@ -1243,13 +1263,15 @@ void ColorMapCubeHelixWidget::reset()
     update();
 }
 
-QVector<QColor> ColorMapCubeHelixWidget::colorMap() const
+QVector<QColor> ColorMapCubeHelixWidget::colorMap(int* clipped) const
 {
     int n;
     float h, r, s, g;
     parameters(n, h, r, s, g);
     QVector<unsigned char> colormap(3 * n);
-    ColorMap::CubeHelix(n, colormap.data(), h, r, s, g);
+    int cl = ColorMap::CubeHelix(n, colormap.data(), h, r, s, g);
+    if (clipped)
+        *clipped = cl;
     return toQColor(colormap);
 }
 
@@ -1359,13 +1381,15 @@ void ColorMapMorelandWidget::reset()
     update();
 }
 
-QVector<QColor> ColorMapMorelandWidget::colorMap() const
+QVector<QColor> ColorMapMorelandWidget::colorMap(int* clipped) const
 {
     int n;
     unsigned char r0, g0, b0, r1, g1, b1;
     parameters(n, r0, g0, b0, r1, g1, b1);
     QVector<unsigned char> colormap(3 * n);
-    ColorMap::Moreland(n, colormap.data(), r0, g0, b0, r1, g1, b1);
+    int cl = ColorMap::Moreland(n, colormap.data(), r0, g0, b0, r1, g1, b1);
+    if (clipped)
+        *clipped = cl;
     return toQColor(colormap);
 }
 
@@ -1438,13 +1462,15 @@ void ColorMapMcNamesWidget::reset()
     update();
 }
 
-QVector<QColor> ColorMapMcNamesWidget::colorMap() const
+QVector<QColor> ColorMapMcNamesWidget::colorMap(int* clipped) const
 {
     int n;
     float p;
     parameters(n, p);
     QVector<unsigned char> colormap(3 * n);
-    ColorMap::McNames(n, colormap.data(), p);
+    int cl = ColorMap::McNames(n, colormap.data(), p);
+    if (clipped)
+        *clipped = cl;
     return toQColor(colormap);
 }
 
index ac42732..a9f95fe 100644 (file)
@@ -70,12 +70,14 @@ public:
     /* Reset all values to their method-specific defaults */
     virtual void reset() = 0;
 
-    /* Get the color map corresponding to the current values as a vector of colors */
-    virtual QVector<QColor> colorMap() const = 0;
+    /* Get the color map corresponding to the current values as a vector of colors.
+     * Also return the number of clipped colors unless 'clipped' is NULL. */
+    virtual QVector<QColor> colorMap(int* clipped = NULL) const = 0;
 
     /* Get a color map as an image of the specified size. If width or height is zero,
-     * then it will be set to the number of colors in the color map. */
-    QImage colorMapImage(int width, int height);
+     * then it will be set to the number of colors in the color map.
+     * Also return the number of clipped colors unless 'clipped' is NULL. */
+    QImage colorMapImage(int width, int height, int* clipped = NULL);
 
     /* Get a rich text string containing the relevant literature reference for this method */
     virtual QString reference() const = 0;
@@ -103,7 +105,7 @@ public:
     ~ColorMapBrewerSequentialWidget();
 
     void reset() override;
-    QVector<QColor> colorMap() const override;
+    QVector<QColor> colorMap(int* clipped = NULL) const override;
     QString reference() const override;
     void parameters(int& n, float& hue,
             float& contrast, float& saturation, float& brightness, float& warmth) const;
@@ -129,7 +131,7 @@ public:
     ~ColorMapBrewerDivergingWidget();
 
     void reset() override;
-    QVector<QColor> colorMap() const override;
+    QVector<QColor> colorMap(int* clipped = NULL) const override;
     QString reference() const override;
     void parameters(int& n, float& hue, float& divergence,
             float& contrast, float& saturation, float& brightness, float& warmth) const;
@@ -154,7 +156,7 @@ public:
     ~ColorMapBrewerQualitativeWidget();
 
     void reset() override;
-    QVector<QColor> colorMap() const override;
+    QVector<QColor> colorMap(int* clipped = NULL) const override;
     QString reference() const override;
     void parameters(int& n, float& hue, float& divergence,
             float& contrast, float& saturation, float& brightness) const;
@@ -176,7 +178,7 @@ public:
     ~ColorMapPLSequentialLightnessWidget();
 
     void reset() override;
-    QVector<QColor> colorMap() const override;
+    QVector<QColor> colorMap(int* clipped = NULL) const override;
     QString reference() const override;
     void parameters(int& n, float& saturation, float& hue) const;
 };
@@ -198,7 +200,7 @@ public:
     ~ColorMapPLSequentialSaturationWidget();
 
     void reset() override;
-    QVector<QColor> colorMap() const override;
+    QVector<QColor> colorMap(int* clipped = NULL) const override;
     QString reference() const override;
     void parameters(int& n, float& lightness, float& saturation, float& hue) const;
 };
@@ -220,7 +222,7 @@ public:
     ~ColorMapPLSequentialRainbowWidget();
 
     void reset() override;
-    QVector<QColor> colorMap() const override;
+    QVector<QColor> colorMap(int* clipped = NULL) const override;
     QString reference() const override;
     void parameters(int& n, float& hue, float& rotations, float& saturation) const;
 };
@@ -242,7 +244,7 @@ public:
     ~ColorMapPLSequentialBlackBodyWidget();
 
     void reset() override;
-    QVector<QColor> colorMap() const override;
+    QVector<QColor> colorMap(int* clipped = NULL) const override;
     QString reference() const override;
     void parameters(int& n, float& temperature, float& range, float& saturation) const;
 };
@@ -265,7 +267,7 @@ public:
     ~ColorMapPLDivergingLightnessWidget();
 
     void reset() override;
-    QVector<QColor> colorMap() const override;
+    QVector<QColor> colorMap(int* clipped = NULL) const override;
     QString reference() const override;
     void parameters(int& n, float& lightness, float& saturation, float& hue, float& divergence) const;
 };
@@ -288,7 +290,7 @@ public:
     ~ColorMapPLDivergingSaturationWidget();
 
     void reset() override;
-    QVector<QColor> colorMap() const override;
+    QVector<QColor> colorMap(int* clipped = NULL) const override;
     QString reference() const override;
     void parameters(int& n, float& lightness, float& saturation, float& hue, float& divergence) const;
 };
@@ -310,7 +312,7 @@ public:
     ~ColorMapPLQualitativeHueWidget();
 
     void reset() override;
-    QVector<QColor> colorMap() const override;
+    QVector<QColor> colorMap(int* clipped = NULL) const override;
     QString reference() const override;
     void parameters(int& n, float& lightness, float& saturation, float& hue) const;
 };
@@ -333,7 +335,7 @@ public:
     ~ColorMapCubeHelixWidget();
 
     void reset() override;
-    QVector<QColor> colorMap() const override;
+    QVector<QColor> colorMap(int* clipped = NULL) const override;
     QString reference() const override;
     void parameters(int& n, float& hue, float& rotations,
             float& saturation, float& gamma) const;
@@ -357,7 +359,7 @@ public:
     ~ColorMapMorelandWidget();
 
     void reset() override;
-    QVector<QColor> colorMap() const override;
+    QVector<QColor> colorMap(int* clipped = NULL) const override;
     QString reference() const override;
     void parameters(int& n,
             unsigned char& r0, unsigned char& g0, unsigned char& b0,
@@ -379,7 +381,7 @@ public:
     ~ColorMapMcNamesWidget();
 
     void reset() override;
-    QVector<QColor> colorMap() const override;
+    QVector<QColor> colorMap(int* clipped = NULL) const override;
     QString reference() const override;
     void parameters(int& n, float& p) const;
 };
diff --git a/gui.cpp b/gui.cpp
index 6597cf3..279f39f 100644 (file)
--- a/gui.cpp
+++ b/gui.cpp
@@ -99,11 +99,13 @@ GUI::GUI()
     _category_widget->addTab(_category_qual_widget, "Qualitative");
     connect(_category_widget, SIGNAL(currentChanged(int)), this, SLOT(update()));
     layout->addWidget(_category_widget, 0, 0);
-    layout->addItem(new QSpacerItem(0, 0), 1, 0);
+    _clipped_label = new QLabel("");
+    layout->addWidget(_clipped_label, 1, 0);
+    layout->addItem(new QSpacerItem(0, 0), 2, 0);
     _reference_label = new QLabel(_brewerseq_widget->reference());
     _reference_label->setWordWrap(true);
     _reference_label->setOpenExternalLinks(true);
-    layout->addWidget(_reference_label, 2, 0);
+    layout->addWidget(_reference_label, 3, 0);
 
     _colormap_label = new QLabel();
     _colormap_label->setScaledContents(true);
@@ -159,7 +161,9 @@ ColorMapWidget* GUI::currentWidget()
 void GUI::update()
 {
     _reference_label->setText(currentWidget()->reference());
-    _colormap_label->setPixmap(QPixmap::fromImage(currentWidget()->colorMapImage(32, _colormap_label->height())));
+    int clipped;
+    _colormap_label->setPixmap(QPixmap::fromImage(currentWidget()->colorMapImage(32, _colormap_label->height(), &clipped)));
+    _clipped_label->setText(QString("Colors clipped: %1").arg(clipped));
 }
 
 void GUI::file_export_png()
diff --git a/gui.hpp b/gui.hpp
index 3fb23f9..1b33b9f 100644 (file)
--- a/gui.hpp
+++ b/gui.hpp
@@ -70,6 +70,7 @@ private:
     QTabWidget* _category_seq_widget;
     QTabWidget* _category_div_widget;
     QTabWidget* _category_qual_widget;
+    QLabel* _clipped_label;
     QLabel* _reference_label;
     QLabel* _colormap_label;