Skip to content

Commit 9155583

Browse files
committed
Add LabelAngle and LabelPlacement to IntervalBarSeries (#2027)
1 parent 41f4013 commit 9155583

7 files changed

Lines changed: 252 additions & 109 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ All notable changes to this project will be documented in this file.
1010
- Example for CategoryAxis with custom MajorStep and uncentered ticks (#1971)
1111
- BarSeries.LabelAngle property (#1870)
1212
- Border properties on PathAnnotation to match functionality in TextAnnotation (#1900)
13+
- Add `LabelAngle` and `LabelPlacement` to `IntervalBarSeries` (#2027)
1314

1415
### Changed
1516
- Make consistent BaseValue and BaseLine across BarSeries, LinearBarSeries, and HistogramSeries

CONTRIBUTORS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ Jonathan Arweck
7676
Jonathan Shore <jonathan.shore@gmail.com>
7777
julien.bataille
7878
Just Slon <just.slon@gmail.com>
79+
Justin Morgan <jmorgan@herbert-abs.com>
7980
Kaplas80 <kaplas80@gmail.com>
8081
kc1212 <kc04bc@gmx.com>
8182
kenny_evoleap

Source/Examples/ExampleLibrary/Series/IntervalBarSeriesExamples.cs

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,5 +49,71 @@ public static PlotModel IntervalBarSeries()
4949
model.Axes.Add(valueAxis);
5050
return model;
5151
}
52+
53+
[Example("IntervalBarSeries with all label types")]
54+
public static PlotModel IntervalBarSeriesWithLabels()
55+
{
56+
var model = new PlotModel { Title = "IntervalBarSeries" };
57+
var l = new Legend { LegendPlacement = LegendPlacement.Outside };
58+
59+
model.Legends.Add(l);
60+
61+
var s1 = new IntervalBarSeries { Title = "IntervalBarSeries 1", LabelFormatString = "{0} - {1}", LabelPlacement = LabelPlacement.Outside };
62+
s1.Items.Add(new IntervalBarItem { Start = 6, End = 8, CategoryIndex = 0 });
63+
s1.Items.Add(new IntervalBarItem { Start = 10, End = 12, CategoryIndex = 0 });
64+
model.Series.Add(s1);
65+
66+
var s2 = new IntervalBarSeries { Title = "IntervalBarSeries 2", LabelFormatString = "{0} - {1}", LabelPlacement = LabelPlacement.Inside };
67+
s2.Items.Add(new IntervalBarItem { Start = 4, End = 8, CategoryIndex = 1 });
68+
s2.Items.Add(new IntervalBarItem { Start = 10, End = 12, CategoryIndex = 1 });
69+
model.Series.Add(s2);
70+
71+
var s3 = new IntervalBarSeries { Title = "IntervalBarSeries 3", LabelFormatString = "{0} - {1}", LabelPlacement = LabelPlacement.Middle };
72+
s3.Items.Add(new IntervalBarItem { Start = 5, End = 11, CategoryIndex = 2 });
73+
s3.Items.Add(new IntervalBarItem { Start = 13, End = 17, CategoryIndex = 2 });
74+
model.Series.Add(s3);
75+
76+
var s4 = new IntervalBarSeries { Title = "IntervalBarSeries 4", LabelFormatString = "{0} - {1}", LabelPlacement = LabelPlacement.Base };
77+
s4.Items.Add(new IntervalBarItem { Start = 4, End = 12, CategoryIndex = 3 });
78+
s4.Items.Add(new IntervalBarItem { Start = 13, End = 17, CategoryIndex = 3 });
79+
model.Series.Add(s4);
80+
81+
var s5 = new IntervalBarSeries { Title = "IntervalBarSeries 5", LabelFormatString = "{0} - {1}", LabelPlacement = LabelPlacement.Outside, LabelAngle = -45 };
82+
s5.Items.Add(new IntervalBarItem { Start = 6, End = 8, CategoryIndex = 4 });
83+
s5.Items.Add(new IntervalBarItem { Start = 10, End = 12, CategoryIndex = 4 });
84+
model.Series.Add(s5);
85+
86+
var s6 = new IntervalBarSeries { Title = "IntervalBarSeries 6", LabelFormatString = "{0} - {1}", LabelPlacement = LabelPlacement.Inside, LabelAngle = -45 };
87+
s6.Items.Add(new IntervalBarItem { Start = 4, End = 8, CategoryIndex = 5 });
88+
s6.Items.Add(new IntervalBarItem { Start = 10, End = 12, CategoryIndex = 5 });
89+
model.Series.Add(s6);
90+
91+
var s7 = new IntervalBarSeries { Title = "IntervalBarSeries 7", LabelFormatString = "{0} - {1}", LabelPlacement = LabelPlacement.Middle, LabelAngle = -45 };
92+
s7.Items.Add(new IntervalBarItem { Start = 5, End = 11, CategoryIndex = 6 });
93+
s7.Items.Add(new IntervalBarItem { Start = 13, End = 17, CategoryIndex = 6 });
94+
model.Series.Add(s7);
95+
96+
var s8 = new IntervalBarSeries { Title = "IntervalBarSeries 8", LabelFormatString = "{0} - {1}", LabelPlacement = LabelPlacement.Base, LabelAngle = -45 };
97+
s8.Items.Add(new IntervalBarItem { Start = 4, End = 12, CategoryIndex = 7 });
98+
s8.Items.Add(new IntervalBarItem { Start = 13, End = 17, CategoryIndex = 7 });
99+
model.Series.Add(s8);
100+
101+
var categoryAxis = new CategoryAxis { Key = "CategoryAxis", Position = AxisPosition.Left, StartPosition = 1, EndPosition = 0 };
102+
categoryAxis.Labels.Add("Label Outside");
103+
categoryAxis.Labels.Add("Label Inside");
104+
categoryAxis.Labels.Add("Label Middle");
105+
categoryAxis.Labels.Add("Label Base");
106+
categoryAxis.Labels.Add("Label Outside (angled)");
107+
categoryAxis.Labels.Add("Label Inside (angled)");
108+
categoryAxis.Labels.Add("Label Middle (angled)");
109+
categoryAxis.Labels.Add("Label Base (angled)");
110+
111+
var valueAxis = new LinearAxis { Key = "ValueAxis", Position = AxisPosition.Bottom, MinimumPadding = 0.1, MaximumPadding = 0.1 };
112+
113+
model.Axes.Add(categoryAxis);
114+
model.Axes.Add(valueAxis);
115+
116+
return model;
117+
}
52118
}
53119
}

Source/OxyPlot/Series/BarSeries/BarSeries.cs

Lines changed: 22 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -87,26 +87,37 @@ public BarSeries()
8787
/// <inheritdoc/>
8888
public bool OverlapsStack { get; set; }
8989

90-
/// <summary>
91-
/// Gets or sets the label format string.
92-
/// </summary>
93-
/// <value>The label format string.</value>
94-
public string LabelFormatString { get; set; }
90+
/// <inheritdoc cref="BarSeriesBase{T}.LabelFormatString"/>
91+
public new string LabelFormatString
92+
{
93+
get => base.LabelFormatString;
94+
set => base.LabelFormatString = value;
95+
}
9596

9697
/// <summary>
9798
/// Gets or sets the label margins. Default value is 2.
9899
/// </summary>
99-
public double LabelMargin { get; set; }
100+
public new double LabelMargin
101+
{
102+
get => base.LabelMargin;
103+
set => base.LabelMargin = value;
104+
}
100105

101106
/// <summary>
102107
/// Gets or sets the label angle in degrees. Default value is 0.
103108
/// </summary>
104-
public double LabelAngle { get; set; }
109+
public new double LabelAngle
110+
{
111+
get => base.LabelAngle;
112+
set => base.LabelAngle = value;
113+
}
105114

106-
/// <summary>
107-
/// Gets or sets label placements.
108-
/// </summary>
109-
public LabelPlacement LabelPlacement { get; set; }
115+
/// <inheritdoc cref="BarSeriesBase{T}.LabelPlacement"/>
116+
public new LabelPlacement LabelPlacement
117+
{
118+
get => base.LabelPlacement;
119+
set => base.LabelPlacement = value;
120+
}
110121

111122
/// <summary>
112123
/// Gets or sets the color of the interior of the bars when the value is negative.
@@ -345,74 +356,6 @@ protected virtual void RenderItem(
345356
this.EdgeRenderingMode.GetActual(EdgeRenderingMode.PreferSharpness));
346357
}
347358

348-
/// <summary>
349-
/// Renders the item label.
350-
/// </summary>
351-
/// <param name="rc">The render context</param>
352-
/// <param name="item">The item.</param>
353-
/// <param name="baseValue">The bar item base value.</param>
354-
/// <param name="topValue">The bar item top value.</param>
355-
/// <param name="categoryValue">The bar item category value.</param>
356-
/// <param name="categoryEndValue">The bar item category end value.</param>
357-
protected void RenderLabel(
358-
IRenderContext rc,
359-
BarItem item,
360-
double baseValue,
361-
double topValue,
362-
double categoryValue,
363-
double categoryEndValue)
364-
{
365-
var s = StringHelper.Format(this.ActualCulture, this.LabelFormatString, item, item.Value);
366-
ScreenPoint pt;
367-
var y = (categoryEndValue + categoryValue) / 2;
368-
var sign = Math.Sign(topValue - baseValue);
369-
var marginVector = new ScreenVector(this.LabelMargin, 0) * sign;
370-
var centreVector = new ScreenVector(0, 0);
371-
372-
var size = rc.MeasureText(
373-
s,
374-
this.ActualFont,
375-
this.ActualFontSize,
376-
this.ActualFontWeight,
377-
this.LabelAngle);
378-
379-
switch (this.LabelPlacement)
380-
{
381-
case LabelPlacement.Inside:
382-
pt = this.Transform(topValue, y);
383-
marginVector = -marginVector;
384-
centreVector = new ScreenVector(-sign * size.Width / 2, 0);
385-
break;
386-
case LabelPlacement.Outside:
387-
pt = this.Transform(topValue, y);
388-
centreVector = new ScreenVector(sign * size.Width / 2, 0);
389-
break;
390-
case LabelPlacement.Middle:
391-
pt = this.Transform((topValue + baseValue) / 2, y);
392-
marginVector = new ScreenVector(0, 0);
393-
break;
394-
case LabelPlacement.Base:
395-
pt = this.Transform(baseValue, y);
396-
centreVector = new ScreenVector(sign * size.Width / 2, 0);
397-
break;
398-
default:
399-
throw new ArgumentOutOfRangeException();
400-
}
401-
402-
pt += this.Orientate(marginVector) + this.Orientate(centreVector);
403-
404-
rc.DrawText(
405-
pt,
406-
s,
407-
this.ActualTextColor,
408-
this.ActualFont,
409-
this.ActualFontSize,
410-
this.ActualFontWeight,
411-
this.LabelAngle,
412-
HorizontalAlignment.Center,
413-
VerticalAlignment.Middle);
414-
}
415-
416359
/// <inheritdoc/>
417360
public override void Render(IRenderContext rc)
418361
{

Source/OxyPlot/Series/BarSeries/BarSeriesBase.cs

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,31 @@ protected BarSeriesBase()
100100
/// </summary>
101101
protected Dictionary<int, int> ValidItemsIndexInversion { get; } = new Dictionary<int, int>();
102102

103+
/// <summary>
104+
/// Gets or sets the label color.
105+
/// </summary>
106+
protected OxyColor LabelColor { get; set; }
107+
108+
/// <summary>
109+
/// Gets or sets the label format string.
110+
/// </summary>
111+
protected string LabelFormatString { get; set; }
112+
113+
/// <summary>
114+
/// Gets or sets the label margins.
115+
/// </summary>
116+
protected double LabelMargin { get; set; }
117+
118+
/// <summary>
119+
/// Gets or sets the label angle in degrees.
120+
/// </summary>
121+
protected double LabelAngle { get; set; }
122+
123+
/// <summary>
124+
/// Gets or sets label placements.
125+
/// </summary>
126+
protected LabelPlacement LabelPlacement { get; set; }
127+
103128
/// <summary>
104129
/// Gets the actual width of the items of this series.
105130
/// </summary>
@@ -176,6 +201,91 @@ protected internal override bool IsUsing(Axis axis)
176201
{
177202
return this.XAxis == axis || this.YAxis == axis;
178203
}
204+
205+
/// <summary>
206+
/// Renders the item label.
207+
/// </summary>
208+
/// <param name="rc">The render context</param>
209+
/// <param name="item">The item.</param>
210+
/// <param name="baseValue">The bar item base value.</param>
211+
/// <param name="topValue">The bar item top value.</param>
212+
/// <param name="categoryValue">The bar item category value.</param>
213+
/// <param name="categoryEndValue">The bar item category end value.</param>
214+
protected void RenderLabel(
215+
IRenderContext rc,
216+
T item,
217+
double baseValue,
218+
double topValue,
219+
double categoryValue,
220+
double categoryEndValue)
221+
{
222+
var v = new List<object>();
223+
switch (item)
224+
{
225+
case BarItem barItem:
226+
v.Add(barItem.Value);
227+
break;
228+
229+
case IntervalBarItem intervalBarItem:
230+
v.Add(intervalBarItem.Start);
231+
v.Add(intervalBarItem.End);
232+
break;
233+
234+
default:
235+
throw new NotImplementedException($"RenderLabel not implemented for {this.GetType().Name}");
236+
}
237+
238+
var s = StringHelper.Format(this.ActualCulture, this.LabelFormatString, item, v.ToArray());
239+
ScreenPoint pt;
240+
var y = (categoryEndValue + categoryValue) / 2;
241+
var sign = Math.Sign(topValue - baseValue);
242+
var marginVector = new ScreenVector(this.LabelMargin, 0) * sign;
243+
var centreVector = new ScreenVector(0, 0);
244+
245+
var size = rc.MeasureText(
246+
s,
247+
this.ActualFont,
248+
this.ActualFontSize,
249+
this.ActualFontWeight,
250+
this.LabelAngle);
251+
var halfSize = (this.IsTransposed() ? size.Height : size.Width) / 2;
252+
253+
switch (this.LabelPlacement)
254+
{
255+
case LabelPlacement.Inside:
256+
pt = this.Transform(topValue, y);
257+
marginVector = -marginVector;
258+
centreVector = new ScreenVector(-sign * halfSize, 0);
259+
break;
260+
case LabelPlacement.Outside:
261+
pt = this.Transform(topValue, y);
262+
centreVector = new ScreenVector(sign * halfSize, 0);
263+
break;
264+
case LabelPlacement.Middle:
265+
pt = this.Transform((topValue + baseValue) / 2, y);
266+
marginVector = new ScreenVector(0, 0);
267+
break;
268+
case LabelPlacement.Base:
269+
pt = this.Transform(baseValue, y);
270+
centreVector = new ScreenVector(sign * halfSize, 0);
271+
break;
272+
default:
273+
throw new ArgumentOutOfRangeException();
274+
}
275+
276+
pt += this.Orientate(marginVector) + this.Orientate(centreVector);
277+
278+
rc.DrawText(
279+
pt,
280+
s,
281+
this.ActualTextColor,
282+
this.ActualFont,
283+
this.ActualFontSize,
284+
this.ActualFontWeight,
285+
this.LabelAngle,
286+
HorizontalAlignment.Center,
287+
VerticalAlignment.Middle);
288+
}
179289

180290
/// <inheritdoc/>
181291
protected internal override void UpdateAxisMaxMin()

0 commit comments

Comments
 (0)