Skip to content

Commit c4a2477

Browse files
committed
Added support for labels in pie and doughnut charts
Issue: eclipse#286
1 parent 0f9c58b commit c4a2477

File tree

8 files changed

+172
-2
lines changed

8 files changed

+172
-2
lines changed

org.eclipse.swtchart.examples/src/org/eclipse/swtchart/examples/MultiLevelDoughnutChart.java

+3
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import org.eclipse.swt.widgets.Shell;
2020
import org.eclipse.swtchart.Chart;
2121
import org.eclipse.swtchart.ICircularSeries;
22+
import org.eclipse.swtchart.ICircularSeriesLabel;
2223
import org.eclipse.swtchart.ISeries.SeriesType;
2324

2425
/**
@@ -77,6 +78,8 @@ static public Chart createChart(Composite parent) {
7778
ICircularSeries<?> multiLevelDoughnut = (ICircularSeries<?>)chart.getSeriesSet().createSeries(SeriesType.DOUGHNUT, "countries");
7879
// sets the series.
7980
multiLevelDoughnut.setSeries(continentLabels, continentValues);
81+
multiLevelDoughnut.getLabel().setVisible(true);
82+
multiLevelDoughnut.getLabel().setPosition(ICircularSeriesLabel.Position.Inside);
8083
// adding Asian countries. These go in as second level
8184
multiLevelDoughnut.getNodeById("Asia").addChildren(AsianCountriesLabels, AsianCountriesValues);
8285
//

org.eclipse.swtchart.examples/src/org/eclipse/swtchart/examples/SingleLevelDoughnutChartExample.java

+6
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import org.eclipse.swt.widgets.Shell;
2121
import org.eclipse.swtchart.Chart;
2222
import org.eclipse.swtchart.ICircularSeries;
23+
import org.eclipse.swtchart.ICircularSeriesLabel;
2324
import org.eclipse.swtchart.ISeries.SeriesType;
2425

2526
/**
@@ -73,6 +74,11 @@ static public Chart createChart(Composite parent) {
7374
// change color of India to DARK_RED
7475
Color color = Display.getDefault().getSystemColor(SWT.COLOR_DARK_RED);
7576
circularSeries.setColor("India", color);
77+
78+
circularSeries.getLabel().setVisible(true);
79+
circularSeries.getLabel().setPosition(ICircularSeriesLabel.Position.Inside);
80+
circularSeries.getLabel().setForeground(Display.getDefault().getSystemColor(SWT.COLOR_DARK_RED));
81+
circularSeries.getLabel().setLabelProvider(node -> String.format("%s (%.1f%%)", node.getId(), node.getValue() / node.getParent().getValue() * 100));
7682
//
7783
return chart;
7884
}

org.eclipse.swtchart/src/org/eclipse/swtchart/ICircularSeries.java

+3
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@
2424
*/
2525
public interface ICircularSeries<T> extends ISeries<T> {
2626

27+
@Override
28+
ICircularSeriesLabel getLabel();
29+
2730
/**
2831
* gets the label series
2932
*
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package org.eclipse.swtchart;
2+
3+
import java.util.function.Function;
4+
5+
import org.eclipse.swtchart.model.Node;
6+
7+
public interface ICircularSeriesLabel extends ISeriesLabel {
8+
9+
public enum Position {
10+
Inside, Outside
11+
}
12+
13+
void setPosition(Position position);
14+
15+
Position getPosition();
16+
17+
void setLabelProvider(Function<Node, String> labelProvider);
18+
}

org.eclipse.swtchart/src/org/eclipse/swtchart/internal/series/CircularSeries.java

+9
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import org.eclipse.swtchart.Chart;
2222
import org.eclipse.swtchart.IAxis;
2323
import org.eclipse.swtchart.ICircularSeries;
24+
import org.eclipse.swtchart.ICircularSeriesLabel;
2425
import org.eclipse.swtchart.internal.axis.Axis;
2526
import org.eclipse.swtchart.internal.compress.Compress;
2627
import org.eclipse.swtchart.internal.compress.CompressCircularSeries;
@@ -47,6 +48,7 @@ public CircularSeries(Chart chart, String id) {
4748

4849
super(chart, id);
4950
this.chart = chart;
51+
this.seriesLabel = new CircularSeriesLabel();
5052
initialise();
5153
model = new IdNodeDataModel(id);
5254
rootNode = model.getRootNode();
@@ -58,6 +60,12 @@ public CircularSeries(Chart chart, String id) {
5860
borderStyle = SWT.LINE_SOLID;
5961
}
6062

63+
@Override
64+
public ICircularSeriesLabel getLabel() {
65+
66+
return (ICircularSeriesLabel)super.getLabel();
67+
}
68+
6169
@Override
6270
public Color getBorderColor() {
6371

@@ -106,6 +114,7 @@ public Compress getCompressor() {
106114
return (Compress)compressor;
107115
}
108116

117+
@Override
109118
public Node getRootNode() {
110119

111120
return rootNode;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
package org.eclipse.swtchart.internal.series;
2+
3+
import java.util.function.Function;
4+
5+
import org.eclipse.swt.graphics.Font;
6+
import org.eclipse.swt.graphics.GC;
7+
import org.eclipse.swt.graphics.Point;
8+
import org.eclipse.swt.graphics.Rectangle;
9+
import org.eclipse.swt.graphics.Transform;
10+
import org.eclipse.swtchart.IAxis;
11+
import org.eclipse.swtchart.ICircularSeriesLabel;
12+
import org.eclipse.swtchart.internal.axis.Axis;
13+
import org.eclipse.swtchart.model.Node;
14+
15+
public class CircularSeriesLabel extends SeriesLabel implements ICircularSeriesLabel {
16+
17+
private Position position = Position.Inside;
18+
private Function<Node, String> labelProvider = Node::getId;
19+
20+
public CircularSeriesLabel() {
21+
22+
}
23+
24+
@Override
25+
public void setPosition(Position position) {
26+
27+
this.position = position;
28+
}
29+
30+
@Override
31+
public Position getPosition() {
32+
33+
return position;
34+
}
35+
36+
@Override
37+
public void setLabelProvider(Function<Node, String> labelProvider) {
38+
39+
this.labelProvider = labelProvider;
40+
}
41+
42+
/* package */ void draw(GC gc, Node node, int level, Axis xAxis, Axis yAxis) {
43+
44+
if(!isVisible())
45+
return;
46+
switch(position) {
47+
case Inside:
48+
drawInside(gc, node, level, xAxis, yAxis);
49+
break;
50+
case Outside:
51+
drawOutside(gc, node, level, xAxis, yAxis);
52+
break;
53+
default:
54+
throw new IllegalArgumentException();
55+
}
56+
}
57+
58+
private void drawInside(GC gc, Node node, int level, Axis xAxis, Axis yAxis) {
59+
60+
String label = labelProvider.apply(node);
61+
if(label == null)
62+
return;
63+
64+
Point angleBounds = node.getAngleBounds();
65+
// check if pie chart (inner bound) is big enough to draw label
66+
gc.setFont(getFont());
67+
Point textSize = gc.textExtent(label);
68+
Point start = getPixelCoordinate(xAxis, yAxis, (level - 1), angleBounds.x);
69+
Point end = getPixelCoordinate(xAxis, yAxis, (level - 1), angleBounds.x + angleBounds.y);
70+
int size = (int)Math.sqrt(Math.pow((end.x - start.x), 2) + Math.pow((end.y - start.y), 2));
71+
if(size < textSize.y - 4)
72+
return;
73+
// calculate text angle
74+
int angle = angleBounds.x + angleBounds.y / 2;
75+
Point innerBound = getPixelCoordinate(xAxis, yAxis, (level - 1), angle);
76+
Point outerBound = getPixelCoordinate(xAxis, yAxis, level, angle);
77+
Transform t = new Transform(gc.getDevice());
78+
Point textPosition;
79+
if(angle >= 90 && angle <= 270) {
80+
textPosition = outerBound;
81+
t.translate(textPosition.x, textPosition.y);
82+
t.rotate((-angle + 180));
83+
} else {
84+
textPosition = innerBound;
85+
t.translate(textPosition.x, textPosition.y);
86+
t.rotate(-angle);
87+
}
88+
gc.setTransform(t);
89+
gc.setForeground(getForeground());
90+
int length = (int)Math.sqrt(Math.pow((outerBound.x - innerBound.x), 2) + Math.pow((outerBound.y - innerBound.y), 2));
91+
gc.setClipping(0, 0 - (textSize.y / 2), length - 3, textSize.y);
92+
gc.drawString(label, 2, 0 - (textSize.y / 2), true);
93+
gc.setClipping((Rectangle)null);
94+
gc.setTransform(null);
95+
}
96+
97+
private void drawOutside(GC gc, Node node, int level, Axis xAxis, Axis yAxis) {
98+
99+
String label = labelProvider.apply(node);
100+
if(label == null)
101+
return;
102+
103+
Point angleBounds = node.getAngleBounds();
104+
int angle = angleBounds.x + angleBounds.y / 2;
105+
Point textSize = gc.textExtent(label);
106+
// some heuristic to check if there is enough space to render a
107+
// label
108+
Point start = getPixelCoordinate(xAxis, yAxis, (level - 1), angleBounds.x);
109+
Point end = getPixelCoordinate(xAxis, yAxis, (level - 1), angleBounds.x + angleBounds.y);
110+
if(Math.abs(start.y - end.y) < 4)
111+
return;
112+
Point point = getPixelCoordinate(xAxis, yAxis, level * 1.03, angle);
113+
int x = angle >= 90 && angle <= 270 ? point.x - textSize.x : point.x;
114+
Font oldFont = gc.getFont();
115+
gc.setForeground(getForeground());
116+
gc.setFont(getFont());
117+
gc.drawString(label, x, point.y - (textSize.y / 2), true);
118+
gc.setFont(oldFont);
119+
}
120+
121+
protected final Point getPixelCoordinate(IAxis xAxis, IAxis yAxis, double pieLevel, int angle) {
122+
123+
double xCoordinate = pieLevel * Math.cos(Math.toRadians(angle));
124+
double yCoordinate = pieLevel * Math.sin(Math.toRadians(angle));
125+
return new Point(xAxis.getPixelCoordinate(xCoordinate), yAxis.getPixelCoordinate(yCoordinate));
126+
}
127+
}

org.eclipse.swtchart/src/org/eclipse/swtchart/internal/series/Doughnut.java

+3-1
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ protected Doughnut(Chart chart, String id) {
3737
* @param xAxis
3838
* @param yAxis
3939
*/
40+
@Override
4041
protected void drawNode(Node node, GC gc, Axis xAxis, Axis yAxis) {
4142

4243
// children drawn first as parent overrides it's section of drawing
@@ -45,7 +46,7 @@ protected void drawNode(Node node, GC gc, Axis xAxis, Axis yAxis) {
4546
drawNode(nodes, gc, xAxis, yAxis);
4647
}
4748
}
48-
if(node.isVisible() == false)
49+
if(!node.isVisible())
4950
return;
5051
int level = node.getLevel() - getRootPointer().getLevel() + 1;
5152
/*
@@ -85,6 +86,7 @@ protected void drawNode(Node node, GC gc, Axis xAxis, Axis yAxis) {
8586
//
8687
if(node != getRootPointer())
8788
gc.drawLine(xZero, yZero, xEndPixelCoordinate, yEndPixelCoordinate);
89+
((CircularSeriesLabel)seriesLabel).draw(gc, node, level, xAxis, yAxis);
8890
}
8991

9092
/**

org.eclipse.swtchart/src/org/eclipse/swtchart/internal/series/Pie.java

+3-1
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ protected Pie(Chart chart, String id) {
3737
* @param xAxis
3838
* @param yAxis
3939
*/
40+
@Override
4041
protected void drawNode(Node node, GC gc, Axis xAxis, Axis yAxis) {
4142

4243
// children drawn first as parent overrides it's section of drawing
@@ -45,7 +46,7 @@ protected void drawNode(Node node, GC gc, Axis xAxis, Axis yAxis) {
4546
drawNode(nodes, gc, xAxis, yAxis);
4647
}
4748
}
48-
if(node.isVisible() == false)
49+
if(!node.isVisible())
4950
return;
5051
int level = node.getLevel() - getRootPointer().getLevel();
5152
/*
@@ -83,6 +84,7 @@ protected void drawNode(Node node, GC gc, Axis xAxis, Axis yAxis) {
8384
int yEndPixelCoordinate = yAxis.getPixelCoordinate(yEndCoordinate);
8485
//
8586
gc.drawLine(xZero, yZero, xEndPixelCoordinate, yEndPixelCoordinate);
87+
((CircularSeriesLabel)seriesLabel).draw(gc, node, level, xAxis, yAxis);
8688
}
8789

8890
/**

0 commit comments

Comments
 (0)