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);
            }
        }