private void SetStateAreaPoints(LineChartState state) { var xAxisValues = XAxisValues; var yAxisValues = YAxisValues; if (xAxisValues is null || xAxisValues.Count <= 1 || yAxisValues is null || yAxisValues.Count <= 1) { state.XAxisLabelStep = double.NaN; state.YAxisLabelStep = double.NaN; state.Points = null; return; } var logarithmicScale = YAxisLogarithmicScale; var yAxisValuesLogScaled = logarithmicScale ? yAxisValues.Select(y => Math.Log(y)).ToList() : yAxisValues.ToList(); var yAxisValuesLogScaledMax = yAxisValuesLogScaled.Max(); var yAxisValuesScaled = yAxisValuesLogScaled .Select(y => ScaleVertical(y, yAxisValuesLogScaledMax, state.AreaHeight)) .ToList(); state.Points = new Point[yAxisValues.Count]; var pointStep = state.AreaWidth / (xAxisValues.Count - 1); for (var i = 0; i < yAxisValuesScaled.Count; i++) { state.Points[i] = new Point(i * pointStep, yAxisValuesScaled[i]); } }
private void DrawXAxisLabels(DrawingContext context, LineChartState state) { var foreground = XAxisLabelForeground; if (foreground is null || state.XAxisLabels is null || double.IsNaN(state.XAxisLabelStep) || state.ChartWidth <= 0 || state.ChartHeight <= 0 || state.ChartHeight - state.AreaMargin.Top < state.AreaMargin.Bottom) { return; } var opacity = XAxisLabelOpacity; var fontFamily = XAxisLabelFontFamily; var fontStyle = XAxisLabelFontStyle; var fontWeight = XAxisLabelFontWeight; var typeface = new Typeface(fontFamily, fontStyle, fontWeight); var fontSize = XAxisLabelFontSize; var offset = XAxisLabelOffset; var angleRadians = Math.PI / 180.0 * XAxisLabelAngle; var alignment = XAxisLabelAlignment; var originTop = state.AreaMargin.Top + state.AreaHeight; var formattedTextLabels = new List <FormattedText>(); var constrainWidthMax = 0.0; var constrainHeightMax = 0.0; foreach (var label in state.XAxisLabels) { var formattedText = CreateFormattedText(label, typeface, alignment, fontSize, Size.Empty); formattedTextLabels.Add(formattedText); constrainWidthMax = Math.Max(constrainWidthMax, formattedText.Bounds.Width); constrainHeightMax = Math.Max(constrainHeightMax, formattedText.Bounds.Height); } var constraintMax = new Size(constrainWidthMax, constrainHeightMax); var offsetTransform = context.PushPreTransform(Matrix.CreateTranslation(offset.X, offset.Y)); for (var i = 0; i < formattedTextLabels.Count; i++) { formattedTextLabels[i].Constraint = constraintMax; var origin = new Point(i * state.XAxisLabelStep - constraintMax.Width / 2 + state.AreaMargin.Left, originTop); var offsetCenter = new Point(constraintMax.Width / 2 - constraintMax.Width / 2, 0); var xPosition = origin.X + constraintMax.Width / 2; var yPosition = origin.Y + constraintMax.Height / 2; var matrix = Matrix.CreateTranslation(-xPosition, -yPosition) * Matrix.CreateRotation(angleRadians) * Matrix.CreateTranslation(xPosition, yPosition); var labelTransform = context.PushPreTransform(matrix); var opacityState = context.PushOpacity(opacity); context.DrawText(foreground, origin + offsetCenter, formattedTextLabels[i]); opacityState.Dispose(); labelTransform.Dispose(); } offsetTransform.Dispose(); }
private LineChartState CreateChartState(double width, double height) { var state = new LineChartState(); state.ChartWidth = width; state.ChartHeight = height; state.AreaMargin = AreaMargin; state.AreaWidth = width - state.AreaMargin.Left - state.AreaMargin.Right; state.AreaHeight = height - state.AreaMargin.Top - state.AreaMargin.Bottom; var values = YAxisValues; if (values is not null && values.Count > 1) { var logarithmicScale = YAxisLogarithmicScale; var valuesList = logarithmicScale ? values.Select(y => Math.Log(y)).ToList() : values.ToList(); var valuesListMax = valuesList.Max(); var scaledValues = valuesList .Select(y => ScaleVertical(y, valuesListMax, state.AreaHeight)) .ToList(); state.Step = state.AreaWidth / (scaledValues.Count - 1); state.Points = new Point[scaledValues.Count]; for (var i = 0; i < scaledValues.Count; i++) { state.Points[i] = new Point(i * state.Step, scaledValues[i]); } }
private void DrawAreaFill(DrawingContext context, LineChartState state) { if (state.Points is null) { return; } var brush = AreaFill; if (brush is null) { return; } if (state.AreaWidth <= 0 || state.AreaHeight <= 0 || state.AreaWidth < AreaMinViableWidth || state.AreaHeight < AreaMinViableHeight) { return; } var deflate = 0.5; var geometry = CreateFillGeometry(state.Points, state.AreaWidth, state.AreaHeight); var transform = context.PushPreTransform( Matrix.CreateTranslation( state.AreaMargin.Left + deflate, state.AreaMargin.Top + deflate)); context.DrawGeometry(brush, null, geometry); transform.Dispose(); }
private void DrawAreaStroke(DrawingContext context, LineChartState state) { var brush = AreaStroke; if (brush is null || state.Points is null || state.AreaWidth <= 0 || state.AreaHeight <= 0 || state.AreaWidth < AreaMinViableWidth || state.AreaHeight < AreaMinViableHeight) { return; } var thickness = AreaStrokeThickness; var dashStyle = AreaStrokeDashStyle; var lineCap = AreaStrokeLineCap; var lineJoin = AreaStrokeLineJoin; var miterLimit = AreaStrokeMiterLimit; var pen = new Pen(brush, thickness, dashStyle, lineCap, lineJoin, miterLimit); var deflate = thickness * 0.5; var geometry = CreateStrokeGeometry(state.Points); var transform = context.PushPreTransform( Matrix.CreateTranslation( state.AreaMargin.Left + deflate, state.AreaMargin.Top + deflate)); context.DrawGeometry(null, pen, geometry); transform.Dispose(); }
private void DrawCursor(DrawingContext context, LineChartState state) { var brush = CursorStroke; if (brush is null || double.IsNaN(state.XAxisCursorPosition) || state.AreaWidth <= 0 || state.AreaHeight <= 0 || state.AreaWidth < AreaMinViableWidth || state.AreaHeight < AreaMinViableHeight) { return; } var thickness = CursorStrokeThickness; var dashStyle = CursorStrokeDashStyle; var lineCap = CursorStrokeLineCap; var lineJoin = CursorStrokeLineJoin; var miterLimit = CursorStrokeMiterLimit; var pen = new Pen(brush, thickness, dashStyle, lineCap, lineJoin, miterLimit); var deflate = thickness * 0.5; var p1 = new Point(state.XAxisCursorPosition + deflate, 0); var p2 = new Point(state.XAxisCursorPosition + deflate, state.AreaHeight); var transform = context.PushPreTransform( Matrix.CreateTranslation( state.AreaMargin.Left, state.AreaMargin.Top)); context.DrawLine(pen, p1, p2); transform.Dispose(); }
private void SetStateXAxisCursor(LineChartState state) { var xAxisMinValue = XAxisMinValue; var xAxisMaxValue = XAxisMaxValue; var xAxisCurrentValue = XAxisCurrentValue; state.XAxisCursorPosition = ScaleHorizontal(xAxisMaxValue - xAxisCurrentValue, xAxisMaxValue, state.AreaWidth); }
private void DrawXAxisLabels(DrawingContext context, LineChartState state) { if (state.XLabels is null) { return; } var foreground = XAxisLabelForeground; if (foreground is null) { return; } var opacity = XAxisLabelOpacity; var fontFamily = XAxisLabelFontFamily; var fontStyle = XAxisLabelFontStyle; var fontWeight = XAxisLabelFontWeight; var typeface = new Typeface(fontFamily, fontStyle, fontWeight); var fontSize = XAxisLabelFontSize; var offset = XAxisLabelOffset; var size = XAxisLabelSize; var angleRadians = Math.PI / 180.0 * XAxisLabelAngle; var alignment = XAxisLabelAlignment; var originTop = state.AreaHeight + state.AreaMargin.Top; var offsetTransform = context.PushPreTransform(Matrix.CreateTranslation(offset.X, offset.Y)); for (var i = 0; i < state.XLabels.Count; i++) { var origin = new Point(i * state.Step - size.Width / 2 + state.AreaMargin.Left, originTop); var constraint = new Size(size.Width, size.Height); var formattedText = CreateFormattedText(state.XLabels[i], typeface, alignment, fontSize, constraint); var xPosition = origin.X + size.Width / 2; var yPosition = origin.Y + size.Height / 2; var matrix = Matrix.CreateTranslation(-xPosition, -yPosition) * Matrix.CreateRotation(angleRadians) * Matrix.CreateTranslation(xPosition, yPosition); var labelTransform = context.PushPreTransform(matrix); var offsetCenter = new Point(0, size.Height / 2 - formattedText.Bounds.Height / 2); var opacityState = context.PushOpacity(opacity); context.DrawText(foreground, origin + offsetCenter, formattedText); #if DEBUG_LABELS context.DrawRectangle(null, new Pen(new SolidColorBrush(Colors.Magenta)), new Rect(origin, constraint)); #endif opacityState.Dispose(); labelTransform.Dispose(); #if DEBUG_LABELS context.DrawRectangle(null, new Pen(new SolidColorBrush(Colors.Cyan)), new Rect(origin, constraint)); #endif } offsetTransform.Dispose(); }
private void AutoGenerateXAxisLabels(LineChartState state) { var xAxisValues = XAxisValues; state.XAxisLabelStep = xAxisValues is null || xAxisValues.Count <= 1 ? double.NaN : state.AreaWidth / (xAxisValues.Count - 1); if (XAxisStroke is not null && XAxisValues is not null) { state.XAxisLabels = XAxisValues.Select(x => x.ToString(CultureInfo.InvariantCulture)).ToList(); } }
private void AutoGenerateYAxisLabels(LineChartState state) { var yAxisValues = YAxisValues; state.YAxisLabelStep = yAxisValues is null || yAxisValues.Count <= 1 ? double.NaN : state.AreaHeight / (yAxisValues.Count - 1); if (YAxisStroke is not null && YAxisValues is not null) { state.YAxisLabels = YAxisValues.Select(x => x.ToString(CultureInfo.InvariantCulture)).ToList(); } }
private void DrawYAxisTitle(DrawingContext context, LineChartState state) { var foreground = YAxisTitleForeground; if (foreground is null) { return; } if (state.AreaWidth <= 0 || state.AreaHeight <= 0 || state.AreaWidth < YAxisMinViableWidth || state.AreaHeight < YAxisMinViableHeight) { return; } var opacity = YAxisTitleOpacity; var fontFamily = YAxisTitleFontFamily; var fontStyle = YAxisTitleFontStyle; var fontWeight = YAxisTitleFontWeight; var typeface = new Typeface(fontFamily, fontStyle, fontWeight); var fontSize = YAxisTitleFontSize; var offset = YAxisTitleOffset; var size = YAxisTitleSize; var angleRadians = Math.PI / 180.0 * YAxisTitleAngle; var alignment = YAxisTitleAlignment; var offsetTransform = context.PushPreTransform(Matrix.CreateTranslation(offset.X, offset.Y)); var origin = new Point(state.AreaMargin.Left, state.AreaHeight + state.AreaMargin.Top); var constraint = new Size(size.Width, size.Height); var formattedText = CreateFormattedText(YAxisTitle, typeface, alignment, fontSize, constraint); var xPosition = origin.X + size.Width / 2; var yPosition = origin.Y + size.Height / 2; var matrix = Matrix.CreateTranslation(-xPosition, -yPosition) * Matrix.CreateRotation(angleRadians) * Matrix.CreateTranslation(xPosition, yPosition); var labelTransform = context.PushPreTransform(matrix); var offsetCenter = new Point(0, size.Height / 2 - formattedText.Bounds.Height / 2); var opacityState = context.PushOpacity(opacity); context.DrawText(foreground, origin + offsetCenter, formattedText); #if DEBUG_AXIS_TITLE context.DrawRectangle(null, new Pen(new SolidColorBrush(Colors.Magenta)), new Rect(origin, constraint)); #endif opacityState.Dispose(); labelTransform.Dispose(); #if DEBUG_AXIS_TITLE context.DrawRectangle(null, new Pen(new SolidColorBrush(Colors.Cyan)), new Rect(origin, constraint)); #endif offsetTransform.Dispose(); }
private LineChartState CreateChartState(double width, double height) { var state = new LineChartState(); state.ChartWidth = width; state.ChartHeight = height; state.AreaMargin = AreaMargin; state.AreaWidth = width - state.AreaMargin.Left - state.AreaMargin.Right; state.AreaHeight = height - state.AreaMargin.Top - state.AreaMargin.Bottom; var values = YAxisValues; if (values is not null) { var logarithmicScale = YAxisLogarithmicScale; var valuesList = logarithmicScale ? values.Select(y => Math.Log(y)).ToList() : values.ToList(); var valuesListMax = valuesList.Max(); var scaledValues = valuesList .Select(y => ScaleVertical(y, valuesListMax, state.AreaHeight)) .ToList(); state.Step = state.AreaWidth / (scaledValues.Count - 1); state.Points = new Point[scaledValues.Count]; for (var i = 0; i < scaledValues.Count; i++) { state.Points[i] = new Point(i * state.Step, scaledValues[i]); } } var labels = XAxisLabels; if (labels is not null) { state.XLabels = labels.ToList(); } var minValue = XAxisMinValue; var maxValue = XAxisMaxValue; var cursorValue = XAxisCurrentValue; state.CursorPosition = ScaleHorizontal(maxValue - cursorValue, maxValue, state.AreaWidth); return(state); }
private void SetStateYAxisLabels(LineChartState state) { var yAxisLabels = YAxisLabels; if (yAxisLabels is not null) { state.YAxisLabelStep = yAxisLabels.Count <= 1 ? double.NaN : state.AreaHeight / (yAxisLabels.Count - 1); state.YAxisLabels = yAxisLabels.ToList(); } else { AutoGenerateYAxisLabels(state); } }
private void SetStateXAxisLabels(LineChartState state) { var xAxisLabels = XAxisLabels; if (xAxisLabels is not null) { state.XAxisLabelStep = xAxisLabels.Count <= 1 ? double.NaN : state.AreaWidth / (xAxisLabels.Count - 1); state.XAxisLabels = xAxisLabels.ToList(); } else { AutoGenerateXAxisLabels(state); } }
private void DrawBorder(DrawingContext context, LineChartState state) { var brush = BorderBrush; if (brush is null || state.AreaWidth <= 0 || state.AreaHeight <= 0) { return; } var thickness = BorderThickness; var radiusX = BorderRadiusX; var radiusY = BorderRadiusY; var pen = new Pen(brush, thickness, null, PenLineCap.Round); var rect = new Rect(0, 0, state.ChartWidth, state.ChartHeight); var rectDeflate = rect.Deflate(thickness * 0.5); context.DrawRectangle(Brushes.Transparent, pen, rectDeflate, radiusX, radiusY); }
private void DrawYAxis(DrawingContext context, LineChartState state) { var brush = YAxisStroke; if (brush is null) { return; } if (state.AreaWidth <= 0 || state.AreaHeight <= 0 || state.AreaWidth < YAxisMinViableWidth || state.AreaHeight < YAxisMinViableHeight) { return; } var size = YAxisArrowSize; var opacity = YAxisOpacity; var thickness = YAxisStrokeThickness; var pen = new Pen(brush, thickness, null, PenLineCap.Round, PenLineJoin.Miter, 10.0); var deflate = thickness * 0.5; var offset = YAxisOffset; var p1 = new Point( state.AreaMargin.Left + offset.X + deflate, state.AreaMargin.Top); var p2 = new Point( state.AreaMargin.Left + offset.X + deflate, state.AreaMargin.Top + state.AreaHeight + offset.Y); var opacityState = context.PushOpacity(opacity); context.DrawLine(pen, p1, p2); var p3 = new Point(p1.X, p1.Y); var p4 = new Point(p1.X - size, p1.Y + size); context.DrawLine(pen, p3, p4); var p5 = new Point(p1.X, p1.Y); var p6 = new Point(p1.X + size, p1.Y + size); context.DrawLine(pen, p5, p6); opacityState.Dispose(); }
private LineChartState CreateChartState(double width, double height) { var state = new LineChartState { ChartWidth = width, ChartHeight = height, AreaMargin = AreaMargin }; state.AreaWidth = width - state.AreaMargin.Left - state.AreaMargin.Right; state.AreaHeight = height - state.AreaMargin.Top - state.AreaMargin.Bottom; SetStateAreaPoints(state); SetStateXAxisLabels(state); SetStateYAxisLabels(state); SetStateXAxisCursor(state); return(state); }
private void DrawXAxisTitle(DrawingContext context, LineChartState state) { // TODO: Draw XAxis title. }
private void SetStateXAxisCursor(LineChartState state) { state.XAxisCursorPosition = GetCursorPosition(state.AreaWidth); }