I want to create a graphics whereby circles are overlapped and their colour fades out, also the circles should have a white space in between each other, something similar to this:
Here is how far I've got:
import java.awt.*;
import java.awt.geom.*;
import javax.swing.*;
import java.awt.event.MouseListener;
import java.awt.event.MouseEvent;
class Circles extends JPanel implements MouseListener {
public void mouseReleased(MouseEvent event) {
}
public void mousePressed(MouseEvent event) {
}
public void mouseClicked(MouseEvent event) {
}
public void mouseEntered(MouseEvent event) {}
public void mouseExited(MouseEvent event) {}
public Circles() {
addMouseListener(this);
}
public void paint(Graphics g) {
g.setColor(Color.white);
int w = getWidth();
int h = getHeight();
g.fillRect(0, 0, w, h);
Graphics2D g2 = (Graphics2D) g;
for (int i = 0; i < 20; i++) {
if (i % 2 == 0) {
Color c = new Color(255 - i * 10, 255 - i * 10, 255 - i * 10);
g2.setColor(c);
} else {
g2.setColor(Color.white);
}
g2.fillOval((5 * i), (5 * i), (w - 10) * i, (h - 10) * i);
}
}
public static void main(String[] args) {
Frame w = new Frame();
w.setVisible(true);
}
}
Frame:
import java.awt.*;
import java.awt.geom.*;
import javax.swing.*;
public class Frame extends JFrame {
Circles myPanel;
public static void main(String[] args) {
Frame w = new Frame();
w.setVisible(true);
}
public Frame() {
setTitle("In-class Test 1: Starting Code");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(500, 220);
setLocation(300, 300);
myPanel = new Circles();
add(myPanel);
}
}
There are a number of ways to achieve this, but first, start by overriding paintComponent instead of paint.
Second, you will need some kind of loop that expand the radius of the circle on each iteration.
Thirdly, you will want a simple int value that acts as a alpha value, starting from 255 and decreases down to the faintest alpha level you want, perhaps 64 of example.
On each iteration of the loop, you will want to increase the radius value and decrease the alpha value accordingly. You would then simply need to create a new Color object with the RGB & alpha values you need
Check out 2D Graphics for details
Updated with example...
import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.Ellipse2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class FadingCircles {
public static void main(String[] args) {
new FadingCircles();
}
public FadingCircles() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public static class TestPane extends JPanel {
public static final int CIRCLE_COUNT = 10;
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
int maxRadius = Math.min(getWidth(), getHeight());
int alpha = 255;
int range = 255 - 32;
g2d.setStroke(new BasicStroke(6, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
for (int index = 0; index < CIRCLE_COUNT; index++) {
float progress = (float) index / (float) CIRCLE_COUNT;
alpha = 255 - Math.round(range * progress);
Color color = new Color(0, 0, 0, alpha);
g2d.setColor(color);
int radius = Math.round(maxRadius * progress);
int x = (getWidth() - radius) / 2;
int y = (getHeight() - radius) / 2;
g2d.draw(new Ellipse2D.Float(x, y, radius, radius));
}
g2d.dispose();
}
}
}
You might also like to check out Performing Custom Painting
Related
I'm creating a 2D topdown shooter game with Java Swing in which I want circular hitboxes for my player and enemies as well as projectiles. For hit detection I need to figure out if there is an intersection between a projtile and a Sprite (player or enemy). My issue is that Ellipse2D's intersect function (that takes a position a width and height) creates a Rectangle out of the arguments. In its description it advises using Area for high precision and I was hoping it had an intersect function for any shape but that also casts its argument to a Rectangle.
Here's the jist of my Sprite object:
public class Sprite {
protected float worldX;
protected float worldY;
protected float drawX;
protected float drawY;
protected int width;
protected int height;
protected Image image;
...
}
In essence I'm storing their x and y coordinates as well as their width and height.(drawX and drawY are only used for rendering)
Is there a build-in method (preferably in Swing) to intersect Ellipses with other shapes (specifically other Ellipses and Rectangles) or is there no better option then implementing these by hand?
With a simple modification to the answer form Detecting collision of two sprites that can rotate
You can end up with something like...
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.AffineTransform;
import java.awt.geom.Area;
import java.awt.geom.Ellipse2D;
import java.awt.geom.GeneralPath;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private Ellipse2D rect01;
private Rectangle rect02;
private int angle = 0;
public TestPane() {
rect01 = new Ellipse2D.Double(0, 0, 200, 50);
rect02 = new Rectangle(0, 0, 100, 100);
Timer timer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
angle++;
repaint();
}
});
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(250, 250);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
int width = getWidth();
int height = getHeight();
AffineTransform at = new AffineTransform();
int center = width / 2;
int x = center + (center - rect01.getBounds().width) / 2;
int y = (height - rect01.getBounds().height) / 2;
at.translate(x, y);
at.rotate(Math.toRadians(angle), rect01.getBounds().width / 2, rect01.getBounds().height / 2);
GeneralPath path1 = new GeneralPath();
path1.append(rect01.getPathIterator(at), true);
g2d.fill(path1);
g2d.setColor(Color.BLUE);
g2d.draw(path1.getBounds());
at = new AffineTransform();
x = (center - rect02.width) / 2;
y = (height - rect02.height) / 2;
at.translate(x, y);
at.rotate(Math.toRadians(-angle), rect02.width / 2, rect02.height / 2);
GeneralPath path2 = new GeneralPath();
path2.append(rect02.getPathIterator(at), true);
g2d.fill(path2);
g2d.setColor(Color.BLUE);
g2d.draw(path2.getBounds());
Area a1 = new Area(path1);
Area a2 = new Area(path2);
a2.intersect(a1);
if (!a2.isEmpty()) {
g2d.setColor(Color.RED);
g2d.fill(a2);
}
g2d.dispose();
}
}
}
The code creates a JFrame with a JPanel onto which it draws an image loaded from a file. The objective is to make a rectangular area of the picture, such as for example the red square, appear darker than the rest. I'm assuming this may involve taking a subimage of the image, looping through an array of pixels, scaling them, and then painting that subimage onto the JPanel, but I don't know how to do this using the Java API.
package SpriteEditor_Tests;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.io.*;
import javax.imageio.*;
import javax.swing.*;
public class ImageTestApp extends JFrame
{
public BufferedImage image;
int x1 = 50;
int x2 = 100;
int y1 = 50;
int y2 = 100;
public static void main (String [] args)
{
new ImageTestApp();
}
public ImageTestApp()
{
setTitle("Image Test App");
try
{
image = ImageIO.read(new File("C:/Users/Paul/Desktop/derp.png"));
}
catch (IOException io)
{
System.out.println("IO caught"); System.exit(0);
}
setSize(500,500);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
add(new ImageDisplay());
}
class ImageDisplay extends JPanel
{
public void paintComponent(Graphics g)
{
g.drawImage(image, -100, -100, getWidth(), getHeight(), Color.RED, null);
g.setColor(Color.RED);
g.drawRect(x1, y1, Math.abs(x2 - x1), Math.abs(y2 - y1));
}
}
}
A "simple" solution would be to just create a new instance of Color with the desired alpha applied to it and fill the area you want darkened.
This is great if you have a color you want to use, but when I want to use a predefined color, it's not as simple. Instead, I prefer to use an AlphaComposite as it gives me some advantages.
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private BufferedImage background;
public TestPane() {
try {
background = ImageIO.read(getClass().getResource("/images/background.jpg"));
} catch (IOException ex) {
ex.printStackTrace();
}
}
#Override
public Dimension getPreferredSize() {
if (background == null) {
return new Dimension(200, 200);
}
return new Dimension(background.getWidth(), background.getHeight());
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (background == null) {
return;
}
Graphics2D g2d = (Graphics2D) g.create();
g2d.drawImage(background, 0, 0, this);
int x = (getWidth() - 100) / 2;
int y = (getHeight() - 100) / 2;
Rectangle rect = new Rectangle(x, y, 200, 200);
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.5f));
g2d.fill(rect);
g2d.dispose();
g2d = (Graphics2D) g.create();
g2d.setColor(Color.RED);
g2d.drawRect(x, y, 200, 200);
g2d.dispose();
}
}
}
Now, if want to generate a new image with the are darkened, you can follow the same basic concept, but instead of painting to the components Graphics context, you'd paint directly to the BufferedImages Graphics content. This is the wonderful power of the abstract nature of the Graphics API.
Don't forget, when you override a method, you are obliged to either over take ALL of its responsibilities or call its super implementation.
paintComponent does some basic, but important work and you should make sure to call super.paintComponent before you start performing your custom painting, this will just reduce any possibility of issues.
Darken each pixel individually
Okay, if, instead, you want to darken each pixel in the rectangle individually, this becomes a "little" more complicated, but not hard.
After a lot of time and testing, I settled on using the follow algorithm to darken a given color. This will push the color towards "black" the more you darken it, which some algorithms don't do.
public static Color darken(Color color, double fraction) {
int red = (int) Math.round(Math.max(0, color.getRed() - 255 * fraction));
int green = (int) Math.round(Math.max(0, color.getGreen() - 255 * fraction));
int blue = (int) Math.round(Math.max(0, color.getBlue() - 255 * fraction));
int alpha = color.getAlpha();
return new Color(red, green, blue, alpha);
}
Then, all you have to do is get the the color of the pixel, darken it and reapply.
For this example, I actually use a separate sub image, but you can do it directly to the parent image
BufferedImage subImage = background.getSubimage(x, y, 200, 200);
for (int row = 0; row < subImage.getHeight(); row++) {
for (int col = 0; col < subImage.getWidth(); col++) {
int packedPixel = subImage.getRGB(col, row);
Color color = new Color(packedPixel, true);
color = darken(color, 0.5);
subImage.setRGB(col, row, color.getRGB());
}
}
Now, before someone jumps down my throat, no, this is not the most performant approach, but it gets over messing about with "packed" pixel values (because I can never remember how to unpack those :P) and most of my API code is based around the use of Color anyway
Runnable example...
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setVisible(true);
}
});
}
public static Color darken(Color color, double fraction) {
int red = (int) Math.round(Math.max(0, color.getRed() - 255 * fraction));
int green = (int) Math.round(Math.max(0, color.getGreen() - 255 * fraction));
int blue = (int) Math.round(Math.max(0, color.getBlue() - 255 * fraction));
int alpha = color.getAlpha();
return new Color(red, green, blue, alpha);
}
public class TestPane extends JPanel {
private BufferedImage background;
private BufferedImage darkended;
public TestPane() {
try {
background = ImageIO.read(getClass().getResource("/images/background.jpg"));
int x = (background.getWidth() - 100) / 2;
int y = (background.getHeight() - 100) / 2;
BufferedImage subImage = background.getSubimage(x, y, 200, 200);
for (int row = 0; row < subImage.getHeight(); row++) {
for (int col = 0; col < subImage.getWidth(); col++) {
int packedPixel = subImage.getRGB(col, row);
Color color = new Color(packedPixel, true);
color = darken(color, 0.5);
subImage.setRGB(col, row, color.getRGB());
}
}
darkended = subImage;
} catch (IOException ex) {
ex.printStackTrace();
}
}
#Override
public Dimension getPreferredSize() {
if (background == null) {
return new Dimension(200, 200);
}
return new Dimension(background.getWidth(), background.getHeight());
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (background == null) {
return;
}
Graphics2D g2d = (Graphics2D) g.create();
g2d.drawImage(background, 0, 0, this);
int x = (getWidth() - 100) / 2;
int y = (getHeight() - 100) / 2;
g2d.drawImage(darkended, x, y, this);
g2d.setColor(Color.RED);
g2d.drawRect(x, y, 200, 200);
g2d.dispose();
}
}
}
I want to create a wheel of fortune game for practice.
I created the wheel slices using GeneralPath, but I'm having issues finding out what is supposed to be the beizer point to have a perfect circle. Take a look at the screenshot and you'll see the issue.
I don't know the formula I should use to get the proper value, now I'm basically just guessing with:
path.moveTo(x, y); // the center
this.firstPointX = x + wSize * Math.cos(angle*i);
this.firstPointY = y + wSize * Math.sin(angle*i);
path.lineTo(this.firstPointX, this.firstPointY);
this.secondPointX = x + wSize * Math.cos(angle*(i+1));
this.secondPointY = y + wSize * Math.sin(angle*(i+1));
path.moveTo(x, y); // back to the center
path.lineTo(this.secondPointX, this.secondPointY);
this.beizerX = x + (THIS IS THE VALUE I NEED) * Math.cos((angle*i+angle/2));
this.beizerY = y + (THIS IS THE VALUE I NEED) * Math.sin((angle*i+angle/2));
path.curveTo(this.secondPointX, this.secondPointY, this.beizerX, this.beizerY, this.firstPointX, this.firstPointY);
path.closePath();
Why not just use Arc2D or simply draw a circle and 6 lines? Then the basic problem is how to determine two points on a circle (start point and end point, which is 180 degrees away)
Maybe something like...
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.Ellipse2D;
import java.awt.geom.GeneralPath;
import java.awt.geom.Point2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private GeneralPath gp = new GeneralPath();
public TestPane() {
//GeneralPath gp = new GeneralPath();
gp.append(new Ellipse2D.Double(0, 0, 200, 200), true);
for (double angle = 0; angle < 180; angle += 30) {
Point2D startPoint = pointOnCircle(angle, 100);
Point2D endPoint = pointOnCircle(angle + 180, 100);
gp.moveTo(startPoint.getX(), startPoint.getY());
gp.lineTo(endPoint.getX(), endPoint.getY());
}
}
protected Point2D pointOnCircle(double degrees, double radius) {
double origin = radius;
double rads = Math.toRadians(degrees);
double x = origin + (Math.cos(rads) * radius);
double y = origin + (Math.sin(rads) * radius);
return new Point2D.Double(x, y);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(300, 300);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
RenderingHints hints = new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setRenderingHints(hints);
g2d.translate(50, 50);
g2d.draw(gp);
g2d.dispose();
}
}
}
Arc2D
Because, you know, you can
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.Arc2D;
import java.awt.geom.Ellipse2D;
import java.awt.geom.GeneralPath;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private GeneralPath gp = new GeneralPath();
public TestPane() {
gp.append(new Ellipse2D.Double(0, 0, 200, 200), true);
for (double angle = 0; angle < 360; angle += 30) {
Arc2D arc = new Arc2D.Double(0, 0, 200, 200,
angle,
30,
Arc2D.PIE);
gp.append(arc, false);
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(300, 300);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
RenderingHints hints = new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setRenderingHints(hints);
g2d.translate(50, 50);
g2d.draw(gp);
g2d.dispose();
}
}
}
I tried to use arches but I had trouble rotating them.
Easy as Pi
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.AffineTransform;
import java.awt.geom.Arc2D;
import java.awt.geom.Ellipse2D;
import java.awt.geom.GeneralPath;
import java.awt.geom.Rectangle2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private GeneralPath gp = new GeneralPath();
private double spinValue = 0;
public TestPane() {
gp.append(new Ellipse2D.Double(0, 0, 200, 200), true);
for (double angle = 0; angle < 360; angle += 30) {
Arc2D arc = new Arc2D.Double(0, 0, 200, 200,
angle,
30,
Arc2D.PIE);
gp.append(arc, false);
}
Timer timer = new Timer(5, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
spinValue += 0.01;
repaint();
}
});
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(300, 300);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
RenderingHints hints = new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setRenderingHints(hints);
Rectangle2D bounds = gp.getBounds2D();
double x = (getWidth() - bounds.getWidth()) / 2d;
double y = (getHeight() - bounds.getHeight()) / 2d;
AffineTransform at = AffineTransform.getTranslateInstance(x, y);
at.rotate(spinValue, bounds.getCenterX(), bounds.getCenterY());
g2d.transform(at);
g2d.draw(gp);
g2d.dispose();
}
}
}
I also would like to fill the single slices with gradients and animate indipendently with rotations and such
Then you can't use a single Path, as it's treated as a single graphical element, you will need to use individual slices, so I would highly recommend using Arc2D as it would be simple to maintain a list of them and simply transform the Graphics context as needed
I resurrect an ancient topic so I really sorry about that. )
But i faced a similar problem recently and here is my solution.
Here's a picture described my decision
And finally my code (variables names are got from the topicstarter's post).
// x, y - center;
// wSize - radius of the circle
// angle = 2 * Math.PI / (number_of_sectors) - sector angle
// before the cycle
double bezierAngle = Math.atan(Math.tan(angle/4) * 4/3);
double bezierRadius = wSize / Math.cos(bezierAngle);
// inside the cycle
path.moveTo(x, y); // center
firstPointX = x + wSize * Math.cos(angle*i);
firstPointY = y + wSize * Math.sin(angle*i);
path.lineTo(firstPointX, firstPointY);
firstBezierX = x + bezierRadius * Math.cos(angle*i + bezierAngle);
firstBezierY = y + bezierRadius * Math.sin(angle*i + bezierAngle);
secondBezierX = x + bezierRadius * Math.cos(angle*(i+1) - bezierAngle);
secondBezierY = y + bezierRadius * Math.sin(angle*(i+1) - bezierAngle);
secondPointX = x + wSize * Math.cos(angle*(i+1));
secondPointY = y + wSize * Math.sin(angle*(i+1));
path.curveTo(firstBezierX, firstBezierY, secondBezierX, secondBezierY, secondPointX, secondPointY);
path.lineTo(x, y); // back to the center
path.closePath();
I want to make a directed line and make it move. I am able to make a directed line and move the line but the arrow get displaced while i move the line
This is my paint method
Line2D.Double line = new Line2D.Double(startX, startY, endX, endY);
g2d.draw(line);
tx.setToIdentity();
double angle = Math.atan2(line.y2 - line.y1, line.x2 - line.x1);
tx.translate(line.x2, line.y2);
tx.rotate((angle - Math.PI / 2d));
Graphics2D gClone = (Graphics2D) g2d.create();
gClone.setTransform(tx);
Polygon arrowHead = new Polygon();
arrowHead.addPoint(0, 15);
arrowHead.addPoint(-15, -15);
arrowHead.addPoint(15, -15);
Area area = new Arear(arrowHead );
Area lineArea = new Area(line);
lineArea.subtract(area);
gClone.fill(area);
gClone.dispose();
and i have change the value of startx and start y at mouse pressedd
and endx and enny at mouse drag
This is a basic example that demonstrates the use of a Path2D, to define a custom shape and an AffineTransformation to transform the shape.
This example will cause the arrow to point towards the mouse as it moves over the panel
import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.AffineTransform;
import java.awt.geom.Path2D;
import javax.swing.JFrame;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class RotateArrow {
public static void main(String[] args) {
new RotateArrow();
}
public RotateArrow() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new RotatePane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class RotatePane extends javax.swing.JPanel {
private Point mousePoint;
private PointyThing pointyThing;
public RotatePane() {
pointyThing = new PointyThing();
addMouseMotionListener(new MouseAdapter() {
#Override
public void mouseMoved(MouseEvent e) {
mousePoint = e.getPoint();
repaint();
}
});
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
double rotation = 0f;
int width = getWidth() - 1;
int height = getHeight() - 1;
if (mousePoint != null) {
int x = width / 2;
int y = height / 2;
int deltaX = mousePoint.x - x;
int deltaY = mousePoint.y - y;
rotation = -Math.atan2(deltaX, deltaY);
rotation = Math.toDegrees(rotation) + 180;
}
Rectangle bounds = pointyThing.getBounds();
AffineTransform at = new AffineTransform();
at.translate((width - bounds.width) / 2, (height - bounds.height) / 2);
at.rotate(Math.toRadians(rotation), bounds.width / 2, bounds.height / 2);
Shape shape = new Path2D.Float(pointyThing, at);
g2d.setStroke(new BasicStroke(3));
g2d.setColor(Color.RED);
g2d.fill(shape);
g2d.draw(shape);
g2d.dispose();
}
}
public class PointyThing extends Path2D.Float {
public PointyThing() {
moveTo(15, 0);
lineTo(30, 15);
lineTo(0, 15);
lineTo(15, 0);
moveTo(15, 15);
lineTo(15, 60);
}
}
}
Update with example two
Basically, this uses the PointyThing as the arrow head only, and draws the line separately.
The arrow is orientated so it will point down the line (towards the mouse)
import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.AffineTransform;
import java.awt.geom.Line2D;
import java.awt.geom.Path2D;
import javax.swing.JFrame;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class RotateArrow {
public static void main(String[] args) {
new RotateArrow();
}
public RotateArrow() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new RotatePane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class RotatePane extends javax.swing.JPanel {
private PointyThing pointyThing;
private Point mouseStart;
private Point mouseEnd;
public RotatePane() {
pointyThing = new PointyThing();
MouseAdapter ma = new MouseAdapter() {
#Override
public void mouseMoved(MouseEvent e) {
mouseEnd = e.getPoint();
repaint();
}
#Override
public void mouseClicked(MouseEvent e) {
mouseStart = e.getPoint();
repaint();
}
};
addMouseListener(ma);
addMouseMotionListener(ma);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
if (mouseStart != null && mouseEnd != null) {
double rotation = 0f;
int width = getWidth() - 1;
int height = getHeight() - 1;
if (mouseEnd != null) {
int x = mouseStart.x;
int y = mouseStart.y;
int deltaX = mouseEnd.x - x;
int deltaY = mouseEnd.y - y;
rotation = -Math.atan2(deltaX, deltaY);
rotation = Math.toDegrees(rotation) + 180;
}
Rectangle bounds = pointyThing.getBounds();
g2d.setStroke(new BasicStroke(3));
g2d.setColor(Color.RED);
g2d.draw(new Line2D.Float(mouseStart, mouseEnd));
AffineTransform at = new AffineTransform();
at.translate(mouseEnd.x - (bounds.width / 2), mouseEnd.y - (bounds.height / 2));
at.rotate(Math.toRadians(rotation), bounds.width / 2, bounds.height / 2);
Shape shape = new Path2D.Float(pointyThing, at);
g2d.fill(shape);
g2d.draw(shape);
}
g2d.dispose();
}
}
public class PointyThing extends Path2D.Float {
public PointyThing() {
moveTo(15, 0);
lineTo(30, 15);
lineTo(0, 15);
lineTo(15, 0);
}
}
}
I want to represent a timer by having a filled circle which is fully drawn over the course of the timer by segment. I.e. If the circle is filled in every 1 second for a timer of 4 seconds the first will show a quarter of a circle then a half then three-quarters and finally a full circle.
Is there a way to draw these slices of a circle in java? I've looked into arbitrary shapes in the graphics API but not sure if this is the right way to go or if there is something written into the language which can easily produce these type of shapes?
Any help much appreciated.
Yes, it's possible. Yes, Graphics2D has the ability to do this for you
Take a look at Drawing Geometric Primitives (look for the Arc2D section).
To "animate" the progress, it would probably be easiest to use a javax.swing.Timer, but your requirements might require you to use a SwingWorker instead. Have a look at Currency in Swing and How to use Swing Timers for more information.
The following example is relatively simple. It assumes a progress of 0-100% and generates a arc as required. It would be a simple matter of changing the color of the Graphics2D context and using draw(Shape) to draw an out line of the circle should you want one.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Arc2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class ArcProgress {
public static void main(String[] args) {
new ArcProgress();
}
private float progress;
public ArcProgress() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
final ArcProgressPane p1 = new ArcProgressPane();
p1.setForeground(Color.RED);
final ArcProgressPane p2 = new ArcProgressPane();
p2.setForeground(Color.BLUE);
p2.setFillProgress(true);
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new FlowLayout());
frame.add(p1);
frame.add(p2);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
Timer timer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
progress += 0.01f;
if (progress >= 1f) {
progress = 1f;
((Timer) e.getSource()).stop();
}
p1.setProgress(progress);
p2.setProgress(progress);
}
});
timer.setRepeats(true);
timer.setCoalesce(true);
timer.start();
}
});
}
public class ArcProgressPane extends JPanel {
private boolean fillProgress = false;
private float progress;
public ArcProgressPane() {
}
public void setFillProgress(boolean value) {
if (fillProgress != value) {
this.fillProgress = value;
firePropertyChange("fillProgress", !fillProgress, fillProgress);
}
}
public boolean isFillProgress() {
return fillProgress;
}
#Override
public Dimension getPreferredSize() {
return new Dimension(100, 100);
}
public void setProgress(float value) {
if (progress != value) {
float old = progress;
this.progress = value;
firePropertyChange("progress", old, progress);
repaint();
}
}
public float getProgress() {
return progress;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
Insets insets = getInsets();
int width = getWidth() - (insets.left + insets.right);
int height = getHeight() - (insets.bottom + insets.top);
int raidus = Math.min(width, height);
int x = insets.left + ((width - raidus) / 2);
int y = insets.right + ((height - raidus) / 2);
double extent = 360d * progress;
g2d.setColor(getForeground());
Arc2D arc = null;
if (isFillProgress()) {
arc = new Arc2D.Double(x, y, raidus, raidus, 90, -extent, Arc2D.PIE);
} else {
extent = 360 - extent;
arc = new Arc2D.Double(x, y, raidus, raidus, 90, extent, Arc2D.PIE);
}
g2d.fill(arc);
g2d.dispose();
}
}
}