/// <summary> /// Converts a given amount of pixels to the equivalent size along the x-axis which that amount of pixels represents. /// </summary> /// <param name="pixels">The amount of pixels for which to know the size along the x-axis.</param> /// <param name="intervals">The currently visible interval.</param> /// <param name="panelSize">The amount of pixels within which the interval is shown.</param> /// <returns></returns> protected static TXSize PixelsToSize(double pixels, AxesIntervals <TX, TXSize, TY, TYSize> intervals, Size panelSize) { double intervalSize = Interval <TX, TXSize> .ConvertSizeToDouble(intervals.IntervalX.Size); double size = (pixels / panelSize.Width) * intervalSize; return(Interval <TX, TXSize> .ConvertDoubleToSize(size)); }
/// <summary> /// Converts a given size of the x-axis to the amount of pixels required to display it. /// </summary> /// <param name="size">The size on the x-axis.</param> /// <param name="intervals">The currently visible interval.</param> /// <param name="panelSize">The amount of pixels within which the interval is shown.</param> /// <returns></returns> protected static double SizeToPixels(TXSize size, AxesIntervals <TX, TXSize, TY, TYSize> intervals, Size panelSize) { double intervalSize = Interval <TX, TXSize> .ConvertSizeToDouble(intervals.IntervalX.Size); double requestedSize = Interval <TX, TXSize> .ConvertSizeToDouble(size); return(panelSize.Width * (requestedSize / intervalSize)); }
public override void LabelResized(FrameworkElement label, AxesIntervals <TX, TXSize, TY, TYSize> visible, Size panelSize) { // TODO: Can this be handled locally within this class, rather than relying on a callback from AxesPanel? // Main problems seems to be 'visible' and 'panelSize' are needed to update. var positioned = _visibleLabels.FirstOrDefault(v => v.Element == label); if (positioned != null) { UpdateLabel(positioned, visible, panelSize); } }
protected override IEnumerable <Tuple <TX, TY> > GetPositions(AxesIntervals <TX, TXSize, TY, TYSize> intervals, Size panelSize) { // When not enough pixels in between labels, do not show any labels. double pixelsBetween = SizeToPixels(MaximumLabelSize, intervals, panelSize); if (pixelsBetween < MinimumPixelsBetweenLabels) { MinimumPixelsExceeded = true; return(new Tuple <TX, TY>[] { }); } MinimumPixelsExceeded = false; return(GetXValues(intervals).Select(x => new Tuple <TX, TY>(x, FixedY))); }
protected override Tuple <TXSize, TYSize> GetMaximumLabelSize(AxesIntervals <TX, TXSize, TY, TYSize> visible) { return(new Tuple <TXSize, TYSize>(MaximumLabelSize, default(TYSize))); }
/// <summary> /// Returns all x positions on which to place labels within the specified interval. /// </summary> /// <param name="intervals">The currently visible interval.</param> protected abstract IEnumerable <TX> GetXValues(AxesIntervals <TX, TXSize, TY, TYSize> intervals);
protected override IEnumerable <TX> GetXValues(AxesIntervals <TX, TXSize, TY, TYSize> intervals) { return(intervals.IntervalX.GetValues(StepSize, Anchor)); }
/// <summary> /// Called whenever a label added by the collection was resized. /// When you position labels within a collection based on their size, this callback can be used to update their position. /// </summary> /// <param name="label">The label which was resized.</param> /// <param name="visible">The visible interval.</param> /// <param name="panelSize">The size within which the intervals are presented.</param> public abstract void LabelResized(FrameworkElement label, AxesIntervals <TX, TXSize, TY, TYSize> visible, Size panelSize);
/// <summary> /// Called whenever either the visible interval has changed, or the size within which it is presented has changed. /// </summary> /// <param name="visible">The visible interval.</param> /// <param name="limits">The maximum ranges within which all values must lie.</param> /// <param name="panelSize">The size within which the intervals are presented.</param> public abstract void VisibleIntervalChanged( AxesIntervals <TX, TXSize, TY, TYSize> visible, AxesIntervals <TX, TXSize, TY, TYSize> limits, Size panelSize);
public override void VisibleIntervalChanged( AxesIntervals <TX, TXSize, TY, TYSize> visible, AxesIntervals <TX, TXSize, TY, TYSize> limits, Size panelSize) { // Create extended intervals. Interval <TX, TXSize> intervalX = visible.IntervalX; Interval <TY, TYSize> intervalY = visible.IntervalY; Tuple <TXSize, TYSize> maxLabelSize = GetMaximumLabelSize(visible); var additionalX = Operator <TXSize> .Add(maxLabelSize.Item1, maxLabelSize.Item1); var additionalY = Operator <TYSize> .Add(maxLabelSize.Item2, maxLabelSize.Item2); var extendedX = Operator <TXSize> .Add(intervalX.Size, additionalX); var extendedY = Operator <TYSize> .Add(intervalY.Size, additionalY); double scaleX = Interval <TX, TXSize> .ConvertSizeToDouble(extendedX) / Interval <TX, TXSize> .ConvertSizeToDouble(intervalX.Size); double scaleY = Interval <TY, TYSize> .ConvertSizeToDouble(extendedY) / Interval <TY, TYSize> .ConvertSizeToDouble(intervalY.Size); Interval <TX, TXSize> scaledX = intervalX.Scale(scaleX, limits.IntervalX); Interval <TY, TYSize> scaledY = intervalY.Scale(scaleY, limits.IntervalY); var extendedIntervals = new AxesIntervals <TX, TXSize, TY, TYSize>(scaledX, scaledY); var toPosition = new HashSet <Tuple <TX, TY> >(GetPositions(extendedIntervals, panelSize)); // Free up labels which are no longer visible, and update those already positioned. var toRemove = new List <PositionedElement>(); var toUpdate = new List <PositionedElement>(); foreach (var positioned in _visibleLabels) { if (toPosition.Contains(positioned.Position)) { toUpdate.Add(positioned); toPosition.Remove(positioned.Position); } else { Remove(positioned.Element); _availableLabels.Push(positioned.Element); toRemove.Add(positioned); } } toRemove.ForEach(r => _visibleLabels.Remove(r)); // Position new labels. var toInitialize = new List <PositionedElement>(); foreach (var position in toPosition) { // Create a new label when needed, or retrieve existing one. FrameworkElement toPlace; if (_availableLabels.Count == 0) { toPlace = CreateLabel(); toPlace.CacheMode = new BitmapCache(); } else { toPlace = _availableLabels.Pop(); } toPlace.SetValue(AxesPanel <TX, TXSize, TY, TYSize> .XProperty, position.Item1); toPlace.SetValue(AxesPanel <TX, TXSize, TY, TYSize> .YProperty, position.Item2); Add(toPlace); var positioned = new PositionedElement(toPlace, position); _visibleLabels.Add(positioned); toInitialize.Add(positioned); } // Only update and initialize labels at end, so that _visibleLabels is up to date. toInitialize.ForEach(i => InitializeLabel(i, visible, panelSize)); toUpdate.ForEach(u => UpdateLabel(u, visible, panelSize)); }
/// <summary> /// Called for already positioned elements which might potentionally need to be updated based on a new visible interval. /// </summary> protected abstract void UpdateLabel(PositionedElement label, AxesIntervals <TX, TXSize, TY, TYSize> visible, Size panelSize);
/// <summary> /// Initializes a label which has just been repositioned to a certain position. /// </summary> protected abstract void InitializeLabel(PositionedElement positioned, AxesIntervals <TX, TXSize, TY, TYSize> visible, Size panelSize);
/// <summary> /// Returns all positions on which to place labels within the specified interval. /// </summary> protected abstract IEnumerable <Tuple <TX, TY> > GetPositions(AxesIntervals <TX, TXSize, TY, TYSize> intervals, Size panelSize);
/// <summary> /// Returns the maximum size a label can have given specified visible intervals. /// </summary> protected abstract Tuple <TXSize, TYSize> GetMaximumLabelSize(AxesIntervals <TX, TXSize, TY, TYSize> visible);