private void UpdateArmorSpectrum() { var finalSpectrumStops = new SortedDictionary <double, GradientStop>(); foreach (var spectrumStop in _sortedSpectrumStops) { finalSpectrumStops.Add(spectrumStop.Offset, spectrumStop.Clone()); } var minValue = _sortedTicks[_sortedTicks.Length - 1]; var maxValue = _sortedTicks[0]; var valueRange = maxValue - minValue; var tickOffsets = new double[_sortedTicks.Length]; // create spectrum stops var spectrumStopStartIndex = 0; for (int tickIndex = 0; tickIndex < _sortedTicks.Length; ++tickIndex) { var tick = _sortedTicks[tickIndex]; var valueOffset = 1 - (tick - minValue) / valueRange; tickOffsets[tickIndex] = valueOffset; if (finalSpectrumStops.ContainsKey(valueOffset)) { continue; } for (int spectrumStopIndex = spectrumStopStartIndex; spectrumStopIndex < _sortedSpectrumStops.Length; ++spectrumStopIndex) { var spectrumStop = _sortedSpectrumStops[spectrumStopIndex]; if (spectrumStop.Offset > valueOffset) { // create a spectrum stop for this value var previousSpectrumStopIndex = spectrumStopIndex - 1; var previousSpectrumStop = _sortedSpectrumStops[previousSpectrumStopIndex]; var colorOffset = (valueOffset - previousSpectrumStop.Offset) / (spectrumStop.Offset - previousSpectrumStop.Offset); var color = ColorEx.Interpolate(previousSpectrumStop.Color, spectrumStop.Color, colorOffset); finalSpectrumStops.Add(valueOffset, new GradientStop(color, valueOffset)); spectrumStopStartIndex = previousSpectrumStopIndex; break; } else if (spectrumStop.Offset == valueOffset) { // this spectrum stop is already existed, no need to add again spectrumStopStartIndex = spectrumStopIndex; break; } } } // remap spectrum offsets var mappedOffsetRangePerStop = 1.0 / (_sortedTicks.Length - 1); spectrumStopStartIndex = 0; var finalSpectrumStopArray = finalSpectrumStops.Values.ToArray(); var headerAndFooterSize = 1.0 / _sortedTicks.Length / 2.0; for (int tickIndex = 0; tickIndex < _sortedTicks.Length; ++tickIndex) { var tickOffset = tickOffsets[tickIndex]; var minMappedOffset = Math.Max(headerAndFooterSize, (tickIndex - 0.5) * mappedOffsetRangePerStop); var maxMappedOffset = Math.Min(1.0 - headerAndFooterSize, (tickIndex + 0.5) * mappedOffsetRangePerStop); var mappedOffsetRange = maxMappedOffset - minMappedOffset; var minOffset = tickIndex == 0 ? 0.0 : tickOffset - (tickOffset - tickOffsets[tickIndex - 1]) / 2; var maxOffset = tickIndex == _sortedTicks.Length - 1 ? 1.0 : tickOffset + (tickOffsets[tickIndex + 1] - tickOffset) / 2; var offsetRange = maxOffset - minOffset; for (int spectrumStopIndex = spectrumStopStartIndex; spectrumStopIndex < finalSpectrumStopArray.Length; ++spectrumStopIndex) { var spectrumStop = finalSpectrumStopArray[spectrumStopIndex]; if (spectrumStop.Offset <= maxOffset && !spectrumStop.IsFrozen) { spectrumStop.Offset = (spectrumStop.Offset - minOffset) / offsetRange * mappedOffsetRange + minMappedOffset; spectrumStop.Freeze(); } else { spectrumStopStartIndex = spectrumStopIndex; break; } } } this.Spectrum.Fill = new LinearGradientBrush(new GradientStopCollection(finalSpectrumStopArray)); // create tick labels this.TicksContainer.RowDefinitions.Clear(); this.TicksContainer.Children.Clear(); for (int tickIndex = 0; tickIndex < _sortedTicks.Length; ++tickIndex) { this.TicksContainer.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(1.0, GridUnitType.Star) }); var tickLabel = new TextBlock(new Run(_sortedTicks[tickIndex].ToString("F0"))); tickLabel.VerticalAlignment = VerticalAlignment.Center; tickLabel.HorizontalAlignment = HorizontalAlignment.Center; tickLabel.FontSize = 10; this.TicksContainer.Children.Add(tickLabel); Grid.SetRow(tickLabel, tickIndex); } }