Skip to content

Commit

Permalink
Added support for labels in pie and doughnut charts
Browse files Browse the repository at this point in the history
  • Loading branch information
buchen committed Dec 4, 2022
1 parent 0f9c58b commit c4a2477
Show file tree
Hide file tree
Showing 8 changed files with 172 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swtchart.Chart;
import org.eclipse.swtchart.ICircularSeries;
import org.eclipse.swtchart.ICircularSeriesLabel;
import org.eclipse.swtchart.ISeries.SeriesType;

/**
Expand Down Expand Up @@ -77,6 +78,8 @@ static public Chart createChart(Composite parent) {
ICircularSeries<?> multiLevelDoughnut = (ICircularSeries<?>)chart.getSeriesSet().createSeries(SeriesType.DOUGHNUT, "countries");
// sets the series.
multiLevelDoughnut.setSeries(continentLabels, continentValues);
multiLevelDoughnut.getLabel().setVisible(true);
multiLevelDoughnut.getLabel().setPosition(ICircularSeriesLabel.Position.Inside);
// adding Asian countries. These go in as second level
multiLevelDoughnut.getNodeById("Asia").addChildren(AsianCountriesLabels, AsianCountriesValues);
//
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swtchart.Chart;
import org.eclipse.swtchart.ICircularSeries;
import org.eclipse.swtchart.ICircularSeriesLabel;
import org.eclipse.swtchart.ISeries.SeriesType;

/**
Expand Down Expand Up @@ -73,6 +74,11 @@ static public Chart createChart(Composite parent) {
// change color of India to DARK_RED
Color color = Display.getDefault().getSystemColor(SWT.COLOR_DARK_RED);
circularSeries.setColor("India", color);

circularSeries.getLabel().setVisible(true);
circularSeries.getLabel().setPosition(ICircularSeriesLabel.Position.Inside);
circularSeries.getLabel().setForeground(Display.getDefault().getSystemColor(SWT.COLOR_DARK_RED));
circularSeries.getLabel().setLabelProvider(node -> String.format("%s (%.1f%%)", node.getId(), node.getValue() / node.getParent().getValue() * 100));
//
return chart;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@
*/
public interface ICircularSeries<T> extends ISeries<T> {

@Override
ICircularSeriesLabel getLabel();

/**
* gets the label series
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package org.eclipse.swtchart;

import java.util.function.Function;

import org.eclipse.swtchart.model.Node;

public interface ICircularSeriesLabel extends ISeriesLabel {

public enum Position {
Inside, Outside
}

void setPosition(Position position);

Position getPosition();

void setLabelProvider(Function<Node, String> labelProvider);
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import org.eclipse.swtchart.Chart;
import org.eclipse.swtchart.IAxis;
import org.eclipse.swtchart.ICircularSeries;
import org.eclipse.swtchart.ICircularSeriesLabel;
import org.eclipse.swtchart.internal.axis.Axis;
import org.eclipse.swtchart.internal.compress.Compress;
import org.eclipse.swtchart.internal.compress.CompressCircularSeries;
Expand All @@ -47,6 +48,7 @@ public CircularSeries(Chart chart, String id) {

super(chart, id);
this.chart = chart;
this.seriesLabel = new CircularSeriesLabel();
initialise();
model = new IdNodeDataModel(id);
rootNode = model.getRootNode();
Expand All @@ -58,6 +60,12 @@ public CircularSeries(Chart chart, String id) {
borderStyle = SWT.LINE_SOLID;
}

@Override
public ICircularSeriesLabel getLabel() {

return (ICircularSeriesLabel)super.getLabel();
}

@Override
public Color getBorderColor() {

Expand Down Expand Up @@ -106,6 +114,7 @@ public Compress getCompressor() {
return (Compress)compressor;
}

@Override
public Node getRootNode() {

return rootNode;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
package org.eclipse.swtchart.internal.series;

import java.util.function.Function;

import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.graphics.Transform;
import org.eclipse.swtchart.IAxis;
import org.eclipse.swtchart.ICircularSeriesLabel;
import org.eclipse.swtchart.internal.axis.Axis;
import org.eclipse.swtchart.model.Node;

public class CircularSeriesLabel extends SeriesLabel implements ICircularSeriesLabel {

private Position position = Position.Inside;
private Function<Node, String> labelProvider = Node::getId;

public CircularSeriesLabel() {

}

@Override
public void setPosition(Position position) {

this.position = position;
}

@Override
public Position getPosition() {

return position;
}

@Override
public void setLabelProvider(Function<Node, String> labelProvider) {

this.labelProvider = labelProvider;
}

/* package */ void draw(GC gc, Node node, int level, Axis xAxis, Axis yAxis) {

if(!isVisible())
return;
switch(position) {
case Inside:
drawInside(gc, node, level, xAxis, yAxis);
break;
case Outside:
drawOutside(gc, node, level, xAxis, yAxis);
break;
default:
throw new IllegalArgumentException();
}
}

private void drawInside(GC gc, Node node, int level, Axis xAxis, Axis yAxis) {

String label = labelProvider.apply(node);
if(label == null)
return;

Point angleBounds = node.getAngleBounds();
// check if pie chart (inner bound) is big enough to draw label
gc.setFont(getFont());
Point textSize = gc.textExtent(label);
Point start = getPixelCoordinate(xAxis, yAxis, (level - 1), angleBounds.x);
Point end = getPixelCoordinate(xAxis, yAxis, (level - 1), angleBounds.x + angleBounds.y);
int size = (int)Math.sqrt(Math.pow((end.x - start.x), 2) + Math.pow((end.y - start.y), 2));
if(size < textSize.y - 4)
return;
// calculate text angle
int angle = angleBounds.x + angleBounds.y / 2;
Point innerBound = getPixelCoordinate(xAxis, yAxis, (level - 1), angle);
Point outerBound = getPixelCoordinate(xAxis, yAxis, level, angle);
Transform t = new Transform(gc.getDevice());
Point textPosition;
if(angle >= 90 && angle <= 270) {
textPosition = outerBound;
t.translate(textPosition.x, textPosition.y);
t.rotate((-angle + 180));
} else {
textPosition = innerBound;
t.translate(textPosition.x, textPosition.y);
t.rotate(-angle);
}
gc.setTransform(t);
gc.setForeground(getForeground());
int length = (int)Math.sqrt(Math.pow((outerBound.x - innerBound.x), 2) + Math.pow((outerBound.y - innerBound.y), 2));
gc.setClipping(0, 0 - (textSize.y / 2), length - 3, textSize.y);
gc.drawString(label, 2, 0 - (textSize.y / 2), true);
gc.setClipping((Rectangle)null);
gc.setTransform(null);
}

private void drawOutside(GC gc, Node node, int level, Axis xAxis, Axis yAxis) {

String label = labelProvider.apply(node);
if(label == null)
return;

Point angleBounds = node.getAngleBounds();
int angle = angleBounds.x + angleBounds.y / 2;
Point textSize = gc.textExtent(label);
// some heuristic to check if there is enough space to render a
// label
Point start = getPixelCoordinate(xAxis, yAxis, (level - 1), angleBounds.x);
Point end = getPixelCoordinate(xAxis, yAxis, (level - 1), angleBounds.x + angleBounds.y);
if(Math.abs(start.y - end.y) < 4)
return;
Point point = getPixelCoordinate(xAxis, yAxis, level * 1.03, angle);
int x = angle >= 90 && angle <= 270 ? point.x - textSize.x : point.x;
Font oldFont = gc.getFont();
gc.setForeground(getForeground());
gc.setFont(getFont());
gc.drawString(label, x, point.y - (textSize.y / 2), true);
gc.setFont(oldFont);
}

protected final Point getPixelCoordinate(IAxis xAxis, IAxis yAxis, double pieLevel, int angle) {

double xCoordinate = pieLevel * Math.cos(Math.toRadians(angle));
double yCoordinate = pieLevel * Math.sin(Math.toRadians(angle));
return new Point(xAxis.getPixelCoordinate(xCoordinate), yAxis.getPixelCoordinate(yCoordinate));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ protected Doughnut(Chart chart, String id) {
* @param xAxis
* @param yAxis
*/
@Override
protected void drawNode(Node node, GC gc, Axis xAxis, Axis yAxis) {

// children drawn first as parent overrides it's section of drawing
Expand All @@ -45,7 +46,7 @@ protected void drawNode(Node node, GC gc, Axis xAxis, Axis yAxis) {
drawNode(nodes, gc, xAxis, yAxis);
}
}
if(node.isVisible() == false)
if(!node.isVisible())
return;
int level = node.getLevel() - getRootPointer().getLevel() + 1;
/*
Expand Down Expand Up @@ -85,6 +86,7 @@ protected void drawNode(Node node, GC gc, Axis xAxis, Axis yAxis) {
//
if(node != getRootPointer())
gc.drawLine(xZero, yZero, xEndPixelCoordinate, yEndPixelCoordinate);
((CircularSeriesLabel)seriesLabel).draw(gc, node, level, xAxis, yAxis);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ protected Pie(Chart chart, String id) {
* @param xAxis
* @param yAxis
*/
@Override
protected void drawNode(Node node, GC gc, Axis xAxis, Axis yAxis) {

// children drawn first as parent overrides it's section of drawing
Expand All @@ -45,7 +46,7 @@ protected void drawNode(Node node, GC gc, Axis xAxis, Axis yAxis) {
drawNode(nodes, gc, xAxis, yAxis);
}
}
if(node.isVisible() == false)
if(!node.isVisible())
return;
int level = node.getLevel() - getRootPointer().getLevel();
/*
Expand Down Expand Up @@ -83,6 +84,7 @@ protected void drawNode(Node node, GC gc, Axis xAxis, Axis yAxis) {
int yEndPixelCoordinate = yAxis.getPixelCoordinate(yEndCoordinate);
//
gc.drawLine(xZero, yZero, xEndPixelCoordinate, yEndPixelCoordinate);
((CircularSeriesLabel)seriesLabel).draw(gc, node, level, xAxis, yAxis);
}

/**
Expand Down

0 comments on commit c4a2477

Please sign in to comment.