diff --git a/maui/src/Charts/Area/Partial/CartesianChartArea.cs b/maui/src/Charts/Area/Partial/CartesianChartArea.cs index ad6d14bf..5c0509fc 100644 --- a/maui/src/Charts/Area/Partial/CartesianChartArea.cs +++ b/maui/src/Charts/Area/Partial/CartesianChartArea.cs @@ -183,9 +183,10 @@ internal void UpdateSBS() double totalWidth = GetTotalWidth() / SideBySideSeriesPosition.Count; double startPosition = 0, end = 0; - for (int i = 0; i < SideBySideSeriesPosition.Count; i++) + var seriesGroups = SideBySideSeriesPosition.Values.ToList(); + for (int i = 0; i < seriesGroups.Count; i++) { - var seriesGroup = SideBySideSeriesPosition.Values.ToList()[i]; + var seriesGroup = seriesGroups[i]; double sbsMaxWidth = GetSBSMaxWidth(seriesGroup); foreach (ChartSeries chartSeries in seriesGroup) @@ -370,10 +371,11 @@ double GetTotalWidth() if (SideBySideSeriesPosition != null) { - for (int i = 0; i < SideBySideSeriesPosition.Count; i++) + var valuesList = SideBySideSeriesPosition.Values.ToList(); + for (int i = 0; i < valuesList.Count; i++) { double maxWidth = 0; - foreach (ChartSeries sideBySideSeries in SideBySideSeriesPosition.Values.ToList()[i]) + foreach (ChartSeries sideBySideSeries in valuesList[i]) { CartesianSeries cartesianSeries = (CartesianSeries)sideBySideSeries; double width = cartesianSeries.GetActualWidth(); @@ -483,73 +485,88 @@ static void CalculateStackingValues(Dictionary> } foreach (var series in seriesList) + { + var xValues = series.GetXValues(); + var yValues = series.YValues; + var bottomValues = new List(); + var topValues = new List(); + var seriesTypeName = series.GetType().Name; + bool is100Series = seriesTypeName.Contains("Stacking", StringComparison.Ordinal) && seriesTypeName.Contains("100Series", StringComparison.Ordinal); + + if (xValues != null) { - var xValues = series.GetXValues(); - var yValues = series.YValues; - var bottomValues = new List(); - var topValues = new List(); - - if (xValues != null) + for (int i = 0; i < xValues.Count; i++) { - for (int i = 0; i < xValues.Count; i++) - { - var xValue = xValues[i]; - var yValue = i < yValues.Count ? yValues[i] : 0; - yValue = double.IsNaN(yValue) ? 0 : yValue; + var xValue = xValues[i]; + var yValue = i < yValues.Count ? yValues[i] : 0; + yValue = double.IsNaN(yValue) ? 0 : yValue; - if (yValue >= 0) + if (yValue >= 0) + { + if (positiveYValues.TryGetValue(xValue, out double currentValue)) { - if (positiveYValues.TryGetValue(xValue, out double currentValue)) - { - bottomValues.Add((axisCross > currentValue) ? axisCross : currentValue); - if (series.GetType().Name.Contains("Stacking", StringComparison.Ordinal) && series.GetType().Name.Contains("100Series", StringComparison.Ordinal)) - { - yValue = GetYValue(seriesList, yValue, i); - } - positiveYValues[xValue] = currentValue + yValue; - } - else + bottomValues.Add((axisCross > currentValue) ? axisCross : currentValue); + if (is100Series) { - bottomValues.Add(axisCross); - if (series.GetType().Name.Contains("Stacking", StringComparison.Ordinal) && series.GetType().Name.Contains("100Series", StringComparison.Ordinal)) - { - yValue = GetYValue(seriesList, yValue, i); - } - positiveYValues.Add(xValue, yValue); + yValue = GetYValue(seriesList, yValue, i); } - - topValues.Add(positiveYValues[xValue]); + positiveYValues[xValue] = currentValue + yValue; } else { - if (!negativeYValues.TryAdd(xValue, yValue)) + bottomValues.Add(axisCross); + if (is100Series) { - bottomValues.Add((axisCross < negativeYValues[xValue]) ? axisCross : negativeYValues[xValue]); - if (series.GetType().Name.Contains("Stacking", StringComparison.Ordinal) && series.GetType().Name.Contains("100Series", StringComparison.Ordinal)) - { - yValue = GetYValue(seriesList, yValue, i); - } - negativeYValues[xValue] += yValue; + yValue = GetYValue(seriesList, yValue, i); } - else + positiveYValues.Add(xValue, yValue); + } + + topValues.Add(positiveYValues[xValue]); + } + else + { + if (!negativeYValues.TryAdd(xValue, yValue)) + { + bottomValues.Add((axisCross < negativeYValues[xValue]) ? axisCross : negativeYValues[xValue]); + if (is100Series) { - bottomValues.Add(axisCross); + yValue = GetYValue(seriesList, yValue, i); } - - topValues.Add(negativeYValues[xValue]); + negativeYValues[xValue] += yValue; + } + else + { + bottomValues.Add(axisCross); } - } - series.BottomValues = bottomValues; - series.TopValues = topValues; + topValues.Add(negativeYValues[xValue]); + } } + + series.BottomValues = bottomValues; + series.TopValues = topValues; } } + } } static double GetYValue(List SeriesList, double yValue, int index) { - double total = SeriesList.Where(series => series != null && series.YValues.Count > index).Sum(series => double.IsNaN(series.YValues[index]) ? 0 : Math.Abs(series.YValues[index])); + double total = 0; + for (int i = 0; i < SeriesList.Count; i++) + { + var series = SeriesList[i]; + if (series != null && series.YValues.Count > index) + { + double val = series.YValues[index]; + if (!double.IsNaN(val)) + { + total += Math.Abs(val); + } + } + } + if (yValue != 0) { yValue = (yValue / total) * 100; diff --git a/maui/src/Charts/Axis/CategoryAxis.cs b/maui/src/Charts/Axis/CategoryAxis.cs index c45125f2..e197248c 100644 --- a/maui/src/Charts/Axis/CategoryAxis.cs +++ b/maui/src/Charts/Axis/CategoryAxis.cs @@ -40,9 +40,10 @@ internal void GroupData() { if (groupedDatas.Count != 0) { - for (int j = 0; j <= xValues.Count - 1; j++) + var seen = new HashSet(groupingValues); + for (int j = 0; j < xValues.Count; j++) { - if (!groupingValues.Contains(xValues[j])) + if (seen.Add(xValues[j])) { groupingValues.Add(xValues[j]); } @@ -65,16 +66,31 @@ internal void GroupData() } var distinctXValues = groupingValues.Distinct().ToList(); + var indexLookup = new Dictionary(distinctXValues.Count); + for (int i = 0; i < distinctXValues.Count; i++) + { + indexLookup[distinctXValues[i]] = i; + } foreach (CartesianSeries series in RegisteredSeries.Cast()) { if (series.ActualXValues is List list) { - series.GroupedXValuesIndexes = (from val in list select (double)distinctXValues.IndexOf(val)).ToList(); + var indexes = new List(list.Count); + for (int i = 0; i < list.Count; i++) + { + indexes.Add(indexLookup.TryGetValue(list[i], out int idx) ? idx : -1); + } + series.GroupedXValuesIndexes = indexes; } - else if (series.ActualXValues != null) + else if (series.ActualXValues is List doubleValues) { - series.GroupedXValuesIndexes = (from val in series.ActualXValues as List select (double)distinctXValues.IndexOf(val.ToString())).ToList(); + var indexes = new List(doubleValues.Count); + for (int i = 0; i < doubleValues.Count; i++) + { + indexes.Add(indexLookup.TryGetValue(doubleValues[i].ToString(), out int idx) ? idx : -1); + } + series.GroupedXValuesIndexes = indexes; } series.GroupedXValues = distinctXValues; diff --git a/maui/src/Charts/Axis/Layouts/CartesianAxisLayout.cs b/maui/src/Charts/Axis/Layouts/CartesianAxisLayout.cs index 71cfc5df..d070717c 100644 --- a/maui/src/Charts/Axis/Layouts/CartesianAxisLayout.cs +++ b/maui/src/Charts/Axis/Layouts/CartesianAxisLayout.cs @@ -499,10 +499,17 @@ void InternalCreateSegments(ChartSeries series) static ChartAxis? GetAxisByName(string name, ObservableCollection? axes) { - var item = (from x in axes where x.Name == name select x).ToList(); - if (item != null && item.Count > 0) + if (axes == null) + { + return null; + } + + for (int i = 0; i < axes.Count; i++) { - return item[0]; + if (axes[i].Name == name) + { + return axes[i]; + } } return null; diff --git a/maui/src/Charts/Segment/ErrorBarSegment.cs b/maui/src/Charts/Segment/ErrorBarSegment.cs index aee89e45..53bcfa27 100644 --- a/maui/src/Charts/Segment/ErrorBarSegment.cs +++ b/maui/src/Charts/Segment/ErrorBarSegment.cs @@ -83,10 +83,30 @@ internal override void SetData(IList xList, IList yList) break; } - double xMin = xValues.Where(v => !double.IsNaN(v)).DefaultIfEmpty(0).Min(); - double xMax = xValues.Where(v => !double.IsNaN(v)).DefaultIfEmpty(0).Max(); - double yMin = yValues.Where(v => !double.IsNaN(v)).DefaultIfEmpty(0).Min(); - double yMax = yValues.Where(v => !double.IsNaN(v)).DefaultIfEmpty(0).Max(); + double xMin = double.MaxValue, xMax = double.MinValue; + double yMin = double.MaxValue, yMax = double.MinValue; + for (int i = 0; i < xValues.Count; i++) + { + double xVal = xValues[i]; + if (!double.IsNaN(xVal)) + { + if (xVal < xMin) xMin = xVal; + if (xVal > xMax) xMax = xVal; + } + } + for (int i = 0; i < yValues.Count; i++) + { + double yVal = yValues[i]; + if (!double.IsNaN(yVal)) + { + if (yVal < yMin) yMin = yVal; + if (yVal > yMax) yMax = yVal; + } + } + if (xMin == double.MaxValue) xMin = 0; + if (xMax == double.MinValue) xMax = 0; + if (yMin == double.MaxValue) yMin = 0; + if (yMax == double.MinValue) yMax = 0; double leftPointMin = xMin - _horizontalErrorValue; double leftPointMax = xMax - _horizontalErrorValue; @@ -192,12 +212,35 @@ internal override void SetData(IList xList, IList yList) if (errorBarSeries.Type == ErrorBarType.Percentage) { - var validTopPoints = _topPointCollection.Where(p => !double.IsNaN(p.Y)).Select(p => p.Y); - var validBottomPoints = _bottomPointCollection.Where(p => !double.IsNaN(p.Y)).Select(p => p.Y); + double rangeMin = double.MaxValue; + double rangeMax = double.MinValue; + bool hasValid = false; - if (validTopPoints.Any() && validBottomPoints.Any()) + for (int i = 0; i < _topPointCollection.Count; i++) { - YRange = new DoubleRange(Math.Min(validBottomPoints.Min(), validTopPoints.Min()), Math.Max(validBottomPoints.Max(), validTopPoints.Max())); + double y = _topPointCollection[i].Y; + if (!double.IsNaN(y)) + { + if (y < rangeMin) rangeMin = y; + if (y > rangeMax) rangeMax = y; + hasValid = true; + } + } + + for (int i = 0; i < _bottomPointCollection.Count; i++) + { + double y = _bottomPointCollection[i].Y; + if (!double.IsNaN(y)) + { + if (y < rangeMin) rangeMin = y; + if (y > rangeMax) rangeMax = y; + hasValid = true; + } + } + + if (hasValid) + { + YRange = new DoubleRange(rangeMin, rangeMax); } } else @@ -383,28 +426,36 @@ double[] GetSdErrorValue(IList values) return valueDoubles; } - var validValues = values.Where(v => !double.IsNaN(v)).ToList(); + double sum = 0; + int validCount = 0; + for (int i = 0; i < values.Count; i++) + { + if (!double.IsNaN(values[i])) + { + sum += values[i]; + validCount++; + } + } - if (validValues.Count <= 1) + if (validCount <= 1) { return valueDoubles; } - var sum = validValues.Sum(); - var mean = sum / validValues.Count; - var dev = new List(); - var sQDev = new List(); + double mean = sum / validCount; + double sumSqDev = 0; - for (var i = 0; i < validValues.Count; i++) + for (int i = 0; i < values.Count; i++) { - dev.Add(validValues[i] - mean); - sQDev.Add(dev[i] * dev[i]); + if (!double.IsNaN(values[i])) + { + double dev = values[i] - mean; + sumSqDev += dev * dev; + } } - var sumSqDev = sQDev.Sum(x => x); - - var sDValue = Math.Sqrt(sumSqDev / (validValues.Count - 1)); - var sDErrorValue = sDValue / Math.Sqrt(validValues.Count); + var sDValue = Math.Sqrt(sumSqDev / (validCount - 1)); + var sDErrorValue = sDValue / Math.Sqrt(validCount); valueDoubles[0] = mean; valueDoubles[1] = errorBarSeries.Type == ErrorBarType.StandardDeviation ? sDValue : sDErrorValue; diff --git a/maui/src/Charts/Series/CartesianSeries.cs b/maui/src/Charts/Series/CartesianSeries.cs index 5e1e23dc..38f3a9de 100644 --- a/maui/src/Charts/Series/CartesianSeries.cs +++ b/maui/src/Charts/Series/CartesianSeries.cs @@ -1027,18 +1027,36 @@ internal override void UpdateRange() return null; } - double xIndexValues = 0d; var xValues = ActualXValues as List; if (IsIndexed || xValues == null) { if (ActualXAxis is CategoryAxis categoryAxis && !categoryAxis.ArrangeByIndex || ActualXAxis == null) { - xValues = GroupedXValuesIndexes.Count > 0 ? GroupedXValuesIndexes : (from val in (ActualXValues as List) select (xIndexValues++)).ToList(); + if (GroupedXValuesIndexes.Count > 0) + { + xValues = GroupedXValuesIndexes; + } + else + { + var stringValues = ActualXValues as List; + int count = stringValues?.Count ?? 0; + xValues = new List(count); + for (int i = 0; i < count; i++) + { + xValues.Add(i); + } + } } else { - xValues = xValues != null ? (from val in xValues select (xIndexValues++)).ToList() : (from val in (ActualXValues as List) select (xIndexValues++)).ToList(); + int count = xValues?.Count ?? (ActualXValues as List)?.Count ?? 0; + var indexValues = new List(count); + for (int i = 0; i < count; i++) + { + indexValues.Add(i); + } + xValues = indexValues; } }