- 
                Notifications
    
You must be signed in to change notification settings  - Fork 26
 
Open
Description
I want to implement StepLineSeries' step-drawing inversion. For example, for two points of the same data source, now the X-axis line is drawn first and then the Y-axis line, can you add an attribute to control the Y-axis line first and then the X-axis line when drawing.
I add an attribute 'IsReverse ',then I modified part of the code in the function Invalidate to reverse the order of the coordinate sequence,This will achieve the effect I want.
`    private bool _isReverse = false;
/// 
/// Reverse step line
///
public bool IsReverse { get => _isReverse; set => SetProperty(ref _isReverse, value); }
/// <inheritdoc cref="ChartElement{TDrawingContext}.Invalidate(Chart{TDrawingContext})"/>
public override void Invalidate(Chart<TDrawingContext> chart)
{
    var cartesianChart = (CartesianChart<TDrawingContext>)chart;    
    var primaryAxis = cartesianChart.YAxes[ScalesYAt];
    var secondaryAxis = cartesianChart.XAxes[ScalesXAt];
    var drawLocation = cartesianChart.DrawMarginLocation;
    var drawMarginSize = cartesianChart.DrawMarginSize;
    var secondaryScale = secondaryAxis.GetNextScaler(cartesianChart);
    var primaryScale = primaryAxis.GetNextScaler(cartesianChart);
    var actualSecondaryScale = secondaryAxis.GetActualScaler(cartesianChart);
    var actualPrimaryScale = primaryAxis.GetActualScaler(cartesianChart);
    var gs = _geometrySize;
    var hgs = gs / 2f;
    var sw = Stroke?.StrokeThickness ?? 0;
    var p = primaryScale.ToPixels(pivot);
    // see note #240222
    var segments = _enableNullSplitting
        ? Fetch(cartesianChart).SplitByNullGaps(point => DeleteNullPoint(point, secondaryScale, primaryScale))
        : [Fetch(cartesianChart)];
    var stacker = (SeriesProperties & SeriesProperties.Stacked) == SeriesProperties.Stacked
        ? cartesianChart.SeriesContext.GetStackPosition(this, GetStackGroup())
        : null;
    var actualZIndex = ZIndex == 0 ? ((ISeries)this).SeriesId : ZIndex;
    var clipping = GetClipRectangle(cartesianChart);
    if (stacker is not null)
    {
        // see note #010621
        actualZIndex = 1000 - stacker.Position;
        if (Fill is not null) Fill.ZIndex = actualZIndex;
        if (Stroke is not null) Stroke.ZIndex = actualZIndex;
    }
    var dls = (float)DataLabelsSize;
    var segmentI = 0;
    var pointsCleanup = ChartPointCleanupContext.For(everFetched);
    if (!_strokePathHelperDictionary.TryGetValue(chart.Canvas.Sync, out var strokePathHelperContainer))
    {
        strokePathHelperContainer = [];
        _strokePathHelperDictionary[chart.Canvas.Sync] = strokePathHelperContainer;
    }
    if (!_fillPathHelperDictionary.TryGetValue(chart.Canvas.Sync, out var fillPathHelperContainer))
    {
        fillPathHelperContainer = [];
        _fillPathHelperDictionary[chart.Canvas.Sync] = fillPathHelperContainer;
    }
    var uwx = secondaryScale.MeasureInPixels(secondaryAxis.UnitWidth);
    uwx = uwx < gs ? gs : uwx;
    var hasSvg = this.HasVariableSvgGeometry();
    var isFirstDraw = !chart._drawnSeries.Contains(((ISeries)this).SeriesId);
    foreach (var segment in segments)
    {
        var hasPaths = false;
        var isSegmentEmpty = true;
        VectorManager<StepLineSegment, TDrawingContext>? strokeVector = null, fillVector = null;
        double previousPrimary = 0, previousSecondary = 0;
        **//Reverse cooordinate for step line
        List<ChartPoint> Reversepoint = new();
        foreach (var point in segment)
        {
            Reversepoint.Add(point);
        }
        if (_isReverse)
        {
            Reversepoint.Reverse();
        }
        //    foreach (var point in segment) //old code
        foreach (var point in Reversepoint)**
        {
            if (!hasPaths)
            {
                hasPaths = true;
                var fillLookup = GetSegmentVisual(segmentI, fillPathHelperContainer, VectorClosingMethod.CloseToPivot);
                var strokeLookup = GetSegmentVisual(segmentI, strokePathHelperContainer, VectorClosingMethod.NotClosed);
                if (fillLookup.Path.Commands.Count == 1)
                {
                    Fill?.RemoveGeometryFromPainTask(cartesianChart.Canvas, fillLookup.Path);
                    fillLookup.Path.Commands.Clear();
                    fillPathHelperContainer.RemoveAt(segmentI);
                    fillLookup = GetSegmentVisual(segmentI, fillPathHelperContainer, VectorClosingMethod.CloseToPivot);
                }
                if (strokeLookup.Path.Commands.Count == 1)
                {
                    Stroke?.RemoveGeometryFromPainTask(cartesianChart.Canvas, strokeLookup.Path);
                    strokeLookup.Path.Commands.Clear();
                    strokePathHelperContainer.RemoveAt(segmentI);
                    strokeLookup = GetSegmentVisual(segmentI, strokePathHelperContainer, VectorClosingMethod.NotClosed);
                }
                var isNew = fillLookup.IsNew || strokeLookup.IsNew;
                var fillPath = fillLookup.Path;
                var strokePath = strokeLookup.Path;
                strokeVector = new VectorManager<StepLineSegment, TDrawingContext>(strokePath);
                fillVector = new VectorManager<StepLineSegment, TDrawingContext>(fillPath);
                if (Fill is not null)
                {
                    Fill.AddGeometryToPaintTask(cartesianChart.Canvas, fillPath);
                    cartesianChart.Canvas.AddDrawableTask(Fill);
                    Fill.ZIndex = actualZIndex + 0.1;
                    Fill.SetClipRectangle(cartesianChart.Canvas, clipping);
                    fillPath.Pivot = p;
                    if (isNew)
                    {
                        fillPath.Animate(EasingFunction ?? cartesianChart.EasingFunction, AnimationsSpeed ?? cartesianChart.AnimationsSpeed);
                    }
                }
                if (Stroke is not null)
                {
                    Stroke.AddGeometryToPaintTask(cartesianChart.Canvas, strokePath);
                    cartesianChart.Canvas.AddDrawableTask(Stroke);
                    Stroke.ZIndex = actualZIndex + 0.2;
                    Stroke.SetClipRectangle(cartesianChart.Canvas, clipping);
                    strokePath.Pivot = p;
                    if (isNew)
                    {
                        strokePath.Animate(EasingFunction ?? cartesianChart.EasingFunction, AnimationsSpeed ?? cartesianChart.AnimationsSpeed);
                    }
                }
                strokePath.Opacity = IsVisible ? 1 : 0;
                fillPath.Opacity = IsVisible ? 1 : 0;
            }
            var coordinate = point.Coordinate;
            isSegmentEmpty = false;
            var s = 0d;
            if (stacker is not null)
                s = coordinate.PrimaryValue > 0
                    ? stacker.GetStack(point).Start
                    : stacker.GetStack(point).NegativeStart;
            var visual = (StepLineVisualPoint<TDrawingContext, TVisual>?)point.Context.AdditionalVisuals;
            var dp = coordinate.PrimaryValue + s - previousPrimary;
            var ds = coordinate.SecondaryValue - previousSecondary;
            if (!IsVisible)
            {
                if (visual is not null)
                {
                    visual.Geometry.X = secondaryScale.ToPixels(coordinate.SecondaryValue);
                    visual.Geometry.Y = p;
                    visual.Geometry.Opacity = 0;
                    visual.Geometry.RemoveOnCompleted = true;
                    visual.StepSegment.Xi = secondaryScale.ToPixels(coordinate.SecondaryValue - ds);
                    visual.StepSegment.Xj = secondaryScale.ToPixels(coordinate.SecondaryValue);
                    visual.StepSegment.Yi = p;
                    visual.StepSegment.Yj = p;
                    point.Context.Visual = null;
                }
                pointsCleanup.Clean(point);
                continue;
            }
            if (visual is null)
            {
                var v = new StepLineVisualPoint<TDrawingContext, TVisual>();
                visual = v;
                if (isFirstDraw)
                {
                    v.Geometry.X = secondaryScale.ToPixels(coordinate.SecondaryValue);
                    v.Geometry.Y = p;
                    v.Geometry.Width = 0;
                    v.Geometry.Height = 0;
                    //v.StepSegment.Xi = secondaryScale.ToPixels(coordinate.SecondaryValue - ds);
                    //v.StepSegment.Xj = secondaryScale.ToPixels(coordinate.SecondaryValue);
                    //v.StepSegment.Yi = p;
                    //v.StepSegment.Yj = p;
                    v.StepSegment.Yi = primaryScale.ToPixels(coordinate.PrimaryValue + s - dp);
                    v.StepSegment.Yj = primaryScale.ToPixels(coordinate.PrimaryValue + s);
                    v.StepSegment.Xi = p;
                    v.StepSegment.Xj = p;
                }
                point.Context.Visual = v.Geometry;
                point.Context.AdditionalVisuals = v;
                OnPointCreated(point);
            }
            visual.Geometry.Opacity = 1;
            if (hasSvg)
            {
                var svgVisual = (IVariableSvgPath<TDrawingContext>)visual.Geometry;
                if (_geometrySvgChanged || svgVisual.SVGPath is null)
                    svgVisual.SVGPath = GeometrySvg ?? throw new Exception("svg path is not defined");
            }
            _ = everFetched.Add(point);
            GeometryFill?.AddGeometryToPaintTask(cartesianChart.Canvas, visual.Geometry);
            GeometryStroke?.AddGeometryToPaintTask(cartesianChart.Canvas, visual.Geometry);
            visual.StepSegment.Id = point.Context.Entity.MetaData!.EntityIndex;
            if (Fill is not null) fillVector!.AddConsecutiveSegment(visual.StepSegment, !isFirstDraw);
            if (Stroke is not null) strokeVector!.AddConsecutiveSegment(visual.StepSegment, !isFirstDraw);
            visual.StepSegment.Xi = secondaryScale.ToPixels(coordinate.SecondaryValue - ds);
            visual.StepSegment.Xj = secondaryScale.ToPixels(coordinate.SecondaryValue);
            visual.StepSegment.Yi = primaryScale.ToPixels(coordinate.PrimaryValue + s - dp);
            visual.StepSegment.Yj = primaryScale.ToPixels(coordinate.PrimaryValue + s);
            var x = secondaryScale.ToPixels(coordinate.SecondaryValue);
            var y = primaryScale.ToPixels(coordinate.PrimaryValue + s);
            visual.Geometry.MotionProperties[nameof(visual.Geometry.X)]
                .CopyFrom(visual.StepSegment.MotionProperties[nameof(visual.StepSegment.Xj)]);
            visual.Geometry.MotionProperties[nameof(visual.Geometry.Y)]
                .CopyFrom(visual.StepSegment.MotionProperties[nameof(visual.StepSegment.Yj)]);
            visual.Geometry.TranslateTransform = new LvcPoint(-hgs, -hgs);
            visual.Geometry.Width = gs;
            visual.Geometry.Height = gs;
            visual.Geometry.RemoveOnCompleted = false;
            visual.FillPath = fillVector!.AreaGeometry;
            visual.StrokePath = strokeVector!.AreaGeometry;
            var hags = gs < 8 ? 8 : gs;
            if (point.Context.HoverArea is not RectangleHoverArea ha)
                point.Context.HoverArea = ha = new RectangleHoverArea();
            _ = ha
                .SetDimensions(x - uwx * 0.5f, y - hgs, uwx, gs)
                .CenterXToolTip();
            _ = coordinate.PrimaryValue >= pivot
                ? ha.StartYToolTip()
                : ha.EndYToolTip().IsLessThanPivot();
            pointsCleanup.Clean(point);
            if (DataLabelsPaint is not null)
            {
                var label = (TLabel?)point.Context.Label;
                if (label is null)
                {
                    var l = new TLabel { X = x - hgs, Y = p - hgs, RotateTransform = (float)DataLabelsRotation, MaxWidth = (float)DataLabelsMaxWidth };
                    l.Animate(EasingFunction ?? cartesianChart.EasingFunction, AnimationsSpeed ?? cartesianChart.AnimationsSpeed);
                    label = l;
                    point.Context.Label = l;
                }
                DataLabelsPaint.AddGeometryToPaintTask(cartesianChart.Canvas, label);
                label.Text = DataLabelsFormatter(new ChartPoint<TModel, TVisual, TLabel>(point));
                label.TextSize = dls;
                label.Padding = DataLabelsPadding;
                if (isFirstDraw)
                    label.CompleteTransition(
                        nameof(label.TextSize), nameof(label.X), nameof(label.Y), nameof(label.RotateTransform));
                var m = label.Measure(DataLabelsPaint);
                var labelPosition = GetLabelPosition(
                    x - hgs, y - hgs, gs, gs, m, DataLabelsPosition,
                    SeriesProperties, coordinate.PrimaryValue > Pivot, drawLocation, drawMarginSize);
                if (DataLabelsTranslate is not null) label.TranslateTransform =
                    new LvcPoint(m.Width * DataLabelsTranslate.Value.X, m.Height * DataLabelsTranslate.Value.Y);
                label.X = labelPosition.X;
                label.Y = labelPosition.Y;
            }
            OnPointMeasured(point);
            previousPrimary = coordinate.PrimaryValue + s;
            previousSecondary = coordinate.SecondaryValue;
        }
        strokeVector?.End();
        fillVector?.End();
        if (GeometryFill is not null)
        {
            cartesianChart.Canvas.AddDrawableTask(GeometryFill);
            GeometryFill.SetClipRectangle(cartesianChart.Canvas, clipping);
            GeometryFill.ZIndex = actualZIndex + 0.3;
        }
        if (GeometryStroke is not null)
        {
            cartesianChart.Canvas.AddDrawableTask(GeometryStroke);
            GeometryStroke.SetClipRectangle(cartesianChart.Canvas, clipping);
            GeometryStroke.ZIndex = actualZIndex + 0.4;
        }
        if (!isSegmentEmpty) segmentI++;
    }
    var maxSegment = fillPathHelperContainer.Count > strokePathHelperContainer.Count
        ? fillPathHelperContainer.Count
        : strokePathHelperContainer.Count;
    for (var i = maxSegment - 1; i >= segmentI; i--)
    {
        if (i < fillPathHelperContainer.Count)
        {
            var segmentFill = fillPathHelperContainer[i];
            Fill?.RemoveGeometryFromPainTask(cartesianChart.Canvas, segmentFill);
            segmentFill.Commands.Clear();
            fillPathHelperContainer.RemoveAt(i);
        }
        if (i < strokePathHelperContainer.Count)
        {
            var segmentStroke = strokePathHelperContainer[i];
            Stroke?.RemoveGeometryFromPainTask(cartesianChart.Canvas, segmentStroke);
            segmentStroke.Commands.Clear();
            strokePathHelperContainer.RemoveAt(i);
        }
    }
    if (DataLabelsPaint is not null)
    {
        cartesianChart.Canvas.AddDrawableTask(DataLabelsPaint);
        DataLabelsPaint.SetClipRectangle(cartesianChart.Canvas, clipping);
        DataLabelsPaint.ZIndex = actualZIndex + 0.5;
    }
    pointsCleanup.CollectPoints(
        everFetched, cartesianChart.View, primaryScale, secondaryScale, SoftDeleteOrDisposePoint);
    _geometrySvgChanged = false;
}
`
Metadata
Metadata
Assignees
Labels
No labels