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);
}
}
}
Related
How i want it to look like:
The circles move along with the arrow around the center circle.
How it is looking at the moment:
I want to draw a 2 lines between two circles. however these circles move all around the screen and i dont know a methodical way to draw lines between them. For example, I always have the top left corner of the two circles i want to draw a line between but thats it. I need help to draw a line in java that will adjust based on its position so that the lines move around the edge as the circles move
for (int z = 0; z < lines.size(); z++) {
if (lines.get(z).getfState().equals(states.get(a).getText()) && !lines.get(z).getfState().equals(lines.get(z).getnState())) {
transition.get(z).setIcon(null);
for (int x = 0; x < states.size(); x++) {
if (states.get(x).getText().equals(lines.get(z).getnState()) && states.get(a).getText().equals(lines.get(z).getfState())) {
int xbegin = (int) states.get(a).getBounds().getX();
int ybegin = (int) states.get(a).getBounds().getY();
int xend = (int) states.get(x).getBounds().getX();
int yend = (int) states.get(x).getBounds().getY();
if (xbegin > xend) {
Path2D.Double rect = new Path2D.Double(drawArrowLine(xbegin, ybegin, xend, yend, 10, 7));
OutlineIcon transit = new OutlineIcon(drawArrowLine(xbegin, ybegin, xend + 30, yend, 10, 7), Color.BLACK);
transition.get(z).setIcon(transit);
transition.get(z).setBounds(rect.getBounds().x, rect.getBounds().y, rect.getBounds().width + 20, rect.getBounds().height + 20);
jPanel2.revalidate();
jPanel2.repaint();
} else {
if (xend - xbegin < 75) {
xbegin = xbegin - 20;
xend = xend - 20;
}
xbegin = xbegin + 5;
ybegin = ybegin + 25;
xend = xend + 5;
yend = yend + 25;
Path2D.Double rect = new Path2D.Double(drawArrowLine(xbegin, ybegin, xend - 10, yend, 10, 7));
OutlineIcon transit = new OutlineIcon(drawArrowLine(xbegin, ybegin, xend - 10, yend, 10, 7), Color.BLACK);
transition.get(z).setIcon(transit);
transition.get(z).setBounds(rect.getBounds().x, rect.getBounds().y, rect.getBounds().width + 20, rect.getBounds().height + 20);
jPanel2.revalidate();
jPanel2.repaint();
}
}
}
} else if (lines.get(z).getnState().equals(states.get(a).getText()) && !lines.get(z).getfState().equals(lines.get(z).getnState())) {
transition.get(z).setIcon(null);
for (int x = 0; x < states.size(); x++) {
if (states.get(x).getText().equals(lines.get(z).getfState()) && states.get(a).getText().equals(lines.get(z).getnState())) {
int xend = (int) states.get(a).getBounds().getX();
int yend = (int) states.get(a).getBounds().getY();
int xbegin = (int) states.get(x).getBounds().getX();
int ybegin = (int) states.get(x).getBounds().getY();
if (xbegin > xend) {
Path2D.Double rect2 = new Path2D.Double(drawArrowLine(xbegin, ybegin, xend, yend, 10, 7));
OutlineIcon transit = new OutlineIcon(drawArrowLine(xbegin, ybegin, xend + 30, yend, 10, 7), Color.BLACK);
transition.get(z).setIcon(transit);
transition.get(z).setBounds(rect2.getBounds().x, rect2.getBounds().y, rect2.getBounds().width + 20, rect2.getBounds().height + 20);
jPanel2.revalidate();
jPanel2.repaint();
} else {
if (xend - xbegin < 75) {
xbegin = xbegin + 20;
xend = xend + 20;
}
xbegin = xbegin + 5;
ybegin = ybegin + 25;
xend = xend + 5;
yend = yend + 25;
Path2D.Double rect2 = new Path2D.Double(drawArrowLine(xbegin, ybegin, xend - 10, yend, 10, 7));
OutlineIcon transit = new OutlineIcon(drawArrowLine(xbegin, ybegin, xend - 10, yend, 10, 7), Color.BLACK);
transition.get(z).setIcon(transit);
transition.get(z).setBounds(rect2.getBounds().x, rect2.getBounds().y, rect2.getBounds().width + 20, rect2.getBounds().height + 20);
jPanel2.revalidate();
jPanel2.repaint();
}
}
}
public static Path2D.Double createArrowForLine(
int fromPointx,
int fromPointy,
double rotationDeg,
double length,
double wingsAngleDeg) {
double ax = fromPointx;
double ay = fromPointy;
double radB = Math.toRadians(-rotationDeg + wingsAngleDeg);
double radC = Math.toRadians(-rotationDeg - wingsAngleDeg);
Path2D resultPath = new Path2D.Double();
resultPath.moveTo(length * Math.cos(radB) + ax, length * Math.sin(radB) + ay);
resultPath.lineTo(ax, ay);
resultPath.lineTo(length * Math.cos(radC) + ax, length * Math.sin(radC) + ay);
return (Path2D.Double) resultPath;
}
Although there have been some hiccups in the question, and the code provided so far looks questionable, the core of the question as it stands now is quite interesting...
There are different options for solving this. From the images that you provided so far, it looks like the circles always have the same size, which makes things far simpler. For circles with different sizes, you'd really have to compute the tangents of the circles, in the desired direction, mutually considering the radius of the other circle. Of course, this is possible, but a bit less trivial.
For the case that you have equally-sized circles, you can
Compute the difference of the centers of two circles
Divide this by the distance, to obtain the (normalized) direction
Rotate this direction by 90°
Scale the rotated direction vector by the radius
Add the scaled and rotated vector to the circle center
This will yield one endpoint of such a line. The rotation about 90° can be done once in clockwise and once in counterclockwise direction, to obtain the "upper" and "lower" endpoint for the line, respectively.
Image was updated with the EDIT, see below
The actual computation is done in the computeLine method of the MCVE below. Note that this example uses the "simple" approach, although it uses circles of slightly different sizes. The effect is that, when the difference between the sizes of two circles is too large (compared to the distance between the circles, basically), then the lines may slightly intersect the circles. But the solution should be a reasonable trade-off between simplicity and general applicability. Particularly, for equally-sized circles, there will be no intersections at all.
Code was updated with the EDIT, see below
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.awt.geom.Path2D;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class LinesAtCirclesTest
{
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);
JPanel linesAtCirclesTestPanel = new LinesAtCirclesTestPanel();
f.getContentPane().add(linesAtCirclesTestPanel);
f.setSize(400,400);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
}
class LinesAtCirclesTestPanel extends JPanel
implements MouseListener, MouseMotionListener
{
private Point2D draggedCenter;
private List<Point2D> centers = new ArrayList<Point2D>();
private List<Double> radii = new ArrayList<Double>();
public LinesAtCirclesTestPanel()
{
addMouseListener(this);
addMouseMotionListener(this);
addCircle(100, 100, 30);
addCircle(200, 300, 50);
addCircle(300, 200, 40);
}
private void addCircle(double x, double y, double radius)
{
centers.add(new Point2D.Double(x,y));
radii.add(radius);
}
#Override
protected void paintComponent(Graphics gr)
{
super.paintComponent(gr);
Graphics2D g = (Graphics2D)gr;
g.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
for (int i=0; i<centers.size(); i++)
{
Point2D center0 = centers.get(i);
double radius0 = radii.get(i);
Shape ellipse = new Ellipse2D.Double(
center0.getX() - radius0, center0.getY() - radius0,
radius0 + radius0, radius0 + radius0);
g.setColor(Color.LIGHT_GRAY);
g.fill(ellipse);
g.setColor(Color.BLACK);
g.draw(ellipse);
}
g.setColor(Color.RED);
for (int i=0; i<centers.size() - 1; i++)
{
Point2D center0 = centers.get(i);
double radius0 = radii.get(i);
Point2D center1 = centers.get(i+1);
double radius1 = radii.get(i+1);
g.draw(createArrow(computeLine(center0, radius0, center1, radius1, true)));
g.draw(createArrow(computeLine(center0, radius0, center1, radius1, false)));
}
}
private static Shape createArrow(Line2D line)
{
double dx = line.getX2() - line.getX1();
double dy = line.getY2() - line.getY1();
double angleToX = Math.atan2(dy, dx);
final double angleRad = Math.toRadians(30);
final double headLength = 20.0f;
double dxL = Math.cos(Math.PI + angleToX + angleRad) * headLength;
double dyL = Math.sin(Math.PI + angleToX + angleRad) * headLength;
double dxR = Math.cos(Math.PI + angleToX - angleRad) * headLength;
double dyR = Math.sin(Math.PI + angleToX - angleRad) * headLength;
Path2D arrow = new Path2D.Double();
arrow.moveTo(line.getX1(), line.getY1());
arrow.lineTo(line.getX2(), line.getY2());
arrow.lineTo(line.getX2() + dxL, line.getY2() + dyL);
arrow.moveTo(line.getX2(), line.getY2());
arrow.lineTo(line.getX2() + dxR, line.getY2() + dyR);
return arrow;
}
private static Line2D computeLine(
Point2D center0, double radius0,
Point2D center1, double radius1,
boolean upper)
{
double dx = center1.getX() - center0.getX();
double dy = center1.getY() - center0.getY();
double invLength = 1.0 / Math.hypot(dx, dy);
double dirX = dx * invLength;
double dirY = dy * invLength;
double rotDirX = dirY;
double rotDirY = -dirX;
if (upper)
{
rotDirX = -dirY;
rotDirY = dirX;
}
double x0 = center0.getX() + rotDirX * radius0;
double y0 = center0.getY() + rotDirY * radius0;
double x1 = center1.getX() + rotDirX * radius1;
double y1 = center1.getY() + rotDirY * radius1;
if (upper)
{
return new Line2D.Double(x1, y1, x0, y0);
}
return new Line2D.Double(x0, y0, x1, y1);
}
#Override
public void mousePressed(MouseEvent e)
{
draggedCenter = null;
for (int i=0; i<centers.size(); i++)
{
Point2D center = centers.get(i);
double radius = radii.get(i);
if (e.getPoint().distance(center) < radius)
{
draggedCenter = center;
}
}
}
#Override
public void mouseReleased(MouseEvent e)
{
draggedCenter = null;
}
#Override
public void mouseDragged(MouseEvent e)
{
if (draggedCenter == null)
{
return;
}
draggedCenter.setLocation(e.getPoint());
repaint();
}
#Override
public void mouseMoved(MouseEvent e)
{
// Not used
}
#Override
public void mouseClicked(MouseEvent e)
{
// Not used
}
#Override
public void mouseEntered(MouseEvent e)
{
// Not used
}
#Override
public void mouseExited(MouseEvent e)
{
// Not used
}
}
EDIT in response to the comment:
The original code computed Line2D objects. Creating an arrow from a line is, in the simplest case, basically done with a bit of trigonometry, and many resources exist for this on the web.
In response to the comment, I extended the example to show simple arrows, as depicted in the above image.
However, when taking a closer look at this, one may notice several degrees of freedom for such an arrow:
Should the head length be absolute or relative to the arrow?
Should the head width be absolute or relative to the arrow?
(Or: What should be the angle of the arrow head?)
Should the arrow head be filled, or consist of lines?
Should the "trunk" of the arrow be a single line, or an outline shape?
What should be the width of the trunk?
...
In order to cover some of these degrees of freedom, I created an ArrowCreator class a while ago, and there's also a sample showing how it may be used.
Really having trouble trying to draw a four leaf rose: This is the exercise:
Draw a picture of the “fourleaved rose” whose equation in polar coordinates is
r =cos(2θ) . Let θ go from 0 to 2*pi in 100 steps. Each time, compute r and then compute the (x, y) coordinates from the polar coordinates by using the formula
x = r ⋅ cos( θ) , y = r ⋅ sin(θ )
My Code:
public Rose(double awidth, double aheight)
{
width = awidth;
height = aheight;
theta = 0;
}
public void drawRose(Graphics2D g2)
{
Ellipse2D.Double test ;
double r = 0;
for(int i = 0; i <= 100; i++)
{
r = Math.cos(Math.toRadians(2*theta) );
x = r *( Math.cos( Math.toRadians(theta) ) * width ) + 300;
y = r * ( Math.sin( Math.toRadians(theta) ) * height ) + 300 ;
test = new Ellipse2D.Double(x, y, width, height);
theta += 3.6;
g2.draw(test);
}
}
}
Any help will be greatly appreciately.
Your biggest mistake is here:
test = new Ellipse2D.Double(x, y, width, height);
You're creating 100 Ellipses with the points that are on the rose, but that with heights and widths of the desired rose. You really don't want 100 ellipses, but rather you want to connect lines between the x and y points you've created, that is connect the current x, y with the previous ones created (as long as there is a previous x and y).
One way is via these suggestions, but there are other ways to do this:
Use a Path2D object, the concrete implementation would be a Path2D.Double, to hold your data points. Create this before creating data points.
Use a for loop that goes from 0 to 100, and do this in the class's constructor
set your double theta in the loop
set your double r variable in the loop
Calculate your x and y double points
Scale your x and y points by multiplying them with a scale factor so that the drawing has some size. I used 150.0
Translate your x and y values by adding a translation constant. I used 200 and it worked nicely in a 400 x 400 JPanel. Else the center of the rose will be at 0, 0 and only a fourth of it will be visible.
In the first iteration of the for loop call the Path2D's moveTo(...) method to add a starting point
In all other iterations call the lineTo(...) method. This will draw a line between neighboring points.
After the for loop, close the path by calling closePath() on it.
Draw the path in your JPanel's paintComponent method by casting your Graphics parameter into a Graphics2D object (actually you don't need this since your draw method gets a Grahpics2D object), and calling draw(path) with the Graphics2D object, passing in your Path2D object.
For example, this is created:
with this code:
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Stroke;
import java.awt.geom.Path2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
#SuppressWarnings("serial")
public class RosePanel extends JPanel {
private static final int PREF_W = 400;
private static final int PREF_H = PREF_W;
private static final int MAX = 100;
private static final double SCALE = 150.0;
private static final double DELTA_X = 200;
private static final double DELTA_Y = DELTA_X;
private static final Color ROSE_COLOR = Color.red;
private static final Stroke ROSE_STROKE = new BasicStroke(8f);
private Path2D path = new Path2D.Double();
public RosePanel() {
for (int i = 0; i < MAX; i++) {
double theta = i * 2 * Math.PI / MAX;
double r = Math.cos(2 * theta);
double dX = SCALE * r * Math.cos(theta) + DELTA_X;
double dY = SCALE * r * Math.sin(theta) + DELTA_Y;
if (i == 0) {
path.moveTo(dX, dY);
} else {
path.lineTo(dX, dY);
}
}
path.closePath();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2.setColor(ROSE_COLOR);
g2.setStroke(ROSE_STROKE);
g2.draw(path);
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
private static void createAndShowGui() {
RosePanel mainPanel = new RosePanel();
JFrame frame = new JFrame("RosePanel");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
Note that the key difference between my code and yours, other than the translations and the scaling is that I'm connecting the line between points created.
I want there to be 12 ellipses so they represent 5 minute intervals around the clock. However every time I change the linApproxLen for the path iterator, it always either draws too many ellipses or 9 ellipses. How can I make these ellipses represent the 5 minute mark?
private Ellipse2D ellipse = new Ellipse2D.Double();
public void setClockContour(int w, int h)
{
size = (w > h) ? h/6f : w/6f;
ellipse.setFrame(w/2-size*2-4.5f,h/2-size*2-4.5f,size*4,size*4);
double linApproxLen = 0.75 * size * 0.258819; // sin(15 degree)
PathIterator pi = ellipse.getPathIterator(null, linApproxLen);
Point2D[] points = new Point2D[100];
int num_pts = 0;
while ( !pi.isDone() )
{
float[] pt = new float[6];
switch ( pi.currentSegment(pt) ) {
case FlatteningPathIterator.SEG_MOVETO:
case FlatteningPathIterator.SEG_LINETO:
points[num_pts] = new Point2D.Float(pt[0], pt[1]);
num_pts++;
}
pi.next();
}
pts = new Point2D[num_pts];
System.arraycopy(points, 0, pts, 0, num_pts);
}
I think that approach will not work regardless of the linApproxLen parameter. This parameter only specifies the "flatness" of the line. While this will certainly influence the number of line segments that are returned, the actual number will at least depend on the radius of the ellipse that you are painting. So even when you manage to return exactly 12 points in one case, changing the radius (that is, the size given as w and h) will affect the number of returned points.
Computing the positions of 12 points in a circular formation is particularly easy. You can compute the position on the circle with the sine and cosine of the respective angle. The resulting points may then be scaled or moved in order to obtain the desired shape.
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.RenderingHints;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Point2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class ClockCirclesTest
{
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{
#Override
public void run()
{
createAndShowGUI();
}
});
}
private static void createAndShowGUI()
{
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().setLayout(new GridLayout(1, 0));
frame.getContentPane().add(new ClockCirclesPanel());
frame.setSize(400,400);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
class ClockCirclesPanel extends JPanel
{
#Override
protected void paintComponent(Graphics gr)
{
super.paintComponent(gr);
Graphics2D g = (Graphics2D)gr;
g.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
int w = getWidth() / 2;
int h = getHeight() / 2;
Point2D points[] = computePoints(w, h, 12);
int cx = getWidth() / 2;
int cy = getHeight() / 2;
g.translate(cx, cy);
for (Point2D p : points)
{
g.draw(new Ellipse2D.Double(
p.getX() - 5, p.getY() - 5,
10, 10));
}
}
private Point2D[] computePoints(double w, double h, int n)
{
Point2D points[] = new Point2D[n];
double angleDeltaRad = Math.PI * 2 / n;
for (int i=0; i<n; i++)
{
double angleRad = i * angleDeltaRad;
double ca = Math.cos(angleRad);
double sa = Math.sin(angleRad);
double x = sa * w/2;
double y = ca * h/2;
points[i] = new Point2D.Double(x,y);
}
return points;
}
}
I have a Shape. I'm basically trying to split an area into two areas using a segment as the bisection.
public Shape divide(Shape a, Point2D p1, Point2D p2) {
Shape str = new BasicStroke().createStrokedShape(new Line2D.Double(p1,p2));
Shape line = new Shape(str);
Shape temp = a;
line.intersect(temp);
temp.exclusiveOr(line);
// temp is the shape with the line intersecting it
AffineTransform t = new AffineTransform();
double angle = Math.atan2(p2.getY() - p1.getY(), p2.getX() - p1.getX());
t.rotate(angle, p1.getX(), p1.getY());
temp = temp.createTransformedArea(t);
return Shape ;
}
I want to bisect the shape into two using the segment, but not sure how to go about it, I was looking at the intersection methods:
http://docs.oracle.com/javase/7/docs/api/java/awt/geom/Area.html but still not sure how to get two Areas from one. I'm hoping to return something like:
return firstHalf secondHalf;
I'd do it something like this. Note the code has a bug for when the point that start out to the right and lower ends up to the left of the upper left point. Left as an exercise for the user.
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.geom.*;
import java.awt.image.BufferedImage;
import javax.swing.*;
class SplitArea {
int s = 100;
JPanel gui = new JPanel(new BorderLayout());
BufferedImage[] images = new BufferedImage[4];
Point p1 = new Point(s / 4, s / 4);
Point p2 = new Point(s * 3 / 4, s * 3 / 4);
Ellipse2D ellipse = new Ellipse2D.Float(
s / 5, s / 5, s * 3 / 5, s * 3 / 5);
Rectangle2D bg = new Rectangle2D.Float(0, 0, s, s);
SplitArea() {
JToolBar tb = new JToolBar();
gui.add(tb, BorderLayout.PAGE_START);
final JToggleButton tob = new JToggleButton("Primary Point");
tb.add(tob);
JPanel view = new JPanel(new GridLayout(1, 0, 4, 4));
gui.add(view, BorderLayout.CENTER);
for (int ii = 0; ii < images.length; ii++) {
BufferedImage bi = new BufferedImage(
s, s, BufferedImage.TYPE_INT_RGB);
images[ii] = bi;
JLabel l = new JLabel(new ImageIcon(bi));
if (ii == 0) {
l.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
if (tob.isSelected()) {
p1 = e.getPoint();
} else {
p2 = e.getPoint();
}
drawImages();
}
});
}
view.add(l);
}
drawImages();
}
public final void drawImages() {
Graphics2D g;
// image 0
g = images[0].createGraphics();
g.setColor(Color.BLACK);
g.fill(bg);
g.setColor(Color.CYAN);
g.fill(ellipse);
g.setColor(Color.WHITE);
g.draw(ellipse);
g.setColor(Color.red);
drawPoint(g, p1);
drawPoint(g, p2);
g.dispose();
int xDiff = p1.x - p2.x;
int yDiff = p1.y - p2.y;
Point2D xAxis;
Point2D xSAxis;
if (xDiff == 0) {
xAxis = new Point2D.Double(p1.x, 0);
xSAxis = new Point2D.Double(p1.x, s);
} else if (yDiff == 0) {
xAxis = new Point2D.Double(0, p1.y);
xSAxis = new Point2D.Double(s, p1.y);
} else {
System.out.println("Not vertical or horizontal!");
// will throw a NaN if line is vertical
double m = (double) yDiff / (double) xDiff;
System.out.println("m: " + m);
double b = (double) p1.y - (m * (double) p1.x);
System.out.println("b: " + b);
// crosses x axis at..
xAxis = new Point2D.Double(0d, b);
double pointS = (s - b) / m;
xSAxis = new Point2D.Double(pointS, s);
}
// image 1
g = images[1].createGraphics();
g.setColor(Color.BLACK);
g.fill(bg);
g.setColor(Color.CYAN);
g.fill(ellipse);
g.setColor(Color.WHITE);
g.draw(ellipse);
g.setColor(Color.YELLOW);
System.out.println(xAxis);
System.out.println(xSAxis);
g.drawLine(
(int) xAxis.getX(), (int) xAxis.getY(),
(int) xSAxis.getX(), (int) xSAxis.getY());
g.setColor(Color.red);
drawPoint(g, p1);
drawPoint(g, p2);
g.dispose();
// image 2
g = images[1].createGraphics();
g.setColor(Color.BLACK);
g.fill(bg);
g.setColor(Color.CYAN);
g.fill(ellipse);
g.setColor(Color.WHITE);
g.draw(ellipse);
g.setColor(Color.YELLOW);
System.out.println(xAxis);
System.out.println(xSAxis);
g.drawLine(
(int) xAxis.getX(), (int) xAxis.getY(),
(int) xSAxis.getX(), (int) xSAxis.getY());
g.setColor(Color.red);
drawPoint(g, p1);
drawPoint(g, p2);
g.dispose();
// split the regions
Rectangle2D.Double all = new Rectangle2D.Double(0, 0, s, s);
Area a1 = new Area(all);
Area a2 = new Area(all);
GeneralPath aPart = new GeneralPath();
aPart.moveTo(0, 0);
aPart.lineTo(0, s);
aPart.lineTo(xSAxis.getX(), xSAxis.getY());
aPart.lineTo(xAxis.getX(), xAxis.getY());
aPart.closePath();
a1.subtract(new Area(aPart));
a2.subtract(a1);
Area ellipsePartA = new Area(ellipse);
ellipsePartA.subtract(a1);
Area ellipsePartB = new Area(ellipse);
ellipsePartB.subtract(a2);
// image 3
g = images[2].createGraphics();
g.setColor(Color.BLACK);
g.fill(bg);
g.setColor(Color.CYAN);
g.fill(ellipsePartA);
g.setColor(Color.WHITE);
g.draw(ellipsePartA);
g.setColor(Color.red);
drawPoint(g, p1);
drawPoint(g, p2);
g.dispose();
// image 4
g = images[3].createGraphics();
g.setColor(Color.BLACK);
g.fill(bg);
g.setColor(Color.CYAN);
g.fill(ellipsePartB);
g.setColor(Color.WHITE);
g.draw(ellipsePartB);
g.setColor(Color.red);
drawPoint(g, p1);
drawPoint(g, p2);
g.dispose();
gui.repaint();
}
public final void drawPoint(Graphics g, Point2D p) {
g.setColor(new Color(255, 0, 0, 128));
int x = (int) p.getX();
int y = (int) p.getY();
g.drawLine(x - 1, y, x - 5, y);
g.drawLine(x + 1, y, x + 5, y);
g.drawLine(x, y - 1, x, y - 5);
g.drawLine(x, y + 1, x, y + 5);
}
public Area[] split(Area a, Point2D p1, Point2D p2) {
Shape str = new BasicStroke().createStrokedShape(new Line2D.Double(p1, p2));
Area line = new Area(str);
Area temp = a;
line.intersect(temp);
temp.exclusiveOr(line);
// temp is the shape with the line intersecting it
Area[] areas = {new Area(temp)};
return areas;
}
public JComponent getGui() {
return gui;
}
public static void main(String[] args) {
Runnable r = new Runnable() {
#Override
public void run() {
SplitArea sa = new SplitArea();
JOptionPane.showMessageDialog(null, sa.getGui());
}
};
// Swing GUIs should be created and updated on the EDT
// http://docs.oracle.com/javase/tutorial/uiswing/concurrency
SwingUtilities.invokeLater(r);
}
}
Here is another https://stackoverflow.com/help/mcve (I started this "yesterday" at ~3:00 am, obviously Andrew Thompson is in a different time zone ;-))
The basic idea here is as follows:
The two given points define a line. That is, an infinite line, and not only a line segment. The corner points of the bounding box of the object are projected on this line and its perpendicular. This gives (an upper bound of) the extent of the object along these lines. These upper bounds can be used to define the "minimum half-spaces" above and below the line that are required to cover the respective half of the object. These half-spaces can then be intersected with the object to obtain the desired results.
The split method in this example receives a Graphics2D parameter. This is only used for "debugging" - that is, to show the intermediate results (extents, half-spaces) that are computed, and a preview of the final results. This Graphics g parameter (and the corresponding debugging output) can simply be removed (but it might also help to show the idea of the approach).
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Shape;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.awt.geom.AffineTransform;
import java.awt.geom.Area;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.awt.geom.Path2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class ShapeSplit
{
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 ShapeSplitPanel());
f.setSize(1100,600);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
}
class ShapeSplitPanel extends JPanel implements MouseMotionListener
{
private Shape inputShape = new Ellipse2D.Double(300,200,200,300);
private Point2D point0 = new Point2D.Double(200,300);
private Point2D point1 = new Point2D.Double(600,400);
ShapeSplitPanel()
{
addMouseMotionListener(this);
}
#Override
protected void paintComponent(Graphics gr)
{
super.paintComponent(gr);
Graphics2D g = (Graphics2D)gr;
g.setColor(Color.BLUE);
g.fill(inputShape);
g.setColor(Color.BLACK);
g.draw(new Line2D.Double(point0, point1));
g.fill(new Ellipse2D.Double(
point0.getX() - 3, point0.getY()-3, 6, 6));
g.fill(new Ellipse2D.Double(
point1.getX() - 3, point1.getY()-3, 6, 6));
split(new Area(inputShape), point0, point1, g);
}
private static Area[] split(Area a, Point2D p0, Point2D p1, Graphics2D g)
{
// Compute the direction of the line (L)
// and its perpendicular (P)
double dx = p1.getX() - p0.getX();
double dy = p1.getY() - p0.getY();
double length = Math.hypot(dx, dy);
double dirLx = dx / length;
double dirLy = dy / length;
double dirPx = -dirLy;
double dirPy = dirLx;
// Compute the minimum and maximum of all dot
// products that describe the distance of the
// projection of the corner points of the
// bounding box on on the line (L) and its
// perpendicular (P). These are upper limits
// for the extents of the object along these
// directions
double minDotL = Double.MAX_VALUE;
double maxDotL = -Double.MAX_VALUE;
double minDotP = Double.MAX_VALUE;
double maxDotP = -Double.MAX_VALUE;
Rectangle2D bounds = a.getBounds2D();
for (int i=0; i<4; i++)
{
Point2D corner = getCorner(bounds, i);
double pdx = corner.getX() - p0.getX();
double pdy = corner.getY() - p0.getY();
double dotL = dirLx * pdx + dirLy * pdy;
minDotL = Math.min(minDotL, dotL);
maxDotL = Math.max(maxDotL, dotL);
double dotP = dirPx * pdx + dirPy * pdy;
minDotP = Math.min(minDotP, dotP);
maxDotP = Math.max(maxDotP, dotP);
}
// Compute the start- and end points of
// the line segments describing the
// extent of the bounds along the line
// and the perpendicular
Point2D extentLmin = new Point2D.Double(
p0.getX() + minDotL * dirLx,
p0.getY() + minDotL * dirLy);
Point2D extentLmax = new Point2D.Double(
p0.getX() + maxDotL * dirLx,
p0.getY() + maxDotL * dirLy);
Point2D extentPmin = new Point2D.Double(
p0.getX() + minDotP * dirPx,
p0.getY() + minDotP * dirPy);
Point2D extentPmax = new Point2D.Double(
p0.getX() + maxDotP * dirPx,
p0.getY() + maxDotP * dirPy);
// Compute the two rectangles that cover
// each half of the object based on
// the given line
Path2D half0 = new Path2D.Double();
half0.moveTo(extentLmin.getX(), extentLmin.getY());
half0.lineTo(
extentLmin.getX() + minDotP * dirPx,
extentLmin.getY() + minDotP * dirPy);
half0.lineTo(
extentLmax.getX() + minDotP * dirPx,
extentLmax.getY() + minDotP * dirPy);
half0.lineTo(extentLmax.getX(), extentLmax.getY());
half0.closePath();
Path2D half1 = new Path2D.Double();
half1.moveTo(extentLmin.getX(), extentLmin.getY());
half1.lineTo(
extentLmin.getX() + maxDotP * dirPx,
extentLmin.getY() + maxDotP * dirPy);
half1.lineTo(
extentLmax.getX() + maxDotP * dirPx,
extentLmax.getY() + maxDotP * dirPy);
half1.lineTo(extentLmax.getX(), extentLmax.getY());
half1.closePath();
// Compute the resulting areas by intersecting
// the original area with both halves
Area a0 = new Area(a);
a0.intersect(new Area(half0));
Area a1 = new Area(a);
a1.intersect(new Area(half1));
// Debugging output
if (g != null)
{
g.setColor(Color.GRAY);
g.draw(bounds);
g.setColor(Color.RED);
g.draw(new Line2D.Double(extentLmin, extentLmax));
g.setColor(Color.GREEN);
g.draw(new Line2D.Double(extentPmin, extentPmax));
g.setColor(Color.YELLOW.darker());
g.draw(half0);
g.setColor(Color.MAGENTA);
g.draw(half1);
g.setColor(Color.BLUE);
g.fill(AffineTransform.getTranslateInstance(400, -20).
createTransformedShape(a0));
g.setColor(Color.BLUE);
g.fill(AffineTransform.getTranslateInstance(400, +20).
createTransformedShape(a1));
}
return new Area[] { a0, a1 };
}
private static Point2D getCorner(Rectangle2D r, int corner)
{
switch (corner)
{
case 0: return new Point2D.Double(r.getMinX(), r.getMinY());
case 1: return new Point2D.Double(r.getMinX(), r.getMaxY());
case 2: return new Point2D.Double(r.getMaxX(), r.getMaxY());
case 3: return new Point2D.Double(r.getMaxX(), r.getMinY());
}
return null;
}
#Override
public void mouseDragged(MouseEvent e)
{
point1.setLocation(e.getPoint());
repaint();
}
#Override
public void mouseMoved(MouseEvent e)
{
}
}
EDIT An aside: Technically, it could be easier (or even more elegant) to transform the original shape and the line so that the line matches the x-axis, then defining the half-spaces to be clipped against (which in this case could be simple Rectangle2Ds), and transforming the clipped results back into the original orientation. But I wanted to compute it "in-place", without having to create many transformed shapes.
EDIT2: Another snippet for the comment, to be inserted directly before the // Debugging output
AffineTransform t = new AffineTransform();
double angle = Math.atan2(p1.getY() - p0.getY(), p1.getX() - p0.getX());
t.rotate(-angle, p0.getX(), p0.getY());
a0 = a0.createTransformedArea(t);
a1 = a1.createTransformedArea(t);
EDIT3 The second approach, only the relevant method this time
private static Area[] split(Area a, Point2D p0, Point2D p1, Graphics2D g)
{
// Compute the angle of the line to the x-axis
double dx = p1.getX() - p0.getX();
double dy = p1.getY() - p0.getY();
double angleRadToX = Math.atan2(dy, dx);
// Align the area so that the line matches the x-axis
AffineTransform at = new AffineTransform();
at.rotate(-angleRadToX);
at.translate(-p0.getX(), -p0.getY());
Area aa = a.createTransformedArea(at);
// Compute the upper and lower halves that the area
// has to be intersected with
Rectangle2D bounds = aa.getBounds2D();
double half0minY = Math.min(0, bounds.getMinY());
double half0maxY = Math.min(0, bounds.getMaxY());
Rectangle2D half0 = new Rectangle2D.Double(
bounds.getX(), half0minY,
bounds.getWidth(), half0maxY-half0minY);
double half1minY = Math.max(0, bounds.getMinY());
double half1maxY = Math.max(0, bounds.getMaxY());
Rectangle2D half1 = new Rectangle2D.Double(
bounds.getX(), half1minY,
bounds.getWidth(), half1maxY-half1minY);
// Compute the resulting areas by intersecting
// the original area with both halves, and
// transform them back to their initial position
Area a0 = new Area(aa);
a0.intersect(new Area(half0));
Area a1 = new Area(aa);
a1.intersect(new Area(half1));
try
{
at.invert();
}
catch (NoninvertibleTransformException e)
{
// Always invertible
}
a0 = a0.createTransformedArea(at);
a1 = a1.createTransformedArea(at);
// Debugging output
if (g != null)
{
g.setColor(Color.GRAY);
g.draw(bounds);
g.setColor(Color.RED);
g.draw(aa);
g.setColor(Color.YELLOW.darker());
g.draw(half0);
g.setColor(Color.MAGENTA);
g.draw(half1);
g.setColor(Color.BLUE.darker());
g.fill(AffineTransform.getTranslateInstance(400, -20).
createTransformedShape(a0));
g.setColor(Color.BLUE.brighter());
g.fill(AffineTransform.getTranslateInstance(400, +20).
createTransformedShape(a1));
}
return new Area[] { a0, a1 };
}
Interesting question.
There are no methods that help you with this directly, but by calculating the bounding rectangle and intersecting with two opposing rectangles from your dividing line, you should be able to create such a method.
The general idea is to
Find the bounding rectangle of your original area: either getBounds() or getBounds2D().
Calculate two rectangles from your line that overlaps your area on both sides of the line. When doing this you will have to take several special cases into account (like is the line long enough, does it intersect the area at all, does the rectangles completely overlap each side of the original area, etc). The size of the rectangles should be decided by the bounding rectangle of your original area.
Get the two areas by intersecting each of the two rectangles with your original area, i.e. by using the intersect() method
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;
}