A color legend shows the scalar range and how it’s mapped across the colormap, but it doesn’t tell you how values are distributed. This feature adds an optional distribution curve next to the continuous color bar so you can quickly see if your results are clustered, skewed, or multi-peak.
Why this additional information matters in practice:
-
Spot outliers vs. meaningful hotspots: a tiny tail reaching max often means “single-element spike”, while a visible high-end hump usually indicates a real region of high values.
-
See if the model is mostly “inactive”: a huge peak near the low end (often near 0) tells you most of the model sits at low values.
-
Validate your legend range: if most of the curve is crushed into a small part of the legend, you may be hiding detail with an overly wide range.
-
Detect multiple regimes: multiple peaks often indicate distinct behaviors (materials, contact vs. free regions, boundary condition regimes, etc.).
Example using Analyzer Desktop
Standard color legend:
Figure 1. Standard continuous color legend showing the temperature range and colormap mapping. While the color bar indicates minimum and maximum values, it provides no information about how much of the model occupies each part of the range.
Legend + distribution curve:
Figure 2. Continuous color legend with an attached scalar distribution curve. The filled curve shows how temperature values are distributed across the model, revealing that most values are clustered toward the higher end of the range, with relatively little volume at the extremes.
Figure 3. Distribution curve for a different state of the same result. Compared to Figure 2, the curve shape changes, indicating a shift in where values concentrate. This makes it easy to see whether changes over time affect the entire model or only localized regions.
How to use
The curve is defined by two arrays:
-
relativePositionsin [0..1] (positions along the legend) -
normalizedMagnitudesin [0..1] (curve heights)
You can either provide them directly, or compute them from scalar values.
Option A — Compute from scalar values
cee::ug::DistributionCurveConfig cfg;
cfg.setBinCount(50);
cfg.setConcentrationThreshold(0.5f);
cfg.setCurveWidth(2.0f);
auto dist = cee::ug::ScalarDistributionCalculator::compute(
values, undefinedCount, minValue, maxValue, cfg);
if (dist.valid)
legend->setDistributionCurve(dist.relativePositions, dist.normalizedMagnitudes);
Note: The values vector should contain only the defined scalar values. Pass the count of undefined values separately as undefinedCount.
Option B — Provide the curve samples directly
void setDistributionCurve(cee::vis::OverlayColorLegendContinuousDomain* legend,
const std::vector<float>& normalizedValues)
{
const int n = static_cast<int>(normalizedValues.size());
std::vector<float> pos;
pos.reserve(n);
for (int i = 0; i < n; ++i)
pos.push_back((i + 0.5f) / n); // bin centers
legend->setDistributionCurve(pos, normalizedValues);
}
DistributionCurveConfig — Configuration options
Use DistributionCurveConfig to control the curve computation and appearance:
binCount (default 100, range [1..10000])
Controls the resolution of the histogram used to compute the distribution curve.
-
More bins = more detail
-
Fewer bins = simpler curve
cfg.setBinCount(50); // Use 50 histogram bins
concentrationThreshold (default 1.0, range [0..1])
Controls when the curve should be automatically hidden because the distribution is not meaningful.
The curve is suppressed if either of these conditions is met:
-
Bin concentration: the largest histogram bin contains more than
thresholdfraction of all binned values -
Problematic values: undefined or invalid (NaN/Inf) values exceed
thresholdfraction of total values
Common settings:
cfg.setConcentrationThreshold(1.0f); // Never suppress (default)
cfg.setConcentrationThreshold(0.5f); // Hide if >50% concentrated OR >50% problematic
cfg.setConcentrationThreshold(0.1f); // Hide if >10% concentrated OR >10% problematic
curveWidth (default 1.0, range [0.1..10])
Controls how much horizontal space is allocated for rendering the curve next to the color bar.
-
1.0 = standard width (default)
-
2.0 = double the allocated space (helps avoid flat-looking curves)
-
0.5 = half the space (more compact)
cfg.setCurveWidth(2.0f); // Allocate more space for better visibility
Complete example
// Prepare your scalar data
std::vector<double> scalarValues = GetMyScalarValues();
int undefinedCount = CountUndefinedValues();
double minVal = GetMinValue();
double maxVal = GetMaxValue();
// Configure the distribution computation
cee::ug::DistributionCurveConfig config;
config.setBinCount(50); // 50 histogram bins
config.setConcentrationThreshold(0.5f); // Hide if >50% concentrated or problematic
config.setCurveWidth(2.0f); // Allocate 2× space for the curve
// Compute the distribution
auto distribution = cee::ug::ScalarDistributionCalculator::compute(
scalarValues,
undefinedCount,
minVal,
maxVal,
config);
// Apply to the legend if valid
if (distribution.valid)
{
legend->setDistributionCurve(
distribution.relativePositions,
distribution.normalizedMagnitudes);
}
else
{
// Curve was suppressed (too concentrated or too many undefined/invalid values)
// The legend will display without the distribution curve
}


