Beispiel #1
0
        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]);
            }
        }
Beispiel #2
0
        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();
        }
Beispiel #3
0
        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]);
                }
            }
Beispiel #4
0
        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();
        }
Beispiel #5
0
        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();
        }
Beispiel #6
0
        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();
        }
Beispiel #7
0
        private void SetStateXAxisCursor(LineChartState state)
        {
            var xAxisMinValue     = XAxisMinValue;
            var xAxisMaxValue     = XAxisMaxValue;
            var xAxisCurrentValue = XAxisCurrentValue;

            state.XAxisCursorPosition = ScaleHorizontal(xAxisMaxValue - xAxisCurrentValue, xAxisMaxValue, state.AreaWidth);
        }
Beispiel #8
0
        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();
        }
Beispiel #9
0
        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();
            }
        }
Beispiel #10
0
        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();
            }
        }
Beispiel #11
0
        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();
        }
Beispiel #12
0
        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);
        }
Beispiel #13
0
        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);
            }
        }
Beispiel #14
0
        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);
            }
        }
Beispiel #15
0
        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);
        }
Beispiel #16
0
        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();
        }
Beispiel #17
0
        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);
        }
Beispiel #18
0
 private void DrawXAxisTitle(DrawingContext context, LineChartState state)
 {
     // TODO: Draw XAxis title.
 }
Beispiel #19
0
 private void SetStateXAxisCursor(LineChartState state)
 {
     state.XAxisCursorPosition = GetCursorPosition(state.AreaWidth);
 }