Drawing string at the center of arc - java

I have pie chart to display the rate of sales through out the year,now i want to draw a string at the center of each arc respectively,to specify the month in my pie chart
this is how my code looks like
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import javax.swing.JPanel;
public class TopSectionPanel extends JPanel {
double[] sales = {4000, 3000, 2000, 6000 , 10000 , 2500,
3400 , 8700 , 6734 , 1200 , 4500 , 6700};
double[] angle = new double[sales.length];
Color[] color = {Color.RED, Color.BLACK, Color.BLUE, Color.DARK_GRAY, Color.GREEN,
Color.LIGHT_GRAY, Color.MAGENTA, Color.ORANGE, Color.PINK, Color.WHITE,
Color.YELLOW, Color.GRAY};
double sum = 0;
/**
* Create the panel.
*/
public TopSectionPanel() {
setBackground(Color.CYAN);
setPreferredSize(new Dimension(400 , 400));
for(int l = 0 ; l < sales.length ; l++)
sum += sales[l];
for(int i = 0 ; i < angle.length ; i++)
angle[i] = sales[i] / sum * 360;
}
#Override
protected void paintComponent(Graphics g) {
// TODO Auto-generated method stub
super.paintComponent(g);
int radius;
// calculations to fit the circle in exact center
if(getHeight() < getWidth())
radius = (int) ((getHeight() / 2.) * 0.8) ;
else
radius = (int) ((getWidth() / 2.) * 0.8) ;
int diameter = radius * 2;
int x = (int)(getWidth() / 2) - radius;
int y = (int)(getHeight() / 2) - radius;
double a = 0;
for( int i = 0 ; i < angle.length ; i++ ) {
g.setColor(color[i]);
g.fillArc(x, y, diameter, diameter, (int)a, (int)angle[i]);
a = a + angle[i];
}
}
}

Not knowing minor details of the requirements such as distance from the arc or font or size I can only produce a raw sketch, using some elementary geometry. Add this after the loop drawing the pie chart.
int mx = x + radius;
int my = y + radius;
double b = 0;
int rad = (int)(radius*1.20);
for( int i = 0 ; i < angle.length ; i++ ) {
b += angle[i]/2;
double brad = b*Math.PI/180.0;
int ix = (int)(rad*Math.cos(brad));
int iy = (int)(rad*Math.sin(brad));
g.drawString( Integer.toString(i+1), mx+ix, my-iy );
b += angle[i]/2;
}
Note that the computed coordinates are the point on the baseline where the text starts. If you really want to center the text around this point you'll have to compute the bounding box for the text and modify the coordinates by these small amounts in x- and y-direction.

Related

How to re-size polygon in jPanel?

I have to make a program that generates stars in random locations of random size. My code already plots the stars in random locations, but I can't manage to randomly change their sizes. I tried assigning a size factor to each point to alter the distance between them but the stars came out all messed up. Is there a scaling method I can use?
Here is what I have so far, it plots the stars in random locations.
final int MID = WIDTH / 2;
final int TOP = 50;
//sky
Color skyColor = new Color(0, 0, 0);
page.fillRect(0,0,getWidth(),getHeight());
//ground
Color groundColor = new Color(95,95,95);
page.setColor(groundColor);
page.fillRect(0,HEIGHT-20,getWidth(),getHeight());
//star
for (int i = 1; i <= starCount; i++)
{
int ranLocX = gen.nextInt(700 - 100) + 100;
int ranLocY = gen.nextInt(300 - 75) + 75;
int ranSize = gen.nextInt(8 - 1) + 1;
int sizeXA = (-10 * ranSize);
int sizeXB = (10 * ranSize);
int sizeXC = (-5 * ranSize);
int sizeXD = (-10 * ranSize);
int sizeXE = (-10 * ranSize);
int sizeXF = (-10 * ranSize);
int sizeYC = (10 * ranSize);
int sizeYD = (-10 * ranSize);
int sizeYE = (10 * ranSize);
page.drawPolygon(new int[] {xa + ranLocX, xb + ranLocX, xc + ranLocX, xd + ranLocX, xe + ranLocX, xf + ranLocX}, new int[] {ya + ranLocY, yb + ranLocY, yc + ranLocY, yd + ranLocY, ye + ranLocY, yf + ranLocY}, 6);
}
Here is a simple method you can use to create a Shape with any given number of points and radius:
public static Shape radiusShape(int points, int... radii)
{
Polygon polygon = new Polygon();
for (int i = 0; i < points; i++)
{
double radians = Math.toRadians(i * 360 / points);
int radius = radii[i % radii.length];
double x = Math.cos(radians) * radius;
double y = Math.sin(radians) * radius;
polygon.addPoint((int)x, (int)y);
}
Rectangle bounds = polygon.getBounds();
polygon.translate(-bounds.x, -bounds.y);
return polygon;
}
To create your 5 point star you would use code like:
Shape star = ShapeUtils.radiusShape(10, 30, 12);
It will create a star with 5 outer points and 5 inner points to give the star shape.
So to randomize the size of the star you would randomize the radius.
Check out Playing With Shapes for more examples of the types of Shapes you can create using this method. The above radiusShape(...) method was taken from the ShapeUtils class found in the above link.
I would then suggest you create a custom class with the properties 1) Shape 2) Point so you can paint the Star at different locations on the panel. Then you create an ArrayList to hold instances of the class. In your painting method you iterate through this ArrayList to paint each Shape. The above link will also provide basic code for this concept.
Here's an example of how to change the size of a Polygon. I drew squares but any Shape will work.
just create a scale instance of an AffineTransform and use that to scale the Shape.
I used ThreadLocalRandom to randomly choose the scale to be applied. I always copy the original polygon(template) and then scale that.
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Polygon;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class Polygons extends JPanel {
static int WIDTH = 500;
static int HEIGHT = 500;
JFrame f = new JFrame();
Polygon b =new Polygon();
ThreadLocalRandom r = ThreadLocalRandom.current();
List<Shape> polys = new ArrayList<>();
public static void main(String[] args) {
SwingUtilities.invokeLater(()-> new Polygons().start());
}
public void start() {
f.add(this);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
Polygon template = new Polygon();
template.addPoint(0,0);
template.addPoint(0,100);
template.addPoint(100,100);
template.addPoint(100,0);
// AffineTransform rotate = AffineTransform.getRotateInstance(Math.toRadians(72.), )
for (int i = 0; i < 20; i++) {
Polygon p = new Polygon(template.xpoints,template.ypoints, template.npoints);
p.translate(r.nextInt(WIDTH), r.nextInt(HEIGHT));
double scale = r.nextInt(10,90)/100.;
AffineTransform scaleIt = AffineTransform.getScaleInstance(scale,scale);
polys.add(scaleIt.createTransformedShape(p));
}
}
public Dimension getPreferredSize() {
return new Dimension(WIDTH,HEIGHT);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
for (Shape shape : polys) {
g2d.draw(shape);
}
}
}
Here is something I wrote a long time ago to create a 5 point star. It draws a single arm and then rotates 72 degrees and draws another, repeating the process.
Because it was written to allow the base to be changed, hence the star size, this might be as better option for scaling the size of your stars rather than using the AffineTransform mentioned above:
for (int i = 0; i < 50; i++) {
// get the base of the next star between 5 and 29 inclusive
int base = r.nextInt(5,30);
Polygon star = createStar(base);
// now randomly position it.
star.translate(r.nextInt(0,400),r.nextInt(0,400));
// and add to the list
polys.add(star);
}
Creating a 5 point star
int startx = 250; // arbitrary starting points
int starty = 250;
public Polygon createStar(int armBase) {
Polygon star = new Polygon();
// The armBase is equal to one side of the inner
// pentagon of the star
// The height of the arm is the distance from the middle of the
// base to the tip of the stars arm. Since the tangent computes
// ratio of the sides of a right triangle, multiplying by half
// the base gives the other side, hence the height.
int armHeight =
(int) (armBase / 2 * Math.tan(Math.toRadians(72)));
// The center offset is the distance from the middle of a given
// base to the center of the inner pentagon.
int centerOffset =
(int) (armBase / 2 * Math.tan(Math.toRadians(54)));
// this works by creating the first arm, rotating 72 degrees
// and then adding the other two coodinates of succeeding arms.
star.addPoint(startx, starty);
star.addPoint(startx + armBase / 2, starty - armHeight);
star.addPoint(startx + armBase, starty);
for (int j = 0; j < 4; j++) {
rotatePolygon(-Math.PI / 5 * 2, startx + armBase / 2,
starty + centerOffset, star);
star.addPoint(startx + armBase / 2, starty - armHeight);
star.addPoint(startx + armBase, starty);
}
star.npoints--;
star.translate(-star.getBounds().x,-star.getBounds().y);
return star;
}
// This is general purpose rotation that rotates about a center
// point. This can be derived using the double angle identities of
// for sin and cosine.
private void rotatePolygon(double ang, double sx, double sy,
Polygon poly) {
for (int j = 0; j < poly.npoints; j++) {
double x = poly.xpoints[j];
double y = poly.ypoints[j];
double xx = sx + (x - sx) * Math.cos(ang)
- (y - sy) * Math.sin(ang);
double yy = sy + (x - sx) * Math.sin(ang)
+ (y - sy) * Math.cos(ang);
poly.xpoints[j] = (int) xx;
poly.ypoints[j] = (int) yy;
}
}
Here's a GUI to draw one five-pointed star.
Here's the complete runnable code. I used polar coordinates to calculate the 10 points I needed to draw a star. I guessed the fraction to get the intermediate points correct.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.Polygon;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class StarryNight2GUI implements Runnable {
public static void main(String[] args) {
SwingUtilities.invokeLater(new StarryNight2GUI());
}
#Override
public void run() {
JFrame frame = new JFrame("Starry Night");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new DrawingPanel(), BorderLayout.CENTER);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public class DrawingPanel extends JPanel {
private static final long serialVersionUID = 1L;
public DrawingPanel() {
this.setBackground(Color.BLACK);
this.setPreferredSize(new Dimension(640, 480));
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Color groundColor = new Color(95, 95, 95);
g.setColor(groundColor);
g.fillRect(0, getHeight() - 30, getWidth(), 30);
Polygon polygon = createStar(new Point(320, 240), 80);
g.setColor(Color.YELLOW);
g.fillPolygon(polygon);
}
private Polygon createStar(Point centerPoint, int radius) {
Polygon polygon = new Polygon();
// 72, 144, 216, 288, 360
// 36, 108, 180, 252, 324
// 54, 126, 198, 270, 342
// 18, 54, 90, 126, 162, 198, 234, 270, 306, 342
for (int angle = 18; angle < 360; angle += 72) {
double r = 0.42 * radius;
Point point = toCartesian(centerPoint, angle, r);
polygon.addPoint(point.x, point.y);
point = toCartesian(centerPoint, angle + 36, radius);
polygon.addPoint(point.x, point.y);
}
return polygon;
}
private Point toCartesian(Point centerPoint, int angle, double radius) {
double theta = Math.toRadians(angle);
int x = centerPoint.x + (int) Math.round(Math.cos(theta) * radius);
int y = centerPoint.y + (int) Math.round(Math.sin(theta) * radius);
return new Point(x, y);
}
}
}

How to change the colour of my Mandelbrot Set display?

I found some code to display a Mandelbrot set in java, this one has a turqoise colour theme and I was wondering how to change this to another colour, for example a red colour theme. Here is the code:
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import javax.swing.JFrame;
public class Mandelbrot extends JFrame {
private final int MAX_ITER = 570;
private final double ZOOM = 150;
private BufferedImage I;
private double zx, zy, cX, cY, tmp;
public Mandelbrot() {
super("Mandelbrot Set");
setBounds(100, 100, 800, 600);
setResizable(false);
setDefaultCloseOperation(EXIT_ON_CLOSE);
I = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_RGB);
for (int y = 0; y < getHeight(); y++) {
for (int x = 0; x < getWidth(); x++) {
zx = zy = 0;
cX = (x - 400) / ZOOM;
cY = (y - 300) / ZOOM;
int iter = MAX_ITER;
while (zx * zx + zy * zy < 4 && iter > 0) {
tmp = zx * zx - zy * zy + cX;
zy = 2.0 * zx * zy + cY;
zx = tmp;
iter--;
}
I.setRGB(x, y, iter | (iter << 8));
}
}
}
#Override
public void paint(Graphics g) {
g.drawImage(I, 0, 0, this);
}
public static void main(String[] args) {
new Mandelbrot().setVisible(true);
}
}
The hex code for red is #FF0000 and the rbg decimal code is (255,0,0) if that helps.
Thanks for your time.
The essense of your code (in coloring) is this
I.setRGB(x, y, iter | (iter << 8));
whatever iter is is plugged into the lower bits and also shifted by 8 bits which corersponds to the Green value in the middle.
So I guess you could try
I.setRGB(x, y, iter << 12);
This will plug the iter value in the upper bits (Red Green).

Simplex Noise Adjustments only working in one direction

I am making a heightmap using simplex noise. I am running into problems adjusting it to give a slight tendency to form an island. I am just working on getting the values to be correct before actually rendering the biomes and other features. I have run into the problem that my code, which should work to create this tendency to make an island in the middle only seems to work in one direction.
If there a particular reason for this? My class that deals with the smoothing out of the terrain does the same thing in the x and y directions, but only one works.
public class MapGenerator{
public double[][] toRender;
int maxHeight = 300;
public MapGenerator() {
int xResolution = 200;
int yResolution = 200;
double[][] result = new double[xResolution][yResolution];
for (int x = 0; x < xResolution; x++){
for (int y = 0; y < yResolution; y++){
result[x][y] = transformPoint(x, y, xResolution, yResolution, SimplexNoise.noise(x, y));
}
}
toRender = result;
}
private double transformPoint(int x, int y, int xSize, int ySize, double point){
System.out.println();
System.out.println(point);
point += 20 * Math.sin(x * Math.PI / xSize);
point += 20 * Math.sin(y * Math.PI / ySize);
System.out.println(point);
return point;
}
}
Images of the white noise:
With X and Y:
With only X (Y commented out):
[With only Y (X commented out):](Similar to X and Y, can't post link because of reputation.)
[Without X and Y:](Similar to only X, can't post link because of reputation.)
I'm not 100% where your error is. It could be in your noise routine. To debug this, I removed the call to noise and substituted a constant value of 0.5. Then I got the rest of it working so I saw a white halo in the center of the image. Then I added the noise call back. (Note I'm using my own SimplexNoise here.)
So the problem is either in your bounds (Math.min( 1.0, point )) or in your graphics display (which you don't show) or in your SimplexNoise (which you also don't show).
import SimpleUtils.noise.SimplexNoise;
import java.awt.Image;
import java.awt.image.BufferedImage;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;
/**
*
* #author Brenden Towey
*/
public class MapGenerator
{
public static void main( String[] args )
{
SwingUtilities.invokeLater( new Runnable()
{
public void run()
{
JFrame frame = new JFrame();
frame.add( new JLabel( new ImageIcon( new MapGenerator().toImage() )));
frame.pack();
frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
frame.setLocationRelativeTo( null );
frame.setVisible( true );
}
} );
}
public double[][] toRender;
int maxHeight = 300;
int xResolution = 200;
int yResolution = 200;
public MapGenerator()
{
double[][] result = new double[ xResolution ][ yResolution ];
SimplexNoise noise = new SimplexNoise();
for( int x = 0; x < xResolution; x++ )
for( int y = 0; y < yResolution; y++ )
result[x][y] = transformPoint( x, y, noise.noise(x, y) );
toRender = result;
}
private double transformPoint( int x, int y, double point )
{
point += 2 * Math.sin( x * Math.PI / xResolution )/2.0;
point += 2 * Math.sin( y * Math.PI / yResolution )/2.0;
return Math.min( 1.0, point);
}
public Image toImage()
{
BufferedImage image = new BufferedImage( xResolution,
yResolution, BufferedImage.TYPE_INT_RGB );
for( int x = 0; x < xResolution; x++ )
for( int y = 0; y < yResolution; y++ )
image.setRGB( x, y, greyScale( toRender[x][y] ) );
return image;
}
private int greyScale( double toRender )
{
int scale = (int) ( 255 * toRender );
return scale + (scale << 8) + (scale << 16);
}
}

How to make star shape in Java?

I'm trying to make some shapes with Java. I created two rectangles with two different colors but I want to create a star shape and I can't find useful source to help me doing this.
Here is my code:
import java.awt.*;
import javax.swing.*;
public class shapes extends JPanel{
#Override
public void paintComponent(Graphics GPHCS){
super.paintComponent(GPHCS);
GPHCS.setColor(Color.BLUE);
GPHCS.fillRect(25,25,100,30);
GPHCS.setColor(Color.GRAY);
GPHCS.fillRect(25,65,100,30);
GPHCS.setColor(new Color(190,81,215));
GPHCS.drawString("This is my text", 25, 120);
}
}
You could try using a polygon and some basic math:
int midX = 500;
int midY = 340;
int radius[] = {118,40,90,40};
int nPoints = 16;
int[] X = new int[nPoints];
int[] Y = new int[nPoints];
for (double current=0.0; current<nPoints; current++)
{
int i = (int) current;
double x = Math.cos(current*((2*Math.PI)/max))*radius[i % 4];
double y = Math.sin(current*((2*Math.PI)/max))*radius[i % 4];
X[i] = (int) x+midX;
Y[i] = (int) y+midY;
}
g.setColor(Color.WHITE);
g.fillPolygon(X, Y, nPoints);
You can also use existing classes e.g. http://java-sl.com/shapes.html for regular polygons and stars.
The Polygon class can be considered as a legacy class that has been there since Java 1.0, but should hardly be used any more in new code. The odd way of specifying the x/y coordinates in separate arrays, and, more importantly, the fact that it only supports int[] arrays limits its application areas. Although it implements the Shape interface, there are more modern implementations of this interface that can be used to represent polygons. In most cases, describing the polygon as a Path2D is easier and more flexible. One can create a Path2D p = new Path2D.Double(); and then do a sequence of moveTo and lineTo calls to geneate the desired shape.
The following program shows how the Path2D class may be used to generate star shapes. The most important method is the createStar method. It is very generic. It receives
the center coordinates for the star
the inner and outer radius of the star
the number of rays that the star should have
the angle where the first ray should be (i.e. the rotation angle of the star)
If desired, a simpler method may be wrapped around this one - as with the createDefaultStar example in the code below.
The program shows different stars, painted as lines and filled with different colors and radial gradient paints, as examples:
The complete program as a MCVE:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RadialGradientPaint;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.geom.Path2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class DrawStarShape
{
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{
#Override
public void run()
{
createAndShowGUI();
}
});
}
private static void createAndShowGUI()
{
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.getContentPane().add(new DrawStarShapePanel());
f.setSize(600, 600);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
}
class DrawStarShapePanel extends JPanel
{
#Override
protected void paintComponent(Graphics gr)
{
super.paintComponent(gr);
Graphics2D g = (Graphics2D) gr;
g.setColor(Color.WHITE);
g.fillRect(0, 0, getWidth(), getHeight());
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g.setColor(Color.BLACK);
g.draw(createDefaultStar(50, 200, 200));
g.setPaint(Color.RED);
g.fill(createStar(400, 400, 40, 60, 10, 0));
g.setPaint(new RadialGradientPaint(
new Point(400, 200), 60, new float[] { 0, 1 },
new Color[] { Color.RED, Color.YELLOW }));
g.fill(createStar(400, 200, 20, 60, 8, 0));
g.setPaint(new RadialGradientPaint(
new Point(200, 400), 50, new float[] { 0, 0.3f, 1 },
new Color[] { Color.RED, Color.YELLOW, Color.ORANGE }));
g.fill(createStar(200, 400, 40, 50, 20, 0));
}
private static Shape createDefaultStar(double radius, double centerX,
double centerY)
{
return createStar(centerX, centerY, radius, radius * 2.63, 5,
Math.toRadians(-18));
}
private static Shape createStar(double centerX, double centerY,
double innerRadius, double outerRadius, int numRays,
double startAngleRad)
{
Path2D path = new Path2D.Double();
double deltaAngleRad = Math.PI / numRays;
for (int i = 0; i < numRays * 2; i++)
{
double angleRad = startAngleRad + i * deltaAngleRad;
double ca = Math.cos(angleRad);
double sa = Math.sin(angleRad);
double relX = ca;
double relY = sa;
if ((i & 1) == 0)
{
relX *= outerRadius;
relY *= outerRadius;
}
else
{
relX *= innerRadius;
relY *= innerRadius;
}
if (i == 0)
{
path.moveTo(centerX + relX, centerY + relY);
}
else
{
path.lineTo(centerX + relX, centerY + relY);
}
}
path.closePath();
return path;
}
}
I have 2 method.
1)
public static Bitmap drawStar(int W, int H, int color, boolean andRing)
{
Path path = new Path();
Bitmap output = Bitmap.createBitmap(W, H, Config.ARGB_8888);
Canvas canvas = new Canvas(output);
final Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setColor(color);
float midW ,min ,fat ,half ,radius;
if(andRing)
{
midW = W / 2;
min = Math.min(W, H);
half = min / 2;
midW = midW - half;
fat = min / 17;
radius = half - fat;
paint.setStrokeWidth(fat);
paint.setStyle(Paint.Style.STROKE);
canvas.drawCircle(midW + half, half, radius, paint);
path.reset();
paint.setStyle(Paint.Style.FILL);
path.moveTo( half * 0.5f, half * 0.84f);
path.lineTo( half * 1.5f, half * 0.84f);
path.lineTo( half * 0.68f, half * 1.45f);
path.lineTo( half * 1.0f, half * 0.5f);
path.lineTo( half * 1.32f, half * 1.45f);
path.lineTo( half * 0.5f, half * 0.84f);
}
else
{
min = Math.min(W, H);
half = min/2;
path.reset();
paint.setStyle(Paint.Style.FILL);
path.moveTo( half * 0.1f , half * 0.65f);
path.lineTo( half * 1.9f , half * 0.65f);
path.lineTo( half * 0.40f , half * 1.65f);
path.lineTo( half , 0 );
path.lineTo( half * 1.60f, half * 1.65f);
path.lineTo( half * 0.1f, half * 0.65f);
}
canvas.drawPath(path, paint);
return output;
}
2)
public static Bitmap drawStar(int W,int H,int spikes,int innerRadius,int outerRadius, int backColor,boolean border, int borderColor)
{
if(W < 10)
W = 10;
if(H < 10)
H = 10;
if(spikes < 5)
spikes = 5;
int smallL = W;
if(H < W)
smallL = H;
if(outerRadius > smallL/2)
outerRadius = smallL/2;
if(innerRadius < 5)
innerRadius = 5;
if(border)
{
outerRadius -=2;
innerRadius -=2;
}
Path path = new Path();
Bitmap output = Bitmap.createBitmap(W, H, Config.ARGB_8888);
Canvas canvas = new Canvas(output);
final Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setColor(backColor);
int cx = W/2;
int cy = H/2;
double rot = Math.PI / 2 * 3;
float x,y;
double step = Math.PI / spikes;
path.moveTo(cx, cy - outerRadius);
for (int i = 0; i < spikes; i++)
{
x = (float) (cx + Math.cos(rot) * outerRadius);
y = (float) (cy + Math.sin(rot) * outerRadius);
path.lineTo(x, y);
rot += step;
x = (float) (cx + Math.cos(rot) * innerRadius);
y = (float) (cy + Math.sin(rot) * innerRadius);
path.lineTo(x, y);
rot += step;
}
path.lineTo(cx, cy - outerRadius);
path.close();
canvas.drawPath(path, paint);
if(border)
{
paint.setStyle(Style.STROKE);
paint.setStrokeWidth(2);
paint.setColor(borderColor);
canvas.drawPath(path, paint);
}
return output;
}

unable to complete the circle of a pie chart

I am working on a pie chart with 4 different elements. I can get the elements to appear in the JFrame but I cannot complete the pie chart circle. These are the elements:
public static class PieChart extends JComponent {
IAPieChart[] pieValue = {new IAPieChart(5, Color.green),
new IAPieChart(4, Color.orange),
new IAPieChart(3, Color.blue),
new IAPieChart(2, Color.red)
};
Now that the elements are instantiated, and loaded into the array, I use this method to paint them:
public void paintComponent(Graphics g) {
drawPie((Graphics2D) g, getBounds(), pieValue);
}
This method puts the elements together on the JFrame but only gives me about 120 degrees of the circle. :
void drawPie(Graphics2D g, Rectangle area, IAPieChart[] pieValue){
double sum = 0.0;
for (int i = 0; i < pieValue.length; i++) {
sum += pieValue[i].arcValue;
}
double endPoint = 0.0D;
int arcStart = 0;
for (int i = 0; i < pieValue.length; i++){
endPoint = (int) (endPoint * 360 / sum);
int radius = (int) (pieValue[i].arcValue * 360/ sum);
g.setColor(pieValue[i].color);
g.fillArc(area.x, area.y, area.width, area.height, arcStart , radius);
radius += pieValue[i].arcValue;
}
}
}
I'm at a loss. I have about 10 weeks expereince with Java so this is mostly trial and error. I am looking for a way to complete the circle. I reduced the values to the lowest point that would make all of the colors show. Anything larger will eliminate one color or the other.
I hope you can help. Here is the full program IAPieChart:
package iapiechart;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import javax.swing.JComponent;
import javax.swing.JFrame;
class IAPieChart{
double arcValue; // passes a value for the calculation of the arc.
Color color; // holds value for color (expressed as an integer
public IAPieChart(double value, Color color){
this.arcValue = value;
this.color = color;
}
public static class PieChart extends JComponent {
IAPieChart[] pieValue = {new IAPieChart(5, Color.green),
new IAPieChart(4, Color.orange),
new IAPieChart(3, Color.blue),
new IAPieChart(2, Color.red)
};
public void paintComponent(Graphics g) {
drawPie((Graphics2D) g, getBounds(), pieValue);
}
void drawPie(Graphics2D g, Rectangle area, IAPieChart[] pieValue){
double sum = 0.0;
for (int i = 0; i < pieValue.length; i++) {
sum += pieValue[i].arcValue;
}
double endPoint = 0.0D;
int arcStart = 0;
for (int i = 0; i < pieValue.length; i++){
endPoint = (int) (endPoint * 360 / sum);
int radius = (int) (pieValue[i].arcValue * 360/ sum);
g.setColor(pieValue[i].color);
g.fillArc(area.x, area.y, area.width, area.height, arcStart , radius);
radius += pieValue[i].arcValue;
}
}
}
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.getContentPane().add(new PieChart());
frame.setSize(300, 300);
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(frame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
If you just want to show pie chart where arc value is a weights for segments, made a change to your draw cycle.
int arcStart = 0;
for (int i = 0; i < pieValue.length; i++){
int radius = (int) (pieValue[i].arcValue * 360.0 / sum);
g.setColor(pieValue[i].color);
g.fillArc(area.x, area.y, area.width, area.height, arcStart, radius);
arcStart += radius;
}
The result is something like this:
As you see there is a small empty area, which, I suppose, caused by accumulated rounding error. Can be solved by drawing the last segment with arcValue that complement sum of all previos to 360.
There is a little mixup with arcStart calculation in the original method. It should start from the end of the previous arc. Consider this slightly modified method. The comments describe the updated lines.
void drawPie(Graphics2D g, Rectangle area, IAPieChart[] pieValue){
double sum = 0.0;
for (int i = 0; i < pieValue.length; i++) {
sum += pieValue[i].arcValue;
}
double endPoint = 0.0D;
int arcStart = 0;
for (int i = 0; i < pieValue.length; i++){
arcStart = (int) (endPoint * 360 / sum); //old line was: endPoint = (int) (endPoint * 360 / sum);
int radius = (int) (pieValue[i].arcValue * 360/ sum);
g.setColor(pieValue[i].color);
g.fillArc(area.x, area.y, area.width, area.height, arcStart , radius);
endPoint += pieValue[i].arcValue; //old line was: radius += pieValue[i].arcValue;
}
}
It replaces line:
endPoint = (int) (endPoint * 360 / sum);
with:
arcStart = (int) (endPoint * 360 / sum);
And replaces line:
radius += pieValue[i].arcValue;
with:
endPoint += pieValue[i].arcValue;
Here is the result:
Don't forget to call super.paintComponent(g); in paintComponent(). Also, you may want to add anti aliasing rendering hints to smooth the image a little bit, ie:
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
drawPie((Graphics2D) g, getBounds(), pieValue);
}
You have
double endPoint = 0.0D;
int arcStart = 0;
for (int i = 0; i < pieValue.length; i++){
endPoint = (int) (endPoint * 360 / sum);
...
However this will always give endPoint = 0 as your multiplying by zero everytime. Try making it
endPoint += (int) (endPoint * 360 / sum);
I learned that I am accumulating the wrong values from both replies from phcoding and mishadoff. With phcoding's recommendation however, I came up with additonal instances of the pieValue array. Both responses were enriching and my experience increased through your responses.
mishadoff: May I have your definition of 'weights' as you mentioned in your response? When I think of weights I imagine an anchoring point where the arc starts then the value given is how much it will increase in it's travel along the radius of the circle. Is that close?
phcoding & mishadoff : I intend to put this into an Applet do you forsee any issues with doing this, such as transfering this code into the applet? I ask because any recommendation you give me will take literally hours off of my programming time, and I am going to need them.
Thank you both very much!
Ed.

Categories