Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

StepLineSeries #1744

Open
JuanSilili opened this issue Dec 26, 2024 · 2 comments
Open

StepLineSeries #1744

JuanSilili opened this issue Dec 26, 2024 · 2 comments

Comments

@JuanSilili
Copy link

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); }
///
public override void Invalidate(Chart chart)
{
var cartesianChart = (CartesianChart)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;

}
`

@beto-rodriguez
Copy link
Owner

Hello and thanks for the issue!

Sorry I am afraid i don't completely understand, what is the problem you are facing?

@JuanSilili
Copy link
Author

Snipaste_2024-12-27_13-28-40
two point (1900,5) (1910,10),This is what it looks like now。
Snipaste_2024-12-27_13-33-53

two point (1900,5) (1910,10), This is what I want to achieve

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants