Moving Objects along an arc path with java graphics - java

I had 3 days looking for moving on objects on a spiral way but I figured that I have to look for the small part of problem which is moving objects on arc.
See my Question :https://stackoverflow.com/questions/36917560/moving-rectangle-spiral-animation-java?noredirect=1#comment61428911_36917560
Now, the problem is how i can calculate the points that are exists on the arc. this is my Approach to get the new X and Y Points( Algorithm not code )
1- Draw arc using this method in JAVA
g2d.fillArc(start_point_X_Arc,start_point_Y_Arc,width_of_arc,height_of_arc,start_angle,end_angle);
2- Draw the Object on the Same Start_point_X,Start_point_Y. And here I will draw a rectangle using this method
g2d.drawRect(start_point_X_Rect, Start_point_Y_Rect, 10, 10);
3- Because I'm using a timer and it needs an ActionListener the actionPerformed method will update the Values of Start_point_X, Start_point_Y for the rectangle
AND HERE IS THE PROBLEM I can't calculate the values of the New X,Y values for the object which will do the moving part of the problem ( I know that these word are not professional words ).
Because of that I search how to calculate points on arc and I find the Parametric Equations for a circle
x = center_X + radius * cos(angle)
y = center_y + radius * sin(angle)
and I know that these equation might be used in someway to get the new points but i'm not good in math.
Therefore,I need help with doing the object moving in arc path and i think this would help me to do an object moving in spiral path. If my algorithm is wrong or anything is wrong please give me advice to do it in a simple way.
This is a code that I made it to draw an arc & rectangle and the rectangle is moving in diagonal path.
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.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.AffineTransform;
import java.awt.geom.Arc2D;
import java.awt.geom.Path2D;
import java.awt.geom.PathIterator;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Test {
public static void main(String[] args) {
new Test();
}
public class SpiralPath extends Path2D.Double {
public SpiralPath(int size) {
int numIterations = 5;
int arcGrowDelta = (size / numIterations) / 2;
int arcWidth = 0;
int centerX = size / 2;
int centerY = size / 2;
moveTo(centerX, centerY);
for (int i = 0; i < numIterations; i++) {
append(new Arc2D.Double(centerX - arcWidth, centerY - arcWidth, 2 * arcWidth, 2 * arcWidth, 180, 180, Arc2D.OPEN), true);
arcWidth += arcGrowDelta;
append(new Arc2D.Double(centerX - arcWidth, centerY - arcWidth, 2 * arcWidth - arcGrowDelta, 2 * arcWidth, 0, 180, Arc2D.OPEN), true);
}
}
}
public class SpiralPath2 extends Path2D.Double {
public SpiralPath2(int size) {
int numIterations = 5;
int arcGrowDelta = (size / numIterations) / 2;
int arcWidth = 0;
int centerX = size / 2+200;
int centerY = size / 2;
moveTo(centerX, centerY);
for (int i = 0; i < numIterations; i++) {
append(new Arc2D.Double(centerX - arcWidth, centerY - arcWidth, 2 * arcWidth, 2 * arcWidth, 180, 180, Arc2D.OPEN), true);
arcWidth += arcGrowDelta;
append(new Arc2D.Double(centerX - arcWidth, centerY - arcWidth, 2 * arcWidth - arcGrowDelta, 2 * arcWidth, 0, 180, Arc2D.OPEN), true);
}
}
}
public Test() {
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 SpiralPath spiralPath;
private final Rectangle box;
private List<Point2D> points;
private double angle;
private Point2D pos;
private int index;
private SpiralPath2 spiralPath2;
private final Rectangle box2;
private List<Point2D> points2;
private double angle2;
private Point2D pos2;
private int index2;
protected static final double PLAY_TIME = 5000; // 5 seconds...
private Long startTime;
public TestPane() {
spiralPath = new SpiralPath(150);
box = new Rectangle(0, 0, 10, 10);
points = new ArrayList<>(25);
PathIterator pi = spiralPath.getPathIterator(null, 0.01);
while (!pi.isDone()) {
double[] coords = new double[6];
switch (pi.currentSegment(coords)) {
case PathIterator.SEG_MOVETO:
case PathIterator.SEG_LINETO:
points.add(new Point2D.Double(coords[0], coords[1]));
break;
}
pi.next();
}
spiralPath2 = new SpiralPath2(200);
box2 = new Rectangle(0, 0, 10, 10);
points2 = new ArrayList<>(25);
PathIterator pi2 = spiralPath2.getPathIterator(null, 0.01);
while (!pi2.isDone()) {
double[] coords = new double[6];
switch (pi2.currentSegment(coords)) {
case PathIterator.SEG_MOVETO:
case PathIterator.SEG_LINETO:
points2.add(new Point2D.Double(coords[0], coords[1]));
break;
}
pi2.next();
}
pos = points.get(0);
pos2 = points2.get(0);
Timer timer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (startTime == null) {
startTime = System.currentTimeMillis();
}
long playTime = System.currentTimeMillis() - startTime;
double progress = playTime / PLAY_TIME;
if (progress >= 1.0) {
progress = 1d;
((Timer) e.getSource()).stop();
}
int index = Math.min(Math.max(0, (int) (points.size() * progress)), points.size() - 1);
int index2 = Math.min(Math.max(0, (int) (points2.size() * progress)), points2.size() - 1);
pos = points.get(index);
pos2 = points2.get(index2);
if (index < points.size() - 1) {
angle = angleTo(pos, points.get(index + 1));
}
if (index2 < points2.size() - 1) {
angle2 = angleTo(pos2, points2.get(index + 1));
}
repaint();
}
});
timer.start();
}
protected double angleTo(Point2D from, Point2D to) {
double angle = Math.atan2(to.getY() - from.getY(), to.getX() - from.getX());
return angle;
}
#Override
public Dimension getPreferredSize() {
return new Dimension(500, 400);
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
applyQualityRenderingHints(g2d);
g2d.translate(20, 50);
g2d.draw(spiralPath);
g2d.draw(spiralPath2);
AffineTransform at = new AffineTransform();
AffineTransform at2 = new AffineTransform();
if (pos != null &&pos2!=null) {
Rectangle bounds = box.getBounds();
at.rotate(angle, (bounds.width / 2), (bounds.width / 2));
Path2D player = new Path2D.Double(box, at);
g2d.translate(pos.getX() - (bounds.width / 2), pos.getY() - (bounds.height / 2));
g2d.setColor(Color.RED);
g2d.draw(player);
}
Rectangle bounds2 = box2.getBounds();
at2.rotate(angle2, (bounds2.width / 2), (bounds2.width / 2));
Path2D player2 = new Path2D.Double(box2, at2);
g2d.translate(pos2.getX() - (bounds2.width / 2)+50, pos2.getY() - (bounds2.height / 2));
g2d.setColor(Color.RED);
g2d.draw(player2);
g2d.dispose();
}
}
public static void applyQualityRenderingHints(Graphics2D g2d) {
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);
}
}

So based on this idea, you can take advantage of the functionality already available in the 2D Graphics API.
The difficult part would be to get your spiral shape setup as a Path object, lucky for us, the API is very flexible ...
public class SpiralPath extends Path2D.Double {
public SpiralPath(int size) {
int numIterations = 5;
int arcGrowDelta = (size / numIterations) / 2;
int arcWidth = 0;
int centerX = size / 2;
int centerY = size / 2;
moveTo(centerX, centerY);
for (int i = 0; i < numIterations; i++) {
append(new Arc2D.Double(centerX - arcWidth, centerY - arcWidth, 2 * arcWidth, 2 * arcWidth, 180, 180, Arc2D.OPEN), true);
arcWidth += arcGrowDelta;
append(new Arc2D.Double(centerX - arcWidth, centerY - arcWidth, 2 * arcWidth - arcGrowDelta, 2 * arcWidth, 0, 180, Arc2D.OPEN), true);
}
}
}
Now we have that, the rest is (relatively) simple, as it follows a well know pattern...
package javaapplication1.pkg005;
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.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.AffineTransform;
import java.awt.geom.Arc2D;
import java.awt.geom.Path2D;
import java.awt.geom.PathIterator;
import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Test {
public static void main(String[] args) {
new Test();
}
public class SpiralPath extends Path2D.Double {
public SpiralPath(int size) {
int numIterations = 5;
int arcGrowDelta = (size / numIterations) / 2;
int arcWidth = 0;
int centerX = size / 2;
int centerY = size / 2;
moveTo(centerX, centerY);
for (int i = 0; i < numIterations; i++) {
append(new Arc2D.Double(centerX - arcWidth, centerY - arcWidth, 2 * arcWidth, 2 * arcWidth, 180, 180, Arc2D.OPEN), true);
arcWidth += arcGrowDelta;
append(new Arc2D.Double(centerX - arcWidth, centerY - arcWidth, 2 * arcWidth - arcGrowDelta, 2 * arcWidth, 0, 180, Arc2D.OPEN), true);
}
}
}
public Test() {
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 SpiralPath spiralPath;
private final Rectangle box;
private List<Point2D> points;
private double angle;
private Point2D pos;
private int index;
protected static final double PLAY_TIME = 5000; // 5 seconds...
private Long startTime;
public TestPane() {
spiralPath = new SpiralPath(150);
box = new Rectangle(0, 0, 10, 10);
points = new ArrayList<>(25);
PathIterator pi = spiralPath.getPathIterator(null, 0.01);
while (!pi.isDone()) {
double[] coords = new double[6];
switch (pi.currentSegment(coords)) {
case PathIterator.SEG_MOVETO:
case PathIterator.SEG_LINETO:
points.add(new Point2D.Double(coords[0], coords[1]));
break;
}
pi.next();
}
pos = points.get(0);
Timer timer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (startTime == null) {
startTime = System.currentTimeMillis();
}
long playTime = System.currentTimeMillis() - startTime;
double progress = playTime / PLAY_TIME;
if (progress >= 1.0) {
progress = 1d;
((Timer) e.getSource()).stop();
}
int index = Math.min(Math.max(0, (int) (points.size() * progress)), points.size() - 1);
pos = points.get(index);
if (index < points.size() - 1) {
angle = angleTo(pos, points.get(index + 1));
}
repaint();
}
});
timer.start();
}
protected double angleTo(Point2D from, Point2D to) {
double angle = Math.atan2(to.getY() - from.getY(), to.getX() - from.getX());
return angle;
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
applyQualityRenderingHints(g2d);
int x = (getWidth() - spiralPath.getBounds().width) / 2;
int y = (getHeight() - spiralPath.getBounds().height) / 2;
g2d.translate(x, y);
g2d.draw(spiralPath);
AffineTransform at = new AffineTransform();
if (pos != null) {
Rectangle bounds = box.getBounds();
at.rotate(angle, (bounds.width / 2), (bounds.width / 2));
Path2D player = new Path2D.Double(box, at);
g2d.translate(pos.getX() - (bounds.width / 2), pos.getY() - (bounds.height / 2));
g2d.setColor(Color.RED);
g2d.draw(player);
}
g2d.dispose();
}
}
public static void applyQualityRenderingHints(Graphics2D g2d) {
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);
}
}

Try a little program (with no timers no nothing just this plane):
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D)g;
g2d.setColor(Color.BLACK);
g2d.drawArc(200,200,200,200,0,90);
g2d.setColor(Color.magenta);
for(double t=0; t<Math.PI/2; t+=Math.PI/100) {
int x = 300 + (int)(100 * Math.cos(t));
int y = 300 + (int)(100 * Math.sin(t));
g.fillOval(x, y , 5 , 5);
}
}
If your arc is 200 wide and high and the arc goes from 0 to 90 (from the right x axis) this should draw the points on the arc.
I think you can generalize this to whatever center you have what width/height etc.
You can also change the angle
int y = 300 + (int)(100 * Math.sin(-t));
if you want to draw backwards.

Related

Java AWT Graphics - Interpolate Shapes with subpixel accuracy

I'm working on a project where I need an animation of balls moving on an ellipse (e.g. a circumference). At the moment, I'm drawing it all on a JPanel, by overriding the paintComponent() method, and the moving effect comes from repainting it at a fixed rate, and changing the position of the "balls".
It all works good enough, except that the balls seem to move in a "laddery" way, and not smoothly. Since it's a problem between the drawings, I suppose messing with RenderingHints will lead nowhere.
An SSCCE of the issue (From the test, it might not seem that noticeable, but when you have more than 100 balls moving, it looks really weird):
import javax.swing.*;
import java.awt.*;
public class SSCCE {
private static long dt;
private static JPanel animationPanel = createAnimationPanel();
private static JPanel createAnimationPanel() {
return new JPanel() {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
int w = this.getWidth();
int h = this.getHeight();
Point center = new Point(w / 2, h / 2);
// Drawing circumference.
int radius = 80;
int x = center.x - radius;
int y = center.y - radius;
g2d.drawOval(x, y, radius * 2, radius * 2);
// Drawing ball.
g2d.setColor(Color.RED);
int ballWidth = 20;
double rad = Math.toRadians((dt / ((radius * 2) / 8.0)));
int xPos = (int) (center.x - (Math.cos(rad) * radius) - (ballWidth / 2));
int yPos = (int) (center.y - (Math.sin(rad) * radius) - (ballWidth / 2));
g2d.fillOval(xPos, yPos, ballWidth, ballWidth);
}
};
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
JFrame frame = new JFrame("Draw Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().setLayout(new BorderLayout());
frame.getContentPane().add(animationPanel, BorderLayout.CENTER);
frame.setSize(400, 400);
frame.setLocationRelativeTo(null);
frame.setEnabled(true);
frame.setVisible(true);
frame.requestFocus();
});
Thread updateThread = new Thread(() -> {
long lastTime = System.currentTimeMillis();
while (true) { // I don't actually use "while (true)", but it doesn't matter since it's a test.
dt = System.currentTimeMillis() - lastTime;
animationPanel.repaint();
try {
Thread.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "Update Thread");
SwingUtilities.invokeLater(updateThread::start);
}
}
So in conclusion, I thought that maybe I could interpolate the positions into subpixel level, and in some way draw the ball "between the pixels", in a way similar to how an Anti-alias effect works... Is it possible? If not, is there any workaround?
You problem is related to the truncating of the double value to int
int xPos = (int) (center.x - (Math.cos(rad) * radius) - (ballWidth / 2));
int yPos = (int) (center.y - (Math.sin(rad) * radius) - (ballWidth / 2));
Instead, you should be making use the double values. In this case, you can simply translate the Graphics2D API, for example...
// Drawing ball.
g2d.setColor(Color.RED);
int ballWidth = 20;
double rad = Math.toRadians((dt / ((radius * 2) / 8.0)));
double xPos = (center.x - (Math.cos(rad) * radius) - (ballWidth / 2));
double yPos = (center.y - (Math.sin(rad) * radius) - (ballWidth / 2));
if (theActualDot == null) {
theActualDot = new Ellipse2D.Double(0, 0, ballWidth, ballWidth);
}
g2d.translate(xPos, yPos);
g2d.fill(theActualDot);
Runnable example
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Ellipse2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class Main {
private static long dt;
private static JPanel animationPanel = createAnimationPanel();
private static JPanel createAnimationPanel() {
return new JPanel() {
private Shape theActualDot;
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
int w = this.getWidth();
int h = this.getHeight();
Point center = new Point(w / 2, h / 2);
// Drawing circumference.
int radius = 80;
int x = center.x - radius;
int y = center.y - radius;
g2d.drawOval(x, y, radius * 2, radius * 2);
// Drawing ball.
g2d.setColor(Color.RED);
int ballWidth = 20;
double rad = Math.toRadians((dt / ((radius * 2) / 8.0)));
double xPos = (center.x - (Math.cos(rad) * radius) - (ballWidth / 2));
double yPos = (center.y - (Math.sin(rad) * radius) - (ballWidth / 2));
if (theActualDot == null) {
theActualDot = new Ellipse2D.Double(0, 0, ballWidth, ballWidth);
}
g2d.translate(xPos, yPos);
g2d.fill(theActualDot);
g2d.dispose();
}
};
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
JFrame frame = new JFrame("Draw Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().setLayout(new BorderLayout());
frame.getContentPane().add(animationPanel, BorderLayout.CENTER);
frame.setSize(400, 400);
frame.setLocationRelativeTo(null);
frame.setEnabled(true);
frame.setVisible(true);
frame.requestFocus();
Timer timer = new Timer(5, new ActionListener() {
long lastTime = System.currentTimeMillis();
#Override
public void actionPerformed(ActionEvent e) {
dt = System.currentTimeMillis() - lastTime;
animationPanel.repaint();
}
});
timer.start();
});
}
}

I have a frame with different shapes that I would like to move, but only one moves so far

My intensions is to be able to move three different shapes in my frame. But I can move only square so far. Any suggestions how I can make program move all my shapes, not just one?
I have some methods and classes below
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.awt.geom.Rectangle2D;
import java.awt.geom.Ellipse2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class MouseMoveScale extends JPanel {
private Rectangle2D.Float myRect = new Rectangle2D.Float(50, 50, 100, 100);
private Ellipse2D.Float myCr = new Ellipse2D.Float(10,10, 100, 100);
private Rectangle2D.Float myTr = new Rectangle2D.Float(50, 50, 50, 50);
MovingAdapter ma = new MovingAdapter();
public MouseMoveScale() {
addMouseMotionListener(ma);
addMouseListener(ma);
addMouseWheelListener(new ScaleHandler());
}
public void paint(Graphics g) {
super.paint(g);
Graphics2D square = (Graphics2D) g;
Graphics2D triangle = (Graphics2D) g;
Graphics2D circle = (Graphics2D) g;
square.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
square.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
square.setColor(new Color(0, 0, 200));
square.fill(myRect);
triangle.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
triangle.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
triangle.setColor(new Color(139, 89, 255));
int xPoints[] = {110,180,30,110};
int yPoints[] = {30,100,100,30};
g.fillPolygon(xPoints, yPoints, 4);
circle.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
circle.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
circle.setColor(new Color(0, 0, 117));
circle.fillOval( 10,10, 100, 100 );
}
class MovingAdapter extends MouseAdapter {
private int x;
private int y;
public void mousePressed(MouseEvent e) {
x = e.getX();
y = e.getY();
}
public void mouseDragged(MouseEvent e) {
int dx = e.getX() - x;
int dy = e.getY() - y;
if (myRect.getBounds2D().contains(x, y)) {
myRect.x += dx;
myRect.y += dy;
repaint();
}
if (myTr.getBounds2D().contains(x, y)) {
myTr.x += dx;
myTr.y += dy;
repaint();
}
if (myCr.getBounds2D().contains(x, y)) {
myCr.x += dx;
myCr.y += dy;
repaint();
}
x += dx;
y += dy;
}
}
class ScaleHandler implements MouseWheelListener {
public void mouseWheelMoved(MouseWheelEvent e) {
int x = e.getX();
int y = e.getY();
if (e.getScrollType() == MouseWheelEvent.WHEEL_UNIT_SCROLL) {
if (myRect.getBounds2D().contains(x, y)) {
float amount = e.getWheelRotation() * 5f;
myRect.width += amount;
myRect.height += amount;
repaint();
}
}
}
}
public static void main(String[] args) {
JFrame frame = new JFrame("Shapes World");
MouseMoveScale m = new MouseMoveScale();
m.setDoubleBuffered(true);
frame.add(m);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(300, 300);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
I probably need to create new objects?
Or is it enough to fix the problem with if statement?
I think this is your error
triangle.setColor(new Color(139, 89, 255));
int xPoints[] = {110,180,30,110};
int yPoints[] = {30,100,100,30};
g.fillPolygon(xPoints, yPoints, 4); ---> Notice this.
You are trying to move the rec without changing the xpoints/ypoints you have used.
if (myTr.getBounds2D().contains(x, y)) {
myTr.x += dx; ---> not related to xPoints
myTr.y += dy; ---> not related to yPoints
repaint();
}
You can change - g.fillPolygon for e.x with -
triangle.fill(myCr);
Our result
So now you can move 2 objects.

How to draw snakes in Swing?

I'm rather new to Java Swing, and working on a ladders and snaked project for my college course. The instructor has told us to implement a game in which the player can choose exactly how many snakes are on the game board, and where the snakes are. So is for the ladders! So I cannot use one, or several fixed images in my game, so that the player cannot change them anymore.
I need a way to draw such snakes and ladders in my game. The question is what is the best option to do this in Java? By which means can I draw user-desired snakes on my game board?
One thing you could do, is rotate the image by a given angle, this way, you could still use images and supply the ability to change their start and end points
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.image.BufferedImage;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
try {
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
} catch (IOException exp) {
exp.printStackTrace();
}
}
});
}
public class TestPane extends JPanel {
private BufferedImage ladder;
private double angle;
public TestPane() throws IOException {
ladder = ImageIO.read(getClass().getResource("Ladder.png"));
JSlider slider = new JSlider(0, 100, 0);
slider.addChangeListener(new ChangeListener() {
#Override
public void stateChanged(ChangeEvent e) {
angle = (360d * (slider.getValue() / 100d));
repaint();
}
});
setLayout(new BorderLayout());
add(slider, BorderLayout.SOUTH);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
applyQualityRenderingHints(g2d);
int x = getWidth() / 2;
int y = getHeight() / 2;
g2d.setColor(Color.RED);
g2d.rotate(Math.toRadians(angle), x, y);
g2d.drawImage(ladder, x - (ladder.getWidth() / 2), y - ladder.getHeight(), this);
g2d.fillOval(x - 3, y - 3, 6, 6);
g2d.dispose();
}
protected void applyQualityRenderingHints(Graphics2D g2d) {
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);
}
}
}
Now, because you're rotating the actual Graphics context, this can become very complicated very quickly, especially when you're trying to change the location and rotate a number of objects
Another option might be to rotate the image as a whole, for example...
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.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
try {
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
} catch (IOException exp) {
exp.printStackTrace();
}
}
});
}
public class TestPane extends JPanel {
private BufferedImage ladder;
private double angle;
public TestPane() throws IOException {
ladder = ImageIO.read(getClass().getResource("Ladder.png"));
JSlider slider = new JSlider(0, 100, 0);
slider.addChangeListener(new ChangeListener() {
#Override
public void stateChanged(ChangeEvent e) {
angle = (360d * (slider.getValue() / 100d));
repaint();
}
});
setLayout(new BorderLayout());
add(slider, BorderLayout.SOUTH);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
applyQualityRenderingHints(g2d);
int x = getWidth() / 2;
int y = getHeight() / 2;
g2d.setColor(Color.RED);
BufferedImage rotated = rotate(ladder, angle);
g2d.drawImage(rotated, x - (rotated.getWidth() / 2), y - (rotated.getHeight() / 2), this);
g2d.dispose();
}
public BufferedImage rotate(BufferedImage image, double byAngle) {
double rads = Math.toRadians(byAngle);
double sin = Math.abs(Math.sin(rads)), cos = Math.abs(Math.cos(rads));
int w = image.getWidth();
int h = image.getHeight();
int newWidth = (int) Math.floor(w * cos + h * sin);
int newHeight = (int) Math.floor(h * cos + w * sin);
BufferedImage rotated = new BufferedImage(newWidth, newHeight, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2d = rotated.createGraphics();
applyQualityRenderingHints(g2d);
AffineTransform at = new AffineTransform();
at.translate((newWidth - w) / 2, (newHeight - h) / 2);
int x = w / 2;
int y = h / 2;
at.rotate(Math.toRadians(byAngle), x, y);
g2d.setTransform(at);
g2d.drawImage(image, 0, 0, this);
g2d.dispose();
return rotated;
}
protected void applyQualityRenderingHints(Graphics2D g2d) {
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);
}
}
}
Either way, you've got some serious management and maths ahead of you.
You might like to take a closer look at Painting in AWT and Swing and Performing Custom Painting and 2D Graphics for more details
There are many (many, many) degrees of freedom for answering this question. In fact, one could consider it as "too broad", as it is not much more specific than "how can I paint something in Swing?" (with "something" being a snake or a ladder here). There is a reason of why a significant part of game development is not only plain programming, but also the graphics design etc.
It is not clear how much the task of your course is focussed on exactly this point. If it is a general computer science course, then there likely is no need to spend dozens of hours for making the "prettiest" game. Instead, it could be sufficient to draw plain lines between the fields that the snakes/ladders should connect. Green lines for snakes, brown lines for ladders. However, maybe the priorities are different.
Regarding this question in particular, there are, broadly speaking, two options:
Paint the snakes as images
Paint the snakes as graphical objects
MadProgrammer showed in his answer the approach of using images. They can be rotated and drawn and scaled arbitrarily. In fact, when you have an image, say of size 100x1000, then you could make it span two arbitrary points. So if you have the points (200,400) and (700,1100) on the screen, then you can compute an orientation and scaling for the image so that the top center point of your image is located at (200,400), and the bottom center point is at (700,1100) - which is likely a requirement that could appear when you want to "draw a ladder starting at one field and ending at another".
The issue that I saw regarding the snakes was that the "contents" of the image would have to depend on the start- and end point. Namely, a snake that is painted between two fields that are close to each other might have a completely different shape than one that is painted between two distant fields.
(Similarly, a ladder: The number of steps that the ladder should have would certainly depend on the distance between the fields that it connects).
So, I did some "recreational programming" here, and created a snake painting class. The difference, compared to images, is that the snakes are graphical objects - particularly, they are composed of Shape objects. The tricky part is the body of the snake: It should have some waves, and a certain thickness, and the thickness should largely be constant along the body, except for the tail part....
Again: There are many degrees of freedom, and of course, this is just a snippet, quickly written down, to see (mainly for myself) of how one could tackle this problem of "drawing a snake body".
The result is a snake where you can drag around the head and tail between arbitrary points:
Some of the degrees of freedom that I mentioned are summarized as (compile-time) variables in the Snake class. One could, for example, adjust the number of "waves" based on the distance between the head and the tail point:
But these are things that I'll leave to the real artists ;-)
The code is a bit crude and largely uncommented, but maybe someone finds it helpful nevertheless:
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.AffineTransform;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Path2D;
import java.awt.geom.Point2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class SnakeDrawing
{
public static void main(String[] args)
{
SwingUtilities.invokeLater(() -> createAndShowGUI());
}
private static void createAndShowGUI()
{
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.getContentPane().add(new SnakeDrawingPanel());
f.setSize(800, 800);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
}
class Snake
{
private Point2D point0 = new Point2D.Double(100,500);
private Point2D point1 = new Point2D.Double(700,500);
double bodyWidth = 10;
int waves = 4;
double waveHeight = 0.05;
double tailStart = 0.8;
double headLength = 20;
double headWidth = 16;
double eyeRadius = 6;
double irisRadius = 3;
private Shape body;
private Shape head;
private Shape eyeR;
private Shape eyeL;
private Shape irisR;
private Shape irisL;
void setPoints(Point2D point0, Point2D point1)
{
this.point0.setLocation(point0);
this.point1.setLocation(point1);
AffineTransform at = AffineTransform.getRotateInstance(
currentAngleRad(), point0.getX(), point0.getY());
at.translate(point0.getX(), point0.getY());
createBody(at);
createHead(at);
}
void draw(Graphics2D g)
{
g.setColor(new Color(0,128,0));
g.fill(body);
g.fill(head);
g.setColor(Color.WHITE);
g.fill(eyeR);
g.fill(eyeL);
g.setColor(Color.BLACK);
g.fill(irisR);
g.fill(irisL);
}
private void createBody(AffineTransform at)
{
double distance = point1.distance(point0);
int steps = 100;
Path2D body = new Path2D.Double();
Point2D previousPoint = null;
for (int i=0; i<steps; i++)
{
double alpha = (double)i/(steps-1);
Point2D point = computeCenterPoint(alpha, distance);
if (previousPoint != null)
{
Point2D bodyPoint =
computeBodyPoint(alpha, point, previousPoint);
if (i==1)
{
body.moveTo(bodyPoint.getX(), bodyPoint.getY());
}
else
{
body.lineTo(bodyPoint.getX(), bodyPoint.getY());
}
}
previousPoint = point;
}
previousPoint = null;
for (int i=steps-1; i>=0; i--)
{
double alpha = (double)i/(steps-1);
Point2D point = computeCenterPoint(alpha, distance);
if (previousPoint != null)
{
Point2D bodyPoint =
computeBodyPoint(alpha, point, previousPoint);
body.lineTo(bodyPoint.getX(), bodyPoint.getY());
}
previousPoint = point;
}
this.body = at.createTransformedShape(body);
}
private Point2D computeBodyPoint(
double alpha, Point2D point, Point2D previousPoint)
{
double dx = point.getX() - previousPoint.getX();
double dy = point.getY() - previousPoint.getY();
double rdx = -dy;
double rdy = dx;
double d = Math.hypot(dx, dy);
double localBodyWidth = bodyWidth;
if (alpha > tailStart)
{
localBodyWidth *= (1 - (alpha - tailStart) / (1.0 - tailStart));
}
double px = point.getX() + rdx * (1.0 / d) * localBodyWidth;
double py = point.getY() + rdy * (1.0 / d) * localBodyWidth;
return new Point2D.Double(px, py);
}
private Point2D computeCenterPoint(
double alpha, double distance)
{
double r = alpha * Math.PI * 2 * waves;
double verticalScaling = 1 - (alpha * 2 - 1) * (alpha * 2 - 1);
double y = Math.sin(r) * distance * waveHeight * verticalScaling;
double x = alpha * distance;
return new Point2D.Double(x,y);
}
private void createHead(AffineTransform at)
{
Shape head = new Ellipse2D.Double(
-headLength, -headWidth,
headLength + headLength,
headWidth + headWidth);
this.head = at.createTransformedShape(head);
Shape eyeR = new Ellipse2D.Double(
-headLength * 0.5 - eyeRadius,
-headWidth * 0.6 - eyeRadius,
eyeRadius + eyeRadius,
eyeRadius + eyeRadius);
Shape eyeL = new Ellipse2D.Double(
-headLength * 0.5 - eyeRadius,
headWidth * 0.6 - eyeRadius,
eyeRadius + eyeRadius,
eyeRadius + eyeRadius);
this.eyeR = at.createTransformedShape(eyeR);
this.eyeL = at.createTransformedShape(eyeL);
Shape irisR = new Ellipse2D.Double(
-headLength * 0.4 - eyeRadius,
-headWidth * 0.6 - irisRadius,
irisRadius + irisRadius,
irisRadius + irisRadius);
Shape irisL = new Ellipse2D.Double(
-headLength * 0.4 - eyeRadius,
headWidth * 0.6 - irisRadius,
irisRadius + irisRadius,
irisRadius + irisRadius);
this.irisR = at.createTransformedShape(irisR);
this.irisL = at.createTransformedShape(irisL);
}
private double currentAngleRad()
{
double dx = point1.getX() - point0.getX();
double dy = point1.getY() - point0.getY();
double angleRad = Math.atan2(dy, dx);
return angleRad;
}
}
class SnakeDrawingPanel extends JPanel
implements MouseListener, MouseMotionListener
{
private Point2D point0 = new Point2D.Double(100,500);
private Point2D point1 = new Point2D.Double(700,500);
private Point2D draggedPoint = null;
private Snake snake = new Snake();
SnakeDrawingPanel()
{
addMouseListener(this);
addMouseMotionListener(this);
}
#Override
protected void paintComponent(Graphics gr)
{
super.paintComponent(gr);
Graphics2D g = (Graphics2D)gr;
g.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g.setColor(Color.WHITE);
g.fillRect(0, 0, getWidth(), getHeight());
snake.setPoints(point0, point1);
snake.draw(g);
}
#Override
public void mouseDragged(MouseEvent e)
{
if (draggedPoint != null)
{
draggedPoint.setLocation(e.getPoint());
repaint();
}
}
#Override
public void mouseMoved(MouseEvent e)
{
// Nothing to do here
}
#Override
public void mouseClicked(MouseEvent e)
{
// Nothing to do here
}
#Override
public void mousePressed(MouseEvent e)
{
draggedPoint = null;
double thresholdSquared = 10*10;
if (e.getPoint().distanceSq(point0) < thresholdSquared)
{
draggedPoint = point0;
}
if (e.getPoint().distanceSq(point1) < thresholdSquared)
{
draggedPoint = point1;
}
}
#Override
public void mouseReleased(MouseEvent e)
{
draggedPoint = null;
}
#Override
public void mouseEntered(MouseEvent e)
{
// Nothing to do here
}
#Override
public void mouseExited(MouseEvent e)
{
// Nothing to do here
}
}
EDIT:
As an example / extension of the answer by MadProgrammer, here is a program that contains a method that allows you to draw an image between two given points. So, for a given ladder image, you can basically drag around the top- and bottom center point of the image:
Coincidentally, the relevant method is called drawImageBetweenPoints:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.geom.AffineTransform;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Point2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class LadderDrawing
{
public static void main(String[] args)
{
SwingUtilities.invokeLater(() -> createAndShowGUI());
}
private static void createAndShowGUI()
{
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.getContentPane().add(new LadderDrawingPanel());
f.setSize(800, 800);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
}
class LadderDrawingPanel extends JPanel
implements MouseListener, MouseMotionListener
{
private Point2D point0 = new Point2D.Double(300,300);
private Point2D point1 = new Point2D.Double(500,700);
private Point2D draggedPoint = null;
private BufferedImage ladderImage;
LadderDrawingPanel()
{
addMouseListener(this);
addMouseMotionListener(this);
try
{
ladderImage = ImageIO.read(new File("ladder.png"));
}
catch (IOException e)
{
e.printStackTrace();
}
}
#Override
protected void paintComponent(Graphics gr)
{
super.paintComponent(gr);
Graphics2D g = (Graphics2D)gr;
g.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g.setColor(Color.WHITE);
g.fillRect(0, 0, getWidth(), getHeight());
g.setColor(Color.RED);
paintDot(g, point0, 8);
paintDot(g, point1, 8);
drawImageBetweenPoints(g, ladderImage, point0, point1);
}
private static void paintDot(Graphics2D g, Point2D p, double radius)
{
g.fill(new Ellipse2D.Double(
p.getX() - radius, p.getY() - radius,
radius + radius, radius + radius));
}
private static void drawImageBetweenPoints(
Graphics2D g, BufferedImage image, Point2D p0, Point2D p1)
{
AffineTransform at = new AffineTransform();
at.concatenate(AffineTransform.getTranslateInstance(
p0.getX(), p0.getY()));
double dx = p1.getX() - p0.getX();
double dy = p1.getY() - p0.getY();
double angleRad = Math.atan2(dy, dx) - Math.PI * 0.5;
at.concatenate(AffineTransform.getRotateInstance(angleRad));
double distance = p1.distance(p0);
double scalingY = distance / image.getHeight();
// Default: Uniform scaling
double scalingX = scalingY;
// For keeping the width of the image
//scalingX = 1.0;
// For scaling to a fixed width:
//double desiredWidth = 50;
//scalingX = desiredWidth / image.getWidth();
at.concatenate(AffineTransform.getScaleInstance(scalingX, scalingY));
at.concatenate(AffineTransform.getTranslateInstance(
-image.getWidth() * 0.5, 0));
AffineTransform oldAT = g.getTransform();
g.transform(at);
g.drawImage(image, 0, 0, null);
g.setTransform(oldAT);
}
#Override
public void mouseDragged(MouseEvent e)
{
if (draggedPoint != null)
{
draggedPoint.setLocation(e.getPoint());
repaint();
}
}
#Override
public void mouseMoved(MouseEvent e)
{
// Nothing to do here
}
#Override
public void mouseClicked(MouseEvent e)
{
// Nothing to do here
}
#Override
public void mousePressed(MouseEvent e)
{
draggedPoint = null;
double thresholdSquared = 10*10;
if (e.getPoint().distanceSq(point0) < thresholdSquared)
{
draggedPoint = point0;
}
if (e.getPoint().distanceSq(point1) < thresholdSquared)
{
draggedPoint = point1;
}
}
#Override
public void mouseReleased(MouseEvent e)
{
draggedPoint = null;
}
#Override
public void mouseEntered(MouseEvent e)
{
// Nothing to do here
}
#Override
public void mouseExited(MouseEvent e)
{
// Nothing to do here
}
}
Again, I think that a manual drawing may be more flexible (and, particularly for the ladder, not much more difficult), because you can select the number of steps of the ladder to dynamically adjust based on the distance of the points. For example:
It boils down to a bit of math for computing the positions of the bars and steps, and playing a bit with strokes and shapes:
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.Stroke;
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.Point2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class LadderDrawingManual
{
public static void main(String[] args)
{
SwingUtilities.invokeLater(() -> createAndShowGUI());
}
private static void createAndShowGUI()
{
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.getContentPane().add(new LadderDrawingManualPanel());
f.setSize(800, 800);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
}
class LadderDrawingManualPanel extends JPanel
implements MouseListener, MouseMotionListener
{
private Point2D point0 = new Point2D.Double(300,300);
private Point2D point1 = new Point2D.Double(500,700);
private Point2D draggedPoint = null;
LadderDrawingManualPanel()
{
addMouseListener(this);
addMouseMotionListener(this);
}
#Override
protected void paintComponent(Graphics gr)
{
super.paintComponent(gr);
Graphics2D g = (Graphics2D)gr;
g.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g.setColor(Color.WHITE);
g.fillRect(0, 0, getWidth(), getHeight());
g.setColor(Color.RED);
paintDot(g, point0, 8);
paintDot(g, point1, 8);
drawLadderBetweenPoints(g, point0, point1);
}
private static void paintDot(Graphics2D g, Point2D p, double radius)
{
g.fill(new Ellipse2D.Double(
p.getX() - radius, p.getY() - radius,
radius + radius, radius + radius));
}
private static void drawLadderBetweenPoints(
Graphics2D g, Point2D p0, Point2D p1)
{
final double ladderWidth = 40;
final double distanceBetweenSteps = 30;
final double barWidth = 5;
double dx = p1.getX() - p0.getX();
double dy = p1.getY() - p0.getY();
double distance = p1.distance(p0);
double dirX = dx / distance;
double dirY = dy / distance;
double offsetX = dirY * ladderWidth * 0.5;
double offsetY = -dirX * ladderWidth * 0.5;
Line2D lineR = new Line2D.Double(
p0.getX() + offsetX,
p0.getY() + offsetY,
p1.getX() + offsetX,
p1.getY() + offsetY);
Line2D lineL = new Line2D.Double(
p0.getX() - offsetX,
p0.getY() - offsetY,
p1.getX() - offsetX,
p1.getY() - offsetY);
drawBar(g, lineL, barWidth);
drawBar(g, lineR, barWidth);
int numSteps = (int)(distance / distanceBetweenSteps);
for (int i=0; i<numSteps; i++)
{
double stepOffsetX = (i+1) * distanceBetweenSteps;
double stepOffsetY = (i+1) * distanceBetweenSteps;
Line2D step = new Line2D.Double(
p0.getX() + stepOffsetX * dirX - offsetX,
p0.getY() + stepOffsetY * dirY - offsetY,
p0.getX() + stepOffsetX * dirX + offsetX,
p0.getY() + stepOffsetY * dirY + offsetY);
drawBar(g, step, barWidth);
}
}
private static void drawBar(Graphics2D g, Line2D line, double barWidth)
{
Stroke stroke = new BasicStroke(
(float)barWidth, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
Shape bar = stroke.createStrokedShape(line);
g.setColor(new Color(200,100,0));
g.fill(bar);
g.setColor(Color.BLACK);
g.draw(bar);
}
#Override
public void mouseDragged(MouseEvent e)
{
if (draggedPoint != null)
{
draggedPoint.setLocation(e.getPoint());
repaint();
}
}
#Override
public void mouseMoved(MouseEvent e)
{
// Nothing to do here
}
#Override
public void mouseClicked(MouseEvent e)
{
// Nothing to do here
}
#Override
public void mousePressed(MouseEvent e)
{
draggedPoint = null;
double thresholdSquared = 10*10;
if (e.getPoint().distanceSq(point0) < thresholdSquared)
{
draggedPoint = point0;
}
if (e.getPoint().distanceSq(point1) < thresholdSquared)
{
draggedPoint = point1;
}
}
#Override
public void mouseReleased(MouseEvent e)
{
draggedPoint = null;
}
#Override
public void mouseEntered(MouseEvent e)
{
// Nothing to do here
}
#Override
public void mouseExited(MouseEvent e)
{
// Nothing to do here
}
}

Rotating a polygon in Java

The program I am writing draws multiple stars on the screen and gives them random directions and speeds. The stars will bounce off the edges of the panel and stay inside. I need to have the stars rotating as they are moving. I have tried so many things and I cannot figure it out. Below is the code I use to draw the stars and move them.
Additional Information:
-the stars are in a collection called "stars"
-there are two classes that I wrote, "Star" and "MyJPanel"
Set the points: (in Star class)
for (double current = 0; current < nPoints; current += 1)
{
i = (int)current;
int[] X = new int[nPoints * 2];
int[] Y = new int[nPoints * 2];
cosX = -(Math.cos(current*((2*Math.PI)/(nPoints)))*radius[i % 2]);
sinY = -(Math.sin(current*((2*Math.PI)/(nPoints)))*radius[i % 2]);
X[i] = (int) cosX+x;
Y[i] = (int) sinY+y;
addPoint(X[i], Y[i]);
}
Move method: (in Star class)
public void move(int width, int height)
{
if (location.x <= radius[0] || location.x >= width - radius[0])
{
xIncr = -xIncr;
}
if (location.y <= radius[0] || location.y >= height - radius[0])
{
yIncr = -yIncr;
}
translate(xIncr, yIncr);
location.setLocation(location.x + xIncr, location.y + yIncr);
xInc = xIncr;
}
Paint the stars: (in MyJPanel class)
public void paintComponent(Graphics g)
{
super.paintComponent(g);
for(int i = 0; i < stars.size(); i++)
{
g2d = (Graphics2D) g;
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, stars.get(i).getAlpha()));
g2d.setColor(stars.get(i).getColor());
g2d.fillPolygon(stars.get(i));
if(stars.get(i).alpha == 0)
{
stars.remove(stars.indexOf(stars.get(i)));
}
}
}
If any more code or information is needed please let me know, thank you!
I would have a look at Working with Geometry and Transforming Shapes, Text, and Images
Basically, I've created a custom Path2D shape which represents my start. This class carries some additional information with, included it's x/y offset and rotation. It then provides a helper method to create a transformed instance of this shape based on these properties, for example...
public Shape getTransformedInstance() {
AffineTransform at = new AffineTransform();
Rectangle bounds = getBounds();
at.rotate(Math.toRadians(angle), x + (bounds.width / 2), y + (bounds.height / 2));
at.translate(x, y);
return createTransformedShape(at);
}
Basically, this is where the magic happens.
There is a simple Swing Timer which applies a delta to the position and rotation.
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.RenderingHints;
import java.awt.Shape;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.AffineTransform;
import java.awt.geom.Path2D;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class RotateShape {
public static void main(String[] args) {
new RotateShape();
}
public RotateShape() {
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 Star star;
private double rotationDelta = 5d;
private int xDelta = 0;
private int yDelta = 0;
public TestPane() {
star = new Star(50, 50);
star.moveLocatioBy(75, 75);
Random rnd = new Random();
xDelta = rnd.nextInt(4) + 1;
yDelta = rnd.nextInt(4) + 1;
Timer timer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
star.moveLocatioBy(xDelta, yDelta);
Rectangle bounds = star.getBounds();
int x = star.getX();
int y = star.getY();
boolean bounced = false;
if (x < 0) {
x = 0;
xDelta *= -1;
bounced = true;
} else if (x + bounds.width > getWidth()) {
x = getWidth() - bounds.width;
xDelta *= -1;
bounced = true;
}
if (y < 0) {
y = 0;
yDelta *= -1;
bounced = true;
} else if (y + bounds.height > getHeight()) {
y = getHeight() - bounds.height;
yDelta *= -1;
bounced = true;
}
if (bounced) {
rotationDelta *= -1;
}
star.rotateByDegrees(rotationDelta);
star.setLocation(x, y);
repaint();
}
});
timer.start();
}
#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);
Shape shape = star.getTransformedInstance();
g2d.setColor(Color.BLUE);
g2d.fill(shape);
g2d.setColor(Color.RED);
g2d.draw(shape);
g2d.dispose();
}
}
public class Star extends Path2D.Double {
private double angle = 0;
private int x = 0, y = 0;
public Star(int width, int height) {
double heightPart = height / 3d;
double widthPart = width / 3d;
moveTo(width / 2, 0);
lineTo(widthPart * 2, heightPart);
lineTo(width, heightPart);
lineTo(widthPart * 2, height / 2);
lineTo(width, height);
lineTo(width / 2, heightPart * 2);
lineTo(0, height);
lineTo(widthPart, height / 2);
lineTo(0, heightPart);
lineTo(widthPart, heightPart);
closePath();
}
public double getAngle() {
return angle;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public void moveLocatioBy(int xDelta, int yDelta) {
this.x += xDelta;
this.y += yDelta;
}
public void rotateByDegrees(double delta) {
angle += delta;
}
public void setLocation(int x, int y) {
this.x = x;
this.y = y;
}
public Shape getTransformedInstance() {
AffineTransform at = new AffineTransform();
Rectangle bounds = getBounds();
at.rotate(Math.toRadians(angle), x + (bounds.width / 2), y + (bounds.height / 2));
at.translate(x, y);
return createTransformedShape(at);
}
}
}
It wouldn't take a lot to expand this idea so that each start had it's own delta values and simple update method (which passed in the width/height of the Container), so you could make lots-o-stars

Drawing a diamond with Polygon on the end of a Line2D

I'm trying to draw a diamond (for a composition UML relationship). Right now, I am making a triangle like this:
but I want to make something like this:
And I do the triangle with this code:
private void drawArrowHead(Graphics2D g2, Point tip, Point tail,
Color color) {
g2.setPaint(color);
double dy = tip.y - tail.y;
double dx = tip.x - tail.x;
double theta = Math.atan2(dy, dx);
double x, y, rho = theta + phi;
Point p1 = new Point();
Point p2 = new Point();
p1.setLocation(tip.x - barb * Math.cos(rho), tip.y - barb * Math.sin(rho));
rho = theta - phi;
p2.setLocation(tip.x - barb * Math.cos(rho), tip.y - barb * Math.sin(rho));
int[] xPoints = new int[5];
int[] yPoints = new int[5];
xPoints[0] = tip.x;
xPoints[1] = p1.x;
xPoints[2] = p2.x;
yPoints[0] = tip.y;
yPoints[1] = p1.y;
yPoints[2] = p2.y;
g2.setPaint(Color.BLACK);
Shape shape = new Polygon(xPoints, yPoints, 3);
g2.fill(shape);
//tip.x - barb * Math.cos(rho);
//y = tip.y - barb * Math.sin(rho);
}
Does anyone have an idea on how to make a diamond out of this? Thanks :)
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Panel;
import java.awt.Point;
import java.awt.Polygon;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class DrawArrows {
public static void main(String[] args) {
new DrawArrows();
}
public DrawArrows() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager
.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException
| IllegalAccessException
| UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new UMLWindow();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setBounds(30, 30, 1000, 700);
frame.getContentPane().setBackground(Color.white);
frame.setVisible(true);
frame.setLocationRelativeTo(null);
}
});
}
public static class UMLWindow extends JFrame {
Shapes shapeList = new Shapes();
Panel panel;
private static final long serialVersionUID = 1L;
public UMLWindow() {
addMenus();
panel = new Panel();
}
public void addMenus() {
getContentPane().add(shapeList);
setSize(300, 200);
setLocationRelativeTo(null);
setDefaultCloseOperation(EXIT_ON_CLOSE);
JMenuItem lineMenuItem = new JMenuItem("New Line");
lineMenuItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
System.out.println("adding line");
shapeList.addLine();
}
});
JMenuBar menubar = new JMenuBar();
menubar.add(lineMenuItem);
setJMenuBar(menubar);
}
}
public static class Shapes extends JPanel {
private static final long serialVersionUID = 1L;
private List<Line2D.Double> lines = new ArrayList<Line2D.Double>();
private Boolean drawing = false;
private Point lineStartingPoint = new Point();
private Point lineEndingPoint = new Point();
private Line2D.Double linePath;
double phi = Math.toRadians(40);
int barb = 20;
public Shapes() {
MyMouseAdapter myMouseAdapter = new MyMouseAdapter();
addMouseListener(myMouseAdapter);
addMouseMotionListener(myMouseAdapter);
this.setOpaque(true);
this.setBackground(Color.WHITE); // set canvas color
}
public void addLine() {
drawing = true;
repaint();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setStroke(new BasicStroke(2));
if (drawing) {
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2.setStroke(new BasicStroke(2));
g2.drawLine(lineStartingPoint.x, lineStartingPoint.y,
lineEndingPoint.x, lineEndingPoint.y);
drawArrowHead(g2, lineEndingPoint, lineStartingPoint,
Color.BLACK);
}
for (Line2D line : lines) {
g2.setColor(Color.BLACK);
Point sw = new Point((int) line.getX1(), (int) line.getY1());
Point ne = new Point((int) line.getX2(), (int) line.getY2());
g2.draw(line);
drawArrowHead(g2, ne, sw, Color.BLACK);
}
}
public Rectangle2D drawRect(int x, int y) {
return new Rectangle2D.Double(x - 4, y - 4, 8, 8);
}
private void drawArrowHead(Graphics2D g2, Point tip, Point tail,
Color color) {
g2.setPaint(color);
double dy = tip.y - tail.y;
double dx = tip.x - tail.x;
double theta = Math.atan2(dy, dx);
double x, y, rho = theta + phi;
Point p1 = new Point();
Point p2 = new Point();
p1.setLocation(tip.x - barb * Math.cos(rho), tip.y - barb * Math.sin(rho));
rho = theta - phi;
p2.setLocation(tip.x - barb * Math.cos(rho), tip.y - barb * Math.sin(rho));
int[] xPoints = new int[5];
int[] yPoints = new int[5];
xPoints[0] = tip.x;
xPoints[1] = p1.x;
xPoints[2] = p2.x;
yPoints[0] = tip.y;
yPoints[1] = p1.y;
yPoints[2] = p2.y;
g2.setPaint(Color.BLACK);
Shape shape = new Polygon(xPoints, yPoints, 3);
g2.fill(shape);
//tip.x - barb * Math.cos(rho);
//y = tip.y - barb * Math.sin(rho);
}
class MyMouseAdapter extends MouseAdapter {
int currentIndex;
Point2D.Double startPoint = new Point2D.Double();
Point2D.Double endPoint = new Point2D.Double();
Boolean resizing = false;
#Override
public void mousePressed(MouseEvent e) {
if (drawing) {
lineStartingPoint = e.getPoint();
lineEndingPoint = lineStartingPoint;
}
}
#Override
public void mouseDragged(MouseEvent e) {
if (drawing) {
lineEndingPoint = e.getPoint();
repaint();
System.out.println(lines.size());
}
}
#Override
public void mouseReleased(MouseEvent e) {
if (drawing) {
drawLine(e);
}
drawing = false;
}
#Override
public void mouseMoved(MouseEvent e) {
}
public void drawLine(MouseEvent e) {
drawing = false;
lineEndingPoint = e.getPoint();
linePath = new Line2D.Double(lineStartingPoint.getX(),
lineStartingPoint.getY(), lineEndingPoint.getX(),
lineEndingPoint.getY());
lines.add(linePath);
repaint();
}
}
}
}
Personally, I would focus on generating the shape and the using the Graphics2D API to perform the translation and transformation...
The Shape API makes it really easy to generate complex shapes, for example, this is the diamond shape used in the following example...
public class DiamondShape extends Path2D.Double {
public DiamondShape(int width, int height) {
moveTo(width / 2, 0);
lineTo(width, height / 2);
lineTo(width / 2, height);
lineTo(0, height / 2);
closePath();
}
}
It's short, simple and (for the most part), easy to understand...
"But it needs to rotate" I hear you say, but of course, the Graphics API provides awesome and simple functionality to achieve that, let it do the heavy lifting. However, if you need to only transform the shape, the Shape API can do that to (transform the shape itself)
import java.awt.BasicStroke;
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.RenderingHints;
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.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class FollowMe {
public static void main(String[] args) {
new FollowMe();
}
public FollowMe() {
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 Point mousePoint;
private DiamondShape head;
public TestPane() {
head = new DiamondShape(10, 20);
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();
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);
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;
}
g2d.rotate(Math.toRadians(rotation), width / 2, height / 2);
int x = width / 2;
int y = height / 2;
g2d.setStroke(new BasicStroke(3));
g2d.setColor(Color.RED);
g2d.drawLine(x, y, x, y - height / 4);
y -= height / 4 + (head.getBounds().height);
x -= head.getBounds().width / 2;
g2d.fill(head.createTransformedShape(AffineTransform.getTranslateInstance(x, y)));
g2d.dispose();
g2d.dispose();
}
}
public class DiamondShape extends Path2D.Double {
public DiamondShape(int width, int height) {
moveTo(width / 2, 0);
lineTo(width, height / 2);
lineTo(width / 2, height);
lineTo(0, height / 2);
closePath();
}
}
}

Categories