/* Copyright (C) 2008 Webyog Softworks Private Limited This file is a part of Visifire Charts. Visifire is a free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. You should have received a copy of the GNU General Public License along with Visifire Charts. If not, see . If GPL is not suitable for your products or company, Webyog provides Visifire under a flexible commercial license designed to meet your specific usage and distribution requirements. If you have already obtained a commercial license from Webyog, you can use this file under those license terms. */ #if WPF using System; using System.Collections.Generic; using System.Linq; using System.Windows; using System.Windows.Controls; using System.Windows.Documents; using System.Windows.Media; using System.Windows.Media.Animation; #else using System; using System.Windows; using System.Linq; using System.Windows.Controls; using System.Windows.Media; using System.Windows.Media.Animation; using System.Collections.Generic; #endif using Visifire.Commons; namespace Visifire.Charts { /// /// Visifire.Charts.BubbleChart class /// internal class BubbleChart { #region Public Methods #endregion #region Public Properties #endregion #region Public Events And Delegates #endregion #region Protected Methods #endregion #region Internal Properties #endregion #region Private Properties #endregion #region Private Delegates #endregion #region Private Methods /// /// Apply animation for bubble chart /// /// Bubble chart grid /// Stroyboard /// Width of the chart canvas /// Height of the chart canvas /// Storyboard private static Storyboard ApplyBubbleChartAnimation(DataSeries currentDataSeries, Panel bubbleGrid, Storyboard storyboard, Double width, Double height) { TranslateTransform translateTransform = new TranslateTransform() { X = 0, Y = -height }; bubbleGrid.RenderTransform = translateTransform; Random rand = new Random((Int32)DateTime.Now.Ticks); Double begin = rand.NextDouble(); Double hPitchSize = width / 5; DoubleCollection times1 = Graphics.GenerateDoubleCollection(0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0); DoubleCollection times2 = Graphics.GenerateDoubleCollection(0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0); DoubleCollection translateXValues; DoubleCollection pitchFactors = Graphics.GenerateDoubleCollection(1, 15.0 / 23.0, 11.0 / 23.0, 7.0 / 23.0, 5.0 / 23.0, 3.0 / 23.0, 2.0 / 23.0, 1.0 / 23.0, 0.5 / 23.0, 0); translateXValues = new DoubleCollection(); Double sign = 1; if (rand.NextDouble() > 0.5) { if ((Double)bubbleGrid.GetValue(Canvas.LeftProperty) > width / 2) sign = -1; else sign = 1; } else { if ((Double)bubbleGrid.GetValue(Canvas.LeftProperty) > width / 2) sign = 1; else sign = -1; } // Generate pitch values foreach (Double factor in pitchFactors) translateXValues.Add(sign * hPitchSize * factor); DoubleCollection translateYValues = Graphics.GenerateDoubleCollection(-height, 0, -height * 0.5, 0, -height * 0.25, 0, -height * 0.125, 0, -height * 0.0625, 0); List splines3 = AnimationHelper.GenerateKeySplineList( new Point(0, 0), new Point(1, 1), new Point(0, 0), new Point(1, 1), new Point(0, 0), new Point(1, 1), new Point(0, 0), new Point(1, 1), new Point(0, 0), new Point(1, 1), new Point(0, 0), new Point(1, 1), new Point(0, 0), new Point(1, 1), new Point(0, 0), new Point(1, 1), new Point(0, 0), new Point(1, 1), new Point(0, 0), new Point(1, 1)); List splines4 = AnimationHelper.GenerateKeySplineList( new Point(0, 0), new Point(1, 1), new Point(0.5, 0), new Point(1, 1), new Point(0, 0), new Point(0.5, 1), new Point(0.5, 0), new Point(1, 1), new Point(0, 0), new Point(0.5, 1), new Point(0.5, 0), new Point(1, 1), new Point(0, 0), new Point(0.5, 1), new Point(0.5, 0), new Point(1, 1), new Point(0, 0), new Point(0.5, 1), new Point(0.5, 0), new Point(1, 1)); DoubleAnimationUsingKeyFrames xTranslateAnimation = AnimationHelper.CreateDoubleAnimation(currentDataSeries, translateTransform, "(TranslateTransform.X)", begin * 0.5 + 0.5, times1, translateXValues, splines3); DoubleAnimationUsingKeyFrames yTranslateAnimation = AnimationHelper.CreateDoubleAnimation(currentDataSeries, translateTransform, "(TranslateTransform.Y)", begin * 0.5 + 0.5, times2, translateYValues, splines4); storyboard.Children.Add(xTranslateAnimation); storyboard.Children.Add(yTranslateAnimation); return storyboard; } #endregion #region Internal Methods /// /// Get visual object for bubble chart /// /// Width of the chart /// Height of the chart /// plotDetails /// List of DataSeries /// Chart /// Plank depth /// Whether animation is enabled /// Bubble chart canvas internal static Canvas GetVisualObjectForBubbleChart(Double width, Double height, PlotDetails plotDetails, List seriesList, Chart chart, Double plankDepth, bool animationEnabled) { if (Double.IsNaN(width) || Double.IsNaN(height) || width <= 0 || height <= 0) return null; DataSeries currentDataSeries = null; Canvas visual = new Canvas(); visual.Width = width; visual.Height = height; Double depth3d = plankDepth / (plotDetails.Layer3DCount == 0 ? 1 : plotDetails.Layer3DCount) * (chart.View3D ? 1 : 0); Double visualOffset = depth3d * (plotDetails.SeriesDrawingIndex[seriesList[0]] + 1 - (plotDetails.Layer3DCount == 0 ? 0 : 1)); visual.SetValue(Canvas.TopProperty, visualOffset); visual.SetValue(Canvas.LeftProperty, -visualOffset); foreach (DataSeries series in seriesList) { if (series.Enabled == false) continue; PlotGroup plotGroup = series.PlotGroup; var dataPointsList = (from dataPoint in series.InternalDataPoints where !Double.IsNaN(dataPoint.ZValue) && dataPoint.Enabled == true select dataPoint.ZValue); Double minValue = 0; Double maxValue = 1; if (dataPointsList.Count() > 0) { minValue = dataPointsList.Min(); maxValue = dataPointsList.Max(); } foreach (DataPoint dataPoint in series.InternalDataPoints) { if (Double.IsNaN(dataPoint.InternalYValue) || (dataPoint.Enabled == false)) { continue; } Faces bubbleFaces = new Faces(); bubbleFaces.Parts = new List(); Double xPosition = Graphics.ValueToPixelPosition(0, width, (Double)plotGroup.AxisX.InternalAxisMinimum, (Double)plotGroup.AxisX.InternalAxisMaximum, dataPoint.InternalXValue); Double yPosition = Graphics.ValueToPixelPosition(height, 0, (Double)plotGroup.AxisY.InternalAxisMinimum, (Double)plotGroup.AxisY.InternalAxisMaximum, dataPoint.InternalYValue); Brush markerColor = dataPoint.Color; markerColor = (chart.View3D ? Graphics.GetLightingEnabledBrush3D(markerColor) : ((Boolean)dataPoint.LightingEnabled ? Graphics.GetLightingEnabledBrush(markerColor, "Linear", null) : markerColor)); Double value = !Double.IsNaN(dataPoint.ZValue) ? dataPoint.ZValue : (minValue + maxValue) / 2; Double markerScale = Graphics.ConvertScale(minValue, maxValue, value, 1, (Double)dataPoint.MarkerScale); Size markerSize = new Size((Double)dataPoint.MarkerSize, (Double)dataPoint.MarkerSize); String labelText = (Boolean)dataPoint.LabelEnabled ? dataPoint.TextParser(dataPoint.LabelText) : ""; Boolean markerBevel = false; Marker marker = new Marker((MarkerTypes)dataPoint.MarkerType, markerScale * (Double)dataPoint.MarkerScale, markerSize, markerBevel, markerColor, labelText); marker.ShadowEnabled = dataPoint.Parent.ShadowEnabled; marker.MarkerSize = new Size((Double)dataPoint.MarkerSize, (Double)dataPoint.MarkerSize); if (dataPoint.BorderColor != null) marker.BorderColor = dataPoint.BorderColor; marker.BorderThickness = ((Thickness)dataPoint.MarkerBorderThickness).Left; marker.Tag = new ElementData() { Element = dataPoint }; Double gap = (markerScale * (Double)dataPoint.MarkerScale * (Double)dataPoint.MarkerSize) / 2; if (!String.IsNullOrEmpty(labelText)) { marker.FontColor = Chart.CalculateDataPointLabelFontColor(chart, dataPoint, dataPoint.LabelFontColor, LabelStyles.OutSide); marker.FontSize = (Double)dataPoint.LabelFontSize; marker.FontWeight = (FontWeight)dataPoint.LabelFontWeight; marker.FontFamily = dataPoint.LabelFontFamily; marker.FontStyle = (FontStyle)dataPoint.LabelFontStyle; marker.TextBackground = dataPoint.LabelBackground; marker.TextAlignmentX = AlignmentX.Center; marker.TextAlignmentY = AlignmentY.Center; if (!Double.IsNaN(dataPoint.LabelAngle) && dataPoint.LabelAngle != 0) { marker.LabelAngle = dataPoint.LabelAngle; marker.TextOrientation = Orientation.Vertical; marker.TextAlignmentX = AlignmentX.Center; marker.TextAlignmentY = AlignmentY.Center; marker.LabelStyle = (LabelStyles)dataPoint.LabelStyle; } marker.CreateVisual(); if (Double.IsNaN(dataPoint.LabelAngle) || dataPoint.LabelAngle == 0) { if (yPosition - gap < 0 && (yPosition - marker.TextBlockSize.Height / 2) < 0) marker.TextAlignmentY = AlignmentY.Bottom; else if (yPosition + gap > height && (yPosition + marker.TextBlockSize.Height / 2) > height) marker.TextAlignmentY = AlignmentY.Top; if (xPosition - gap < 0 && (xPosition - marker.TextBlockSize.Width / 2) < 0) marker.TextAlignmentX = AlignmentX.Right; else if (xPosition + gap > width && (xPosition + marker.TextBlockSize.Width / 2) > width) marker.TextAlignmentX = AlignmentX.Left; } } marker.CreateVisual(); marker.Visual.Opacity = dataPoint.InternalOpacity * dataPoint.Parent.InternalOpacity; marker.AddToParent(visual, xPosition, yPosition, new Point(0.5, 0.5)); // Apply animation if (animationEnabled) { if (dataPoint.Parent.Storyboard == null) dataPoint.Parent.Storyboard = new Storyboard(); currentDataSeries = dataPoint.Parent; // Apply animation to the bubbles dataPoint.Parent.Storyboard = ApplyBubbleChartAnimation(currentDataSeries, marker.Visual, dataPoint.Parent.Storyboard, width, height); } bubbleFaces.Parts.Add(marker.MarkerShape); bubbleFaces.VisualComponents.Add(marker.Visual); bubbleFaces.BorderElements.Add(marker.MarkerShape); bubbleFaces.Visual = marker.Visual; dataPoint.Faces = bubbleFaces; } } RectangleGeometry clipRectangle = new RectangleGeometry(); clipRectangle.Rect = new Rect(0, -chart.ChartArea.PLANK_DEPTH, width + chart.ChartArea.PLANK_OFFSET, height + chart.ChartArea.PLANK_DEPTH); visual.Clip = clipRectangle; return visual; } #endregion #region Internal Events And Delegates #endregion #region Data #endregion } }