-
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