/// <summary> /// Attempts to convert an object into a double. /// </summary> /// <param name="value">The value to convert.</param> /// <param name="doubleValue">The double value.</param> /// <returns>A value indicating whether the value can be converted to a /// double.</returns> public static bool TryConvert(object value, out double doubleValue) { doubleValue = default(double); try { if (value != null && (value is double || value is int || value is byte || value is short || value is decimal || value is float || value is long || value is uint || value is sbyte || value is ushort || value is ulong)) { doubleValue = ValueHelper.ToDouble(value); return(true); } } catch (FormatException) { } catch (InvalidCastException) { } return(false); }
/// <summary> /// Returns the plot area coordinate of a value. /// </summary> /// <param name="value">The value to plot.</param> /// <param name="currentRange">The range of values.</param> /// <param name="length">The length of axis.</param> /// <returns>The plot area coordinate of a value.</returns> private static UnitValue GetPlotAreaCoordinate(object value, Range <double> currentRange, double length) { if (currentRange.HasData) { double doubleValue = ValueHelper.ToDouble(value); double pixelLength = Math.Max(length - 1, 0); double rangelength = currentRange.Maximum - currentRange.Minimum; return(new UnitValue((doubleValue - currentRange.Minimum) * (pixelLength / rangelength), Unit.Pixels)); } return(UnitValue.NaN()); }
/// <summary> /// Updates the ratios of each data point. /// </summary> private void UpdateRatios() { double sum = ActivePieDataPoints.Select(pieDataPoint => Math.Abs(ValueHelper.ToDouble(pieDataPoint.DependentValue))).Sum(); // Priming the loop by calculating initial value of // offset ratio and its corresponding points. double offsetRatio = 0; foreach (PieDataPoint dataPoint in ActivePieDataPoints) { double dependentValue = Math.Abs(ValueHelper.ToDouble(dataPoint.DependentValue)); double ratio = dependentValue / sum; if (!ValueHelper.CanGraph(ratio)) { ratio = 0.0; } dataPoint.Ratio = ratio; dataPoint.OffsetRatio = offsetRatio; offsetRatio += ratio; } }
private static Storyboard CreateStoryboard( FrameworkElement target, DependencyProperty animatingDependencyProperty, string propertyPath, ref object toValue, TimeSpan durationTimeSpan, EasingFunctionBase easingFunction) { object fromValue = target.GetValue(animatingDependencyProperty); double fromDoubleValue; double toDoubleValue; DateTime fromDateTime; DateTime toDateTime; Storyboard storyBoard = new Storyboard(); Storyboard.SetTarget(storyBoard, target); Storyboard.SetTargetProperty(storyBoard, propertyPath); if ((fromValue != null && toValue != null)) { if (ValueHelper.TryConvert(fromValue, out fromDoubleValue) && ValueHelper.TryConvert(toValue, out toDoubleValue)) { DoubleAnimation doubleAnimation = new DoubleAnimation(); doubleAnimation.EnableDependentAnimation = true; #if !NO_EASING_FUNCTIONS doubleAnimation.EasingFunction = easingFunction; #endif doubleAnimation.Duration = durationTimeSpan; doubleAnimation.To = ValueHelper.ToDouble(toValue); toValue = doubleAnimation.To; storyBoard.Children.Add(doubleAnimation); } else if (ValueHelper.TryConvert(fromValue, out fromDateTime) && ValueHelper.TryConvert(toValue, out toDateTime)) { ObjectAnimationUsingKeyFrames keyFrameAnimation = new ObjectAnimationUsingKeyFrames(); keyFrameAnimation.EnableDependentAnimation = true; keyFrameAnimation.Duration = durationTimeSpan; long intervals = (long)(durationTimeSpan.TotalSeconds * KeyFramesPerSecond); if (intervals < 2L) { intervals = 2L; } IEnumerable <TimeSpan> timeSpanIntervals = ValueHelper.GetTimeSpanIntervalsInclusive(durationTimeSpan, intervals); IEnumerable <DateTime> dateTimeIntervals = ValueHelper.GetDateTimesBetweenInclusive(fromDateTime, toDateTime, intervals); IEnumerable <DiscreteObjectKeyFrame> keyFrames = EnumerableFunctions.Zip( dateTimeIntervals, timeSpanIntervals, (dateTime, timeSpan) => new DiscreteObjectKeyFrame() { Value = dateTime, KeyTime = timeSpan }); foreach (DiscreteObjectKeyFrame keyFrame in keyFrames) { keyFrameAnimation.KeyFrames.Add(keyFrame); toValue = keyFrame.Value; } storyBoard.Children.Add(keyFrameAnimation); } } if (storyBoard.Children.Count == 0) { ObjectAnimationUsingKeyFrames keyFrameAnimation = new ObjectAnimationUsingKeyFrames(); keyFrameAnimation.EnableDependentAnimation = true; DiscreteObjectKeyFrame endFrame = new DiscreteObjectKeyFrame() { Value = toValue, KeyTime = new TimeSpan(0, 0, 0) }; keyFrameAnimation.KeyFrames.Add(endFrame); storyBoard.Children.Add(keyFrameAnimation); } return(storyBoard); }
/// <summary> /// Updates each point. /// </summary> /// <param name="dataPoint">The data point to update.</param> protected override void UpdateDataPoint(DataPoint dataPoint) { if (SeriesHost == null || PlotArea == null) { return; } object category = dataPoint.ActualIndependentValue ?? (this.ActiveDataPoints.IndexOf(dataPoint) + 1); Range <UnitValue> coordinateRange = GetCategoryRange(category); if (!coordinateRange.HasData) { return; } if (coordinateRange.Maximum.Unit != Unit.Pixels || coordinateRange.Minimum.Unit != Unit.Pixels) { throw new InvalidOperationException(Properties.Resources.DataPointSeriesWithAxes_ThisSeriesDoesNotSupportRadialAxes); } double minimum = (double)coordinateRange.Minimum.Value; double maximum = (double)coordinateRange.Maximum.Value; double plotAreaHeight = ActualDependentRangeAxis.GetPlotAreaCoordinate(ActualDependentRangeAxis.Range.Maximum).Value; IEnumerable <ColumnSeries> columnSeries = SeriesHost.Series.OfType <ColumnSeries>().Where(series => series.ActualIndependentAxis == ActualIndependentAxis); int numberOfSeries = columnSeries.Count(); double coordinateRangeWidth = (maximum - minimum); double segmentWidth = coordinateRangeWidth * 0.8; double columnWidth = segmentWidth / numberOfSeries; int seriesIndex = columnSeries.IndexOf(this); double dataPointY = ActualDependentRangeAxis.GetPlotAreaCoordinate(ValueHelper.ToDouble(dataPoint.ActualDependentValue)).Value; double zeroPointY = ActualDependentRangeAxis.GetPlotAreaCoordinate(ActualDependentRangeAxis.Origin).Value; double offset = seriesIndex * Math.Round(columnWidth) + coordinateRangeWidth * 0.1; double dataPointX = minimum + offset; if (GetIsDataPointGrouped(category)) { // Multiple DataPoints share this category; offset and overlap them appropriately IGrouping <object, DataPoint> categoryGrouping = GetDataPointGroup(category); int index = categoryGrouping.IndexOf(dataPoint); dataPointX += (index * (columnWidth * 0.2)) / (categoryGrouping.Count() - 1); columnWidth *= 0.8; Canvas.SetZIndex(dataPoint, -index); } if (ValueHelper.CanGraph(dataPointY) && ValueHelper.CanGraph(dataPointX) && ValueHelper.CanGraph(zeroPointY)) { dataPoint.Visibility = Visibility.Visible; double left = Math.Round(dataPointX); double width = Math.Round(columnWidth); double top = Math.Round(plotAreaHeight - Math.Max(dataPointY, zeroPointY) + 0.5); double bottom = Math.Round(plotAreaHeight - Math.Min(dataPointY, zeroPointY) + 0.5); double height = bottom - top + 1; Canvas.SetLeft(dataPoint, left); Canvas.SetTop(dataPoint, top); dataPoint.Width = width; dataPoint.Height = height; } else { dataPoint.Visibility = Visibility.Collapsed; } }
protected override Range <IComparable> IRangeProviderGetRange(IRangeConsumer rangeConsumer) { if (rangeConsumer == ActualDependentAxis) { var dependentValuesByIndependentValue = IndependentValueDependentValues.Select(e => e.ToArray()).ToArray(); var mostNegative = dependentValuesByIndependentValue .Select(g => g.Where(v => v < 0) .Sum()) .Where(v => v < 0) .ToArray(); var leastNegative = dependentValuesByIndependentValue .Select(g => g.Where(v => v <= 0) .DefaultIfEmpty(1.0) .First()) .Where(v => v <= 0) .ToArray(); var mostPositive = dependentValuesByIndependentValue .Select(g => g.Where(v => 0 < v) .Sum()) .Where(v => 0 < v) .ToArray(); var leastPositive = dependentValuesByIndependentValue .Select(g => g.Where(v => 0 <= v) .DefaultIfEmpty(-1.0) .First()) .Where(v => 0 <= v) .ToArray(); // Compute minimum double minimum = 0; if (mostNegative.Any()) { minimum = mostNegative.Min(); } else if (leastPositive.Any()) { minimum = leastPositive.Min(); } // Compute maximum double maximum = 0; if (mostPositive.Any()) { maximum = mostPositive.Max(); } else if (leastNegative.Any()) { maximum = leastNegative.Max(); } if (IsStacked100) { minimum = Math.Min(minimum, 0); maximum = Math.Max(maximum, 0); } return(new Range <IComparable>(minimum, maximum)); } else if (rangeConsumer == ActualIndependentAxis) { // Using a non-ICategoryAxis for the independent axis // Need to specifically adjust for slot size of bars/columns so they don't overlap // Note: Calculation for slotSize is not perfect, but it's quick, close, and errs on the safe side Range <IComparable> range = base.IRangeProviderGetRange(rangeConsumer); int count = Math.Max(IndependentValueGroups.Count(), 1); if (ActualIndependentAxis.CanPlot(0.0)) { double minimum = ValueHelper.ToDouble(range.Minimum); double maximum = ValueHelper.ToDouble(range.Maximum); double slotSize = (maximum - minimum) / count; return(new Range <IComparable>(minimum - slotSize, maximum + slotSize)); } else { DateTime minimum = ValueHelper.ToDateTime(range.Minimum); DateTime maximum = ValueHelper.ToDateTime(range.Maximum); TimeSpan slotSize = TimeSpan.FromTicks((maximum - minimum).Ticks / count); return(new Range <IComparable>(minimum - slotSize, maximum + slotSize)); } } else { return(base.IRangeProviderGetRange(rangeConsumer)); } }
protected override void UpdateDataItemPlacement(IEnumerable <DefinitionSeries.DataItem> dataItems) { IAxis actualIndependentAxis = ActualIndependentAxis; if ((null != ActualDependentAxis) && (null != actualIndependentAxis)) { double plotAreaMaximumDependentCoordinate = ActualDependentAxis.GetPlotAreaCoordinate(ActualDependentRangeAxis.Range.Maximum).Value; double zeroCoordinate = ActualDependentAxis.GetPlotAreaCoordinate(ActualDependentRangeAxis.Origin ?? 0.0).Value; ICategoryAxis actualIndependentCategoryAxis = actualIndependentAxis as ICategoryAxis; double nonCategoryAxisRangeMargin = (null != actualIndependentCategoryAxis) ? 0 : GetMarginForNonCategoryAxis(actualIndependentAxis); foreach (IndependentValueGroup group in IndependentValueGroups) { Range <UnitValue> categoryRange = new Range <UnitValue>(); if (null != actualIndependentCategoryAxis) { categoryRange = actualIndependentCategoryAxis.GetPlotAreaCoordinateRange(group.IndependentValue); } else { UnitValue independentValueCoordinate = actualIndependentAxis.GetPlotAreaCoordinate(group.IndependentValue); if (ValueHelper.CanGraph(independentValueCoordinate.Value)) { categoryRange = new Range <UnitValue>(new UnitValue(independentValueCoordinate.Value - nonCategoryAxisRangeMargin, independentValueCoordinate.Unit), new UnitValue(independentValueCoordinate.Value + nonCategoryAxisRangeMargin, independentValueCoordinate.Unit)); } } if (categoryRange.HasData) { double categoryMinimumCoordinate = categoryRange.Minimum.Value; double categoryMaximumCoordinate = categoryRange.Maximum.Value; double padding = 0.1 * (categoryMaximumCoordinate - categoryMinimumCoordinate); categoryMinimumCoordinate += padding; categoryMaximumCoordinate -= padding; double sum = IsStacked100 ? group.DataItems.Sum(di => Math.Abs(ValueHelper.ToDouble(di.DataPoint.ActualDependentValue))) : 1; if (0 == sum) { sum = 1; } double ceiling = 0; double floor = 0; foreach (DataItem dataItem in group.DataItems) { DataPoint dataPoint = dataItem.DataPoint; double value = IsStacked100 ? (ValueHelper.ToDouble(dataPoint.ActualDependentValue) * (100 / sum)) : ValueHelper.ToDouble(dataPoint.ActualDependentValue); if (ValueHelper.CanGraph(value)) { double valueCoordinate = ActualDependentAxis.GetPlotAreaCoordinate(value).Value; double fillerCoordinate = (0 <= value) ? ceiling : floor; double topCoordinate = 0, leftCoordinate = 0, height = 0, width = 0, deltaCoordinate = 0; if (AxisOrientation.Y == ActualDependentAxis.Orientation) { topCoordinate = plotAreaMaximumDependentCoordinate - Math.Max(valueCoordinate + fillerCoordinate, zeroCoordinate + fillerCoordinate); double bottomCoordinate = plotAreaMaximumDependentCoordinate - Math.Min(valueCoordinate + fillerCoordinate, zeroCoordinate + fillerCoordinate); deltaCoordinate = bottomCoordinate - topCoordinate; height = (0 < deltaCoordinate) ? deltaCoordinate + 1 : 0; leftCoordinate = categoryMinimumCoordinate; width = categoryMaximumCoordinate - categoryMinimumCoordinate + 1; } else { leftCoordinate = Math.Min(valueCoordinate + fillerCoordinate, zeroCoordinate + fillerCoordinate); double rightCoordinate = Math.Max(valueCoordinate + fillerCoordinate, zeroCoordinate + fillerCoordinate); deltaCoordinate = rightCoordinate - leftCoordinate; width = (0 < deltaCoordinate) ? deltaCoordinate + 1 : 0; topCoordinate = categoryMinimumCoordinate; height = categoryMaximumCoordinate - categoryMinimumCoordinate + 1; } double roundedTopCoordinate = Math.Round(topCoordinate); Canvas.SetTop(dataItem.Container, roundedTopCoordinate); dataPoint.Height = Math.Round(topCoordinate + height - roundedTopCoordinate); double roundedLeftCoordinate = Math.Round(leftCoordinate); Canvas.SetLeft(dataItem.Container, roundedLeftCoordinate); dataPoint.Width = Math.Round(leftCoordinate + width - roundedLeftCoordinate); dataPoint.Visibility = Visibility.Visible; if (0 <= value) { ceiling += deltaCoordinate; } else { floor -= deltaCoordinate; } } else { dataPoint.Visibility = Visibility.Collapsed; } } } else { foreach (DataPoint dataPoint in group.DataItems.Select(di => di.DataPoint)) { dataPoint.Visibility = Visibility.Collapsed; } } } } }
/// <summary> /// Overrides the actual range to ensure that it is never set to an /// empty range. /// </summary> /// <param name="range">The range to override.</param> /// <returns>Returns the overridden range.</returns> protected override Range <IComparable> OverrideDataRange(Range <IComparable> range) { range = base.OverrideDataRange(range); if (!range.HasData) { return(new Range <IComparable>(0.0, 1.0)); } else if (ValueHelper.Compare(range.Minimum, range.Maximum) == 0) { Range <IComparable> outputRange = new Range <IComparable>((ValueHelper.ToDouble(range.Minimum)) - 1, (ValueHelper.ToDouble(range.Maximum)) + 1); return(outputRange); } // ActualLength of 1.0 or less maps all points to the same coordinate if (range.HasData && this.ActualLength > 1.0) { bool isDataAnchoredToOrigin = false; IList <ValueMarginCoordinateAndOverlap> valueMargins = new List <ValueMarginCoordinateAndOverlap>(); foreach (IValueMarginProvider valueMarginProvider in this.RegisteredListeners.OfType <IValueMarginProvider>()) { foreach (ValueMargin valueMargin in valueMarginProvider.GetValueMargins(this)) { IAnchoredToOrigin dataAnchoredToOrigin = valueMarginProvider as IAnchoredToOrigin; isDataAnchoredToOrigin = (dataAnchoredToOrigin != null && dataAnchoredToOrigin.AnchoredAxis == this); valueMargins.Add( new ValueMarginCoordinateAndOverlap { ValueMargin = valueMargin, }); } } if (valueMargins.Count > 0) { double maximumPixelMarginLength = valueMargins .Select(valueMargin => valueMargin.ValueMargin.LowMargin + valueMargin.ValueMargin.HighMargin) .MaxOrNullable().Value; // Requested margin is larger than the axis so give up // trying to find a range that will fit it. if (maximumPixelMarginLength > this.ActualLength) { return(range); } Range <double> originalRange = range.ToDoubleRange(); Range <double> currentRange = range.ToDoubleRange(); // Ensure range is not empty. if (currentRange.Minimum == currentRange.Maximum) { currentRange = new Range <double>(currentRange.Maximum - 1, currentRange.Maximum + 1); } // priming the loop double actualLength = this.ActualLength; ValueMarginCoordinateAndOverlap maxLeftOverlapValueMargin; ValueMarginCoordinateAndOverlap maxRightOverlapValueMargin; UpdateValueMargins(valueMargins, currentRange.ToComparableRange()); GetMaxLeftAndRightOverlap(valueMargins, out maxLeftOverlapValueMargin, out maxRightOverlapValueMargin); while (maxLeftOverlapValueMargin.LeftOverlap > 0 || maxRightOverlapValueMargin.RightOverlap > 0) { double unitOverPixels = currentRange.GetLength().Value / actualLength; double newMinimum = currentRange.Minimum - ((maxLeftOverlapValueMargin.LeftOverlap + 0.5) * unitOverPixels); double newMaximum = currentRange.Maximum + ((maxRightOverlapValueMargin.RightOverlap + 0.5) * unitOverPixels); currentRange = new Range <double>(newMinimum, newMaximum); UpdateValueMargins(valueMargins, currentRange.ToComparableRange()); GetMaxLeftAndRightOverlap(valueMargins, out maxLeftOverlapValueMargin, out maxRightOverlapValueMargin); } if (isDataAnchoredToOrigin) { if (originalRange.Minimum >= 0 && currentRange.Minimum < 0) { currentRange = new Range <double>(0, currentRange.Maximum); } else if (originalRange.Maximum <= 0 && currentRange.Maximum > 0) { currentRange = new Range <double>(currentRange.Minimum, 0); } } return(currentRange.ToComparableRange()); } } return(range); }
/// <summary> /// Updates the placement of the DataItems (data points) of the series. /// </summary> /// <param name="dataItems">DataItems in need of an update.</param> protected override void UpdateDataItemPlacement(IEnumerable <DefinitionSeries.DataItem> dataItems) { if ((null != ActualDependentAxis) && (null != ActualIndependentAxis)) { double plotAreaMaximumDependentCoordinate = ActualDependentAxis.GetPlotAreaCoordinate(ActualDependentRangeAxis.Range.Maximum).Value; double lineTopBuffer = 1; List <Point>[] points = new List <Point> [SeriesDefinitions.Count]; for (int i = 0; i < points.Length; i++) { points[i] = new List <Point>(); } foreach (IndependentValueGroup group in IndependentValueGroupsOrderedByIndependentValue) { double sum = IsStacked100 ? group.DataItems.Sum(di => Math.Abs(ValueHelper.ToDouble(di.DataPoint.ActualDependentValue))) : 1; if (0 == sum) { sum = 1; } double x = ActualIndependentAxis.GetPlotAreaCoordinate(group.IndependentValue).Value; if (ValueHelper.CanGraph(x)) { double lastValue = 0; Point lastPoint = new Point(x, Math.Max(plotAreaMaximumDependentCoordinate - ActualDependentRangeAxis.GetPlotAreaCoordinate(lastValue).Value, lineTopBuffer)); int i = -1; SeriesDefinition lastDefinition = null; foreach (DataItem dataItem in group.DataItems) { if (lastDefinition != dataItem.SeriesDefinition) { i++; } while (dataItem.SeriesDefinition != SeriesDefinitions[i]) { points[i].Add(lastPoint); i++; } DataPoint dataPoint = dataItem.DataPoint; double value = IsStacked100 ? (ValueHelper.ToDouble(dataItem.DataPoint.ActualDependentValue) * (100 / sum)) : ValueHelper.ToDouble(dataItem.DataPoint.ActualDependentValue); if (ValueHelper.CanGraph(value)) { value += lastValue; dataItem.ActualStackedDependentValue = value; double y = ActualDependentRangeAxis.GetPlotAreaCoordinate(value).Value; lastValue = value; lastPoint.Y = Math.Max(plotAreaMaximumDependentCoordinate - y, lineTopBuffer); points[i].Add(lastPoint); dataItem.CenterPoint = new Point(x, plotAreaMaximumDependentCoordinate - y); double left = dataItem.CenterPoint.X - (dataPoint.ActualWidth / 2); double top = dataItem.CenterPoint.Y - (dataPoint.ActualHeight / 2); Canvas.SetLeft(dataItem.Container, Math.Round(left)); Canvas.SetTop(dataItem.Container, Math.Round(top)); dataPoint.Visibility = Visibility.Visible; } else { points[i].Add(lastPoint); dataPoint.Visibility = Visibility.Collapsed; } lastDefinition = dataItem.SeriesDefinition; } } else { foreach (DataPoint dataPoint in group.DataItems.Select(di => di.DataPoint)) { dataPoint.Visibility = Visibility.Collapsed; } } } UpdateShape(points); } }