How can I draw in java figure like this?
Here is my code which has to draw at least half of this figure
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JComponent;
import javax.swing.JFrame;
public class Main {
public static void main(String[] a) {
JFrame window = new JFrame();
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setBounds(30, 30, 300, 300);
window.getContentPane().add(new MyCanvas());
window.setVisible(true);
}
}
class MyCanvas extends JComponent {
private static final long serialVersionUID = 1L;
public void paint(Graphics g) {
int i =0;
for ( i = 0; i < 100; i++) {
int x=1+i*3;
g.drawLine(x, 200, 2+(x+(i/2)), 400-((i*i)/20));
}
}
}
And I get this one.
A little animation to show you the logic you need to be looking for in terms of line rotation. Think of the line like a hand on a clock. How would to animate a hand on a clock. It's pretty much the exact same concept. The only difference is that the x1 (the x point for the center point of the clock hand), instead of remaining still, it moves along the x axis (which is the y1 constant) while the hand is turning. So for every tick of the clock (hand rotation), the x location is also moved horizontally. That's the way I looked at it.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public class Main {
public static void main(String[] a) {
JFrame window = new JFrame();
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setResizable(false);
window.getContentPane().add(new MyCanvas());
window.pack();
window.setVisible(true);
}
}
class MyCanvas extends JPanel {
int x1 = 0;
int rotate = 50;
List<Line> lines;
Timer timer = null;
public MyCanvas() {
lines = new ArrayList<>();
timer = new Timer(75, new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (rotate < -50) {
((Timer) e.getSource()).stop();
} else {
lines.add(new Line(x1, rotate));
repaint();
x1 += 5;
rotate--;
}
}
});
JButton start = new JButton("Start the Magic");
start.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
timer.start();
}
});
add(start);
}
public Dimension getPreferredSize() {
return new Dimension(502, 400);
}
private static final long serialVersionUID = 1L;
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.BLACK);
g.fillRect(0, 0, getWidth(), getHeight());
for (Line line : lines) {
line.drawLine(g);
}
}
class Line {
int x1;
int rotate;
int y1 = 200;
public Line(int x1, int rotate) {
this.x1 = x1;
this.rotate = rotate;
}
void drawLine(Graphics g) {
int Radius = (int) (Math.min(getWidth(), getHeight()) * 0.4);
int sLength = (int) (Radius * 0.9);
int xSecond = (int) (x1 + sLength * Math.sin(rotate * (2 * Math.PI / 100)));
int ySecond = (int) (y1 - sLength * Math.cos(rotate * (2 * Math.PI / 100)));
g.setColor(Color.GREEN);
g.drawLine(x1, y1, xSecond, ySecond);
}
}
}
Me so much :D
float centerY = 250;
float x1 = 0;
float x2 = 0;
float y2 = 400;
float way2 = 0;
for (int i = 0; i < 125; i++)
{
x2 += cos(way2*PI/-180)*10;
y2 += sin(way2*PI/-180)*10;
way2 += centerY/y2*0.235*10;
x1 += y2/600*10;
g.drawLine(x1,centerY,x2,y2);
}
Here's what I figured out, little different though :)
public void paint(Graphics g) {
for (int i = 0; i < 100; i++) {
int x = 1 + i * 3;
g.drawLine(x, 200, x + i, 400 - i * i / 20);
g.drawLine(600 - x, 200, 600 - (x + i), 400 - i * i / 20);
}
}
We need to rework on the function '400 - i * i / 20'.
Related
I currently have a working code that draws a fractal tree using recursion. However, when I try to draw it iteratively, it is not working.
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JFrame;
/*
-Used built in Math trig methods to accomodate angling...looked this up online
https://stackoverflow.com/questions/30032635/java-swing-draw-a-line-at-a-specific-angle
*/
public class Test extends JFrame {
public Test() {
setBounds(100, 100, 800, 600); //sets the boundary for drawing
}
public void drawTree(Graphics g, int x1, int y1, double angle, int depth) {
System.out.println("x");
if (depth == 6){
return; //base case here to prevent infinite recursion..
}
else {
System.out.println("y1");
//embedded portion '(Math.toRadians(angle) * depth * 10.0)'represents angle...
int x2 = x1 + (int) (Math.cos(Math.toRadians(angle)) * depth * 10.0); //vertical shift calculated using the Sine...PARSED to int because method drawLine() accepts only ints as params
int y2 = y1 + (int) (Math.sin(Math.toRadians(angle)) * depth * 10.0); //hor. shift calculated using the Cos..PARSED to int
// System.out.println("x2: " + x2);//will reflect the change in vertical shift
//System.out.println("y2: " + y2);//will reflect change in hor. shift
g.drawLine(x1, y1, x2, y2);//value x1 equals previous line...in other word, start at where previously left off
// notice that the end point (x2 and y2) becomes starting point for each successive call
drawTree(g, x2, y2, angle - 20, depth - 1); //DRAWS LEFT SIDE?
// drawTree(g, x2, y2, angle + 20, depth - 1); //DRAWS RIGHT SIDE?
}
if (depth == 6){
return; //base case here to prevent infinite recursion..
}
else {
System.out.println("y2");
int x2 = x1 + (int) (Math.cos(Math.toRadians(angle)) * depth * 10.0); //vertical shift calculated using the Sine...PARSED to int because method drawLine() accepts only ints as params
int y2 = y1 + (int) (Math.sin(Math.toRadians(angle)) * depth * 10.0); //hor. shift calculated using the Cos..PARSED to int
// System.out.println("x2: " + x2);//will reflect the change in vertical shift
//System.out.println("y2: " + y2);//will reflect change in hor. shift
g.drawLine(x1, y1, x2, y2);//value x1 equals previous line...in other word, start at where previously left off
// notice that the end point (x2 and y2) becomes starting point for each successive call
// drawTree(g, x2, y2, angle - 20, depth - 1); //DRAWS LEFT SIDE?
drawTree(g, x2, y2, angle + 20, depth - 1); //DRAWS RIGHT SIDE?
}
}
public void drawIteratively(Graphics g, int x1A, int y1A, int x1B, int y1B, double angleA, double angleB, int depthA, int depthB){
while (depthA != 4) {
int x2A = x1A + (int) (Math.cos(Math.toRadians(angleA)) * depthA * 10.0);
int y2A = y1A + (int) (Math.sin(Math.toRadians(angleA)) * depthA * 10.0);
g.drawLine(x1A, y1A, x2A, y2A); //remember it must continue drawing from where it left off
angleA = angleA - 20;
depthA = depthA - 1;
x1A = x2A;
y1A = y2A;
}
while (depthA != 4) {
int x2A = x1A + (int) (Math.cos(Math.toRadians(angleA)) * depthA * 10.0);
int y2A = y1A + (int) (Math.sin(Math.toRadians(angleA)) * depthA * 10.0);
g.drawLine(x1A, y1A, x2A, y2A); //remember it must continue drawing from where it left off
angleA = angleA - 20;
depthA = depthA - 1;
x1A = x2A;
y1A = y2A;
}
/*
while(depthB != 4){
int x2B = x1B + (int) (Math.cos(Math.toRadians(angleB)) * depthB * 10.0);
int y2B = y1B + (int) (Math.sin(Math.toRadians(angleB)) * depthB * 10.0);
g.drawLine(x1B, y1B, x2B, y2B);
angleB = angleB + 20;
depthB = depthB - 1;
x1B = x2B;
y1B = y2B;
}
*/
}
#Override
public void paint(Graphics g) {
g.setColor(Color.BLUE);
//drawTree(g, 400, 400, -90, 9); //these values corresponding to original line aka trunk during initial method call
drawIteratively(g, 400, 500, 400,500 ,-90 , -90, 9,9);
}
public static void main(String[] args) {
new Test().setVisible(true);
}
}
Ignoring the fractals math: if you want to keep the recursive drawing, warp the long process (recursive calculation) with a SwingWorker, and let it update the GUI. Here is an example:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingWorker;
public class RecursiveDraw extends JFrame {
private int x1A, y1A, x2A, y2A;
private final int W = 700, H = 500;
private Random random = new Random();
private Color randomColor = Color.BLUE;
private JPanel panel;
public RecursiveDraw() {
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
panel = new MyPanel();
add(panel, BorderLayout.CENTER);
pack();
setVisible(true);
new Task().run();
}
public void recursiveDraw(int x1A, int y1A, int depth){
if(depth > 15) { return;}
this.x1A = x1A; this.y1A = y1A;
x2A = random.nextInt(W);
y2A = random.nextInt(H);
randomColor = new Color(random.nextInt(0xFFFFFF));
panel.repaint();
try {
Thread.sleep(1000); //delay
} catch (InterruptedException ex) { ex.printStackTrace();}
recursiveDraw(x2A, y2A, ++depth );
}
class MyPanel extends JPanel{
public MyPanel() {
setPreferredSize(new Dimension(W,H));
}
#Override
public void paintComponent(Graphics g) {
//super.paintComponent(g); //requires storing all points calculated
//so they can be redrawn. recommended
g.setColor(randomColor);
g.drawLine(x1A, y1A, x2A, y2A);
}
}
class Task extends SwingWorker<Void,Void> {
#Override
public Void doInBackground() {
recursiveDraw(W/2, H/2, 0);
return null;
}
}
public static void main(String[] args) {
new RecursiveDraw();
}
}
The basic structure of iterative drawing, using Timer, as proposed by #MadProgrammer could look like this :
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public class TimerIterativeDraw extends JFrame {
private final static int W = 700, H = 500;
private final static int DELAY= 1000;
private final static int NUMBER_OF_DRAWS_LIMIT = 50;
private int x2A = W/2, y2A = H/2, x1A, y1A, numberOfDraws;
private Random random = new Random();
private Color randomColor = Color.BLUE;
private JPanel panel;
private Timer timer;
public TimerIterativeDraw() {
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
panel = new MyPanel();
add(panel, BorderLayout.CENTER);
pack();
setVisible(true);
timer = new Timer(DELAY,new Task());
timer.start();
}
public void upDateGui(){
if(numberOfDraws++ >= NUMBER_OF_DRAWS_LIMIT){
timer.stop();
}
x1A = x2A; y1A = y2A;
x2A = random.nextInt(W);
y2A = random.nextInt(H);
randomColor = new Color(random.nextInt(0xFFFFFF));
//for better implementation store all points in an array list
//so they can be redrawn
panel.repaint();
}
class MyPanel extends JPanel{
public MyPanel() {
setPreferredSize(new Dimension(W,H));
}
#Override
public void paintComponent(Graphics g) {
//super.paintComponent(g); //requires storing all points calculated
//so they can be redrawn. recommended
g.setColor(randomColor);
g.drawLine(x1A, y1A, x2A, y2A);
}
}
class Task implements ActionListener {
#Override
public void actionPerformed(ActionEvent arg0) {
upDateGui();
}
}
public static void main(String[] args) {
new TimerIterativeDraw();
}
}
There's probably a few ways you can do this, but...
You need some kind of class which you can call which will calculate the next step and record it
You need to make sure that you're only updating the state from within the context of the Event Dispatching Thread. This is important, as you don't want to update the UI or anything the UI might rely on out side the EDT, otherwise you run the risk of race conditions and dirty paints
So, first, we need some way to create the branches in some kind of stepped manner. The idea is to only generate a new branch each time the class is told to update.
The class will contain it's only state and management, but will provide access to a List of points which it has created, maybe something like...
public class Generator {
private List<Point> points;
private double angle;
private double delta;
private int depth = 9;
private Timer timer;
public Generator(Point startPoint, double startAngle, double delta) {
points = new ArrayList<>(25);
points.add(startPoint);
angle = startAngle;
this.delta = delta;
}
public List<Point> getPoints() {
return new ArrayList<Point>(points);
}
public boolean tick() {
Point next = updateTree(points.get(points.size() - 1), angle);
angle += delta;
depth--;
if (next != null) {
points.add(next);
}
return next != null;
}
public Point updateTree(Point p, double angle) {
if (depth == 6) {
return null;
}
System.out.println("depth = " + depth + "; angle = " + angle);
//embedded portion '(Math.toRadians(angle) * depth * 10.0)'represents angle...
int x2 = p.x + (int) (Math.cos(Math.toRadians(angle)) * depth * 10.0); //vertical shift calculated using the Sine...PARSED to int because method drawLine() accepts only ints as params
int y2 = p.y + (int) (Math.sin(Math.toRadians(angle)) * depth * 10.0); //hor. shift calculated using the Cos..PARSED to int
return new Point(x2, y2);
}
}
Now, this class only generates a single branch, in order to make a tree, you will need two instances of this class, with different deltas
Next, we need someway to ask this generator to generate the next step on a regular bases. For me, this typically invokes using a Swing Timer.
The reason been:
It's simple. Seriously, it's really simple
It won't block the EDT, thus not freezing the UI
It updates within the context of the EDT, making it safe to update the state of the UI from within.
Putting these two things together into a simple JPanel which controls the Timer and paints the points...
public class TestPane extends JPanel {
private Generator left;
private Generator right;
public TestPane() {
Point startPoint = new Point(200, 400);
left = new Generator(startPoint, -90, -20);
right = new Generator(startPoint, -90, 20);
Timer timer = new Timer(1000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
boolean shouldContinue = left.tick() && right.tick();
if (!shouldContinue) {
((Timer)(e.getSource())).stop();
}
repaint();
}
});
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 400);
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(Color.RED);
render(g2d, left.getPoints());
g2d.setColor(Color.BLUE);
render(g2d, right.getPoints());
g2d.dispose();
}
protected void render(Graphics2D g2d, List<Point> points) {
Point start = points.remove(0);
while (points.size() > 0) {
Point end = points.remove(0);
g2d.draw(new Line2D.Double(start, end));
start = end;
}
}
}
I am trying to draw a circle like this: http://35.197.37.158/Circle/
using drawline function and animate it same as the attached link.
here is what I tried but it draw a line of circle and delete the previous one
This is the class that have my code for drawing a circle and animating it using swin Timer. anyone have better idea to animate the circle ?
import java.awt.Color;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.GeneralPath;
import javax.swing.*;
public class CustomPanel extends JPanel implements ActionListener{
Point [] coordinates;
GeneralPath circle;
final int C = 10;
int j =0;
int i =j;
Point p;
Point p2 ;
Timer clock = new Timer(100, this);
public CustomPanel()
{
LinesCoordinates();
clock.setInitialDelay(50);
clock.start();
}
private void LinesCoordinates()
{
int numberOfLines = 360/C;
coordinates = new Point[numberOfLines];
double cx = 200.0;
double cy = 200.0;
double r = 75.0;
int count = 0;
for(int theta = 0; theta < 360; theta+=C)
{
int x = (int)(cx + r * Math.cos(Math.toRadians(theta)));
int y = (int)(cy + r * Math.sin(Math.toRadians(theta)));
coordinates[count++] = new Point(x, y);
}
}
#Override
public void actionPerformed(ActionEvent e) {
Redraw();
repaint();
}
public void Redraw(){
j=j+1;
p = p2;
}
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2.setPaint(Color.red);
// while (j<=coordinates.length){
while(i<=j){
j--;
p2 = coordinates[j % coordinates.length];
g2.drawLine(p.x, p.y, p2.x , p2.y);
}}}
And this is my Main
public static void main(String[] args)
{
// SwingUtilities.invokeLater(new Runnable() {
// public void run() {
JFrame frame = new JFrame("Circle ");
CustomPanel co = new CustomPanel();
frame.add(co);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(300, 300);
frame.setVisible(true);
}
Since I was not able to get your solution draw the circle I had to rewrite your code a bit. Here is my solution preserving your JPanel listening to Timer approach. Hopefully this will work for you. I can send the complete NetBeans project if you want.
package circleanimation;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JPanel;
import javax.swing.Timer;
public class CustomPanel extends JPanel implements ActionListener {
final int numberOfPoints = 20;
Point[] circleCoordinates = new Point[numberOfPoints];
int nextPointToAnimate = 1;
Timer clock = new Timer(100, this);
public CustomPanel() {
calculateCirclePoints();
clock.setInitialDelay(50);
clock.start();
}
private void calculateCirclePoints() {
int angle = 360 / numberOfPoints;
double cx = 150.0;
double cy = 150.0;
double r = 75.0;
int count = 0;
for (int totalAngle = 0; totalAngle < 360; totalAngle = totalAngle + angle) {
int x = (int) (cx + r * Math.cos(Math.toRadians(totalAngle)));
int y = (int) (cy + r * Math.sin(Math.toRadians(totalAngle)));
circleCoordinates[count++] = new Point(x, y);
}
}
#Override
protected void paintComponent(Graphics g) {
g.setColor(Color.red);
for (int i = 0; i < nextPointToAnimate; i++) {
Point firstPoint = circleCoordinates[i];
Point secondPoint;
if (i == numberOfPoints - 1) {
secondPoint = circleCoordinates[0];
} else {
secondPoint = circleCoordinates[i + 1];
}
g.drawLine(firstPoint.x, firstPoint.y, secondPoint.x, secondPoint.y);
}
}
#Override
public void actionPerformed(ActionEvent e) {
nextPointToAnimate++;
if (nextPointToAnimate == numberOfPoints) {
clock.stop();
}
repaint();
}
}
I'm having issues drawing some circles to my JFrame. I originally had it using the default layout and realized this was only adding the most recent circle, so I changed the layout to null, and now nothing gets drawn. I've also tried frame.setLayout(new FlowLayout()) which also doesn't draw anything. Any help would be appreciated!
import java.util.ArrayList;
import java.util.Random;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
/**
* #author Christopher Nielson
*
*/
public class Main {
private static JFrame frame;
private static Random rand;
private static Jiggler jiggler;
private static ArrayList<JComponent> circles;
private static int fps;
public static void main(String[] args) {
frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(null);
frame.setBounds(100, 100, 450, 450);
rand = new Random();
circles = new ArrayList<JComponent>();
int x = frame.getWidth();
int y = frame.getHeight();
for (int i = 0; i < Integer.parseInt(args[0]); i++) {
circles.add(new Circle(rand.nextInt(frame.getWidth()), rand.nextInt(frame.getHeight()),
rand.nextInt(frame.getWidth() / 10) + 100, rand.nextInt(frame.getHeight() / 10) + 100, null));
}
circles.forEach(current -> {
frame.add(current);
});
frame.setVisible(true);
jiggler = new Jiggler(circles, new JLabel("FPS: ")); // TODO add fps
jiggler.run();
}
}
And this is one reason you'll see us recommending time and time again to avoid using null layouts like the plague.
Having said that, your main problem is a design problem, not a layout problem, and that problem being that your Circle class shouldn't extend JComponent or any component for that matter, since if you want to draw multiple circles, you should have only one component, probably a JPanel doing the drawing, and the Circles should be logical classes, classes that have a public void draw(Graphics g) method, not component classes. You would pass the List of Circles to your drawing JPanel, and it would draw the Circles in its paintComponent method by calling the draw(g) methods of each Circle in the list.
For example:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.event.*;
import java.awt.geom.*;
import java.util.ArrayList;
import java.util.List;
import javax.swing.*;
#SuppressWarnings("serial")
public class DrawChit extends JPanel {
private static final int PREF_W = 900;
private static final int PREF_H = 700;
private static final int MAX_SHAPES = 30;
private List<MyShape> shapes = new ArrayList<>();
public DrawChit() {
setBackground(Color.WHITE);
for (int i = 0; i < MAX_SHAPES; i++) {
double x = (PREF_W - 100) * Math.random();
double y = (PREF_H - 100) * Math.random();
double w = 100 + (Math.random() * PREF_W) / 10;
double h = 100 + (Math.random() * PREF_H) / 10;
Ellipse2D ellipse = new Ellipse2D.Double(x, y, w, h);
float hue = (float) Math.random();
double delta = 0.3;
float saturation = (float) (Math.random() * delta + (1 - delta));
float brightness = (float) (Math.random() * delta + (1 - delta));
Color color = Color.getHSBColor(hue, saturation, brightness);
shapes.add(new MyShape(ellipse, color));
}
// we'll throw a black square in the middle!
int rectW = 200;
int rectX = (PREF_W - rectW) / 2;
int rectY = (PREF_H - rectW) / 2;
shapes.add(new MyShape(new Rectangle(rectX, rectY, rectW, rectW), Color.BLACK));
MyMouse myMouse = new MyMouse();
addMouseListener(myMouse);
addMouseMotionListener(myMouse);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
// use anti-aliasing to make graphics smooth
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
// iterate through the shapes list, filling all
for (MyShape shape : shapes) {
shape.fill(g2);
}
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
private class MyMouse extends MouseAdapter {
private Point p0 = null;
private MyShape shape = null;
#Override
public void mousePressed(MouseEvent e) {
if (e.getButton() != MouseEvent.BUTTON1) {
return;
}
// iterate *backwards* so get top-most Shape
for (int i = shapes.size() - 1; i >= 0; i--) {
if (shapes.get(i).contains(e.getPoint())) {
p0 = e.getPoint();
shape = shapes.get(i);
// move selected shape to the top!
shapes.remove(shape);
shapes.add(shape);
repaint();
return;
}
}
}
#Override
public void mouseDragged(MouseEvent e) {
if (p0 != null) {
moveShape(e.getPoint());
}
}
#Override
public void mouseReleased(MouseEvent e) {
if (p0 != null) {
moveShape(e.getPoint());
p0 = null;
shape = null;
}
}
// translates the shape
private void moveShape(Point p1) {
int deltaX = p1.x - p0.x;
int deltaY = p1.y - p0.y;
shape.translate(deltaX, deltaY);
p0 = p1;
repaint();
}
}
private static void createAndShowGui() {
DrawChit mainPanel = new DrawChit();
JFrame frame = new JFrame("Draw Chit");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
}
class MyShape {
private Path2D path = new Path2D.Double();
private Color color;
public MyShape(Shape shape, Color color) {
path.append(shape, true);
this.color = color;
}
public boolean contains(Point p) {
return path.contains(p);
}
public void draw(Graphics2D g2) {
g2.setColor(color);
g2.draw(path);
}
public void fill(Graphics2D g2) {
g2.setColor(color);
g2.fill(path);
}
public void translate(int deltaX, int deltaY) {
path.transform(AffineTransform.getTranslateInstance(deltaX, deltaY));
}
}
I'm new to java, and this is a first for using methods. I've been trying to create a Epitrochoid, but it only outputs has a straight line. Is there something wrong with my method? If so, I'm asking for any tips/hints for solving the problem.
I've posted the relevant code below.
public class myEpitrochoid{
public myEpitrochoid( ) {
double a = 50;
double b = 5;
double k = 10;
int num = 100;
drawEpitrochoid( a, b, k, num);
}
private void drawEpitrochoid (double a, double b, double k, int num) {
for ( int t=1 ; t<=num ; t++ ) {
t = t/num;
double x = (a+b)*cos(2*PI*t) - k * cos(2*PI*(a+b)*(t/b));
double y = (a+b)*sin(2*PI*t) - k * sin(2*PI*(a+b)*(t/b));
yertle.moveTo(x,y);
}
}
}
Here is a working example
import java.awt.Color;
import java.awt.Dimension;
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.Ellipse2D;
import java.awt.geom.Line2D;
import java.awt.geom.Path2D;
import java.beans.Transient;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
#SuppressWarnings("serial")
public class EpitrochoidDemo extends JPanel {
private Path2D.Double points = new Path2D.Double();
private double t = 0.0;
private double a = 50;
private double b = 5;
private double k = 1.7;
private Ellipse2D.Double stationaryCircle;
protected double num = 100.0;
public EpitrochoidDemo() {
setBackground(Color.black);
stationaryCircle = new Ellipse2D.Double(400 - a, 400 - a,
2 * a, 2 * a);
nextPoint();
}
public void nextPoint() {
double x = (a + b) * Math.cos(2 * Math.PI * t) - k
* Math.cos(2 * Math.PI * (a + b) * (t / b));
double y = (a + b) * Math.sin(2 * Math.PI * t) - k
* Math.sin(2 * Math.PI * (a + b) * (t / b));
x += 400;
y += 400;
// The Path2D needs an initial point
if (t == 0.0)
points.moveTo(x, y);
// Draw a line from previous point to this point
else
points.lineTo(x, y);
t += 0.001;
}
#Override
#Transient
public Dimension getPreferredSize() {
return new Dimension(800, 800);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
// Pretty graphics
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
// Draw axis and stationary circle and timer in corner
g2d.setColor(Color.white);
g2d.drawString("t=" + t, 10, 10);
g2d.draw(new Line2D.Double(0, 400, 800, 400));
g2d.draw(new Line2D.Double(400, 0, 400, 800));
g2d.draw(stationaryCircle);
g2d.setColor(Color.red);
g2d.draw(points);
}
public static void main(String[] args) {
JFrame frame = new JFrame();
final EpitrochoidDemo e = new EpitrochoidDemo();
frame.getContentPane().add(e);
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
final Timer timer = new Timer(15, new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
e.nextPoint();
e.repaint();
if (e.t >= e.num) {
((Timer) arg0.getSource()).stop();
}
}
});
timer.start();
}
}
I am using the same formulas as you are (also posted here) and it works just fine as demonstrated by the picture
I need a on-screen virtual joystick solution to control external device - a cart. My program is written on Java/Swing and it works on windows rugged tablet with resistive touch screen. There's no x-y slider control in Swing, failed to find it thru Google, so probably I need to write it from scratch. Now the question: what is best approach to write such control? I was thinking to place two JSlider controls, one horizontal and second vertical, and make custom thumb, but i'm afraid this can bring me troubles because it's actually a hack. Maybe just write it from scratch? Maybe there's an existing solution?
I will use it with thumb dragging and touching final thumb position.
for example
import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.Stroke;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class SimpleJoystickDemo extends JFrame {
//I think that orginally made by #HFOE
private int displayWidth = 340;
private int displayHeight = 550;
private final Point position;
public SimpleJoystickDemo() {
super("SimpleJoystickDemo");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(displayWidth, displayHeight);
setLocationRelativeTo(null);
position = new Point();
SimpleJoystick myJoystick = new SimpleJoystick(150, position, 100);
add(myJoystick, BorderLayout.PAGE_END);
Drawing drawing = new Drawing(position);
add(drawing);
}
public static void main(final String[] args) {
Runnable gui = new Runnable() {
#Override
public void run() {
new SimpleJoystickDemo().setVisible(true);
}
};
SwingUtilities.invokeLater(gui);
}
private class Drawing extends JPanel {
private static final long serialVersionUID = 1L;
private final Point position;
public Drawing(Point position) {
this.position = position;
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2.setColor(Color.RED);
g2.fillOval(160 + position.x, 160 - position.y, 15, 15);
}
}
}
class SimpleJoystick extends JPanel {
private static final long serialVersionUID = 1L;
//Maximum value for full horiz or vert position where centered is 0:
private int joyOutputRange;
private float joySize; //joystick icon size
private float joyWidth, joyHeight;
private float joyCenterX, joyCenterY; //Joystick displayed Center
//Display positions for text feedback values:
private int textHorizPos, textVertPos;
private int fontSpace = 12;
private float curJoyAngle; //Current joystick angle
private float curJoySize; //Current joystick size
private boolean isMouseTracking;
private boolean leftMouseButton;
private int mouseX, mouseY;
private Stroke lineStroke = new BasicStroke(10, BasicStroke.CAP_ROUND,
BasicStroke.JOIN_ROUND);
private final Point position;
public SimpleJoystick(final int joyOutputRange,
final Point position, final int joySize) {
this.joyOutputRange = joyOutputRange;
this.position = position;
this.joySize = joySize;
joyWidth = joySize;
joyHeight = joyWidth;
setPreferredSize(new Dimension((int) joyWidth + 250,
(int) joyHeight + 80));
joyCenterX = getPreferredSize().width / 2;
joyCenterY = getPreferredSize().height / 2;
this.joySize = joyWidth / 2;
setBackground(new Color(226, 226, 226));
MouseAdapter mouseAdapter = new MouseAdapter() {
#Override
public void mouseMoved(final MouseEvent e) {
mouseCheck(e);
}
#Override
public void mousePressed(final MouseEvent e) {
leftMouseButton = SwingUtilities.isLeftMouseButton(e);
mouseCheck(e);
}
};
addMouseMotionListener(mouseAdapter);
addMouseListener(mouseAdapter);
}
private void mouseCheck(final MouseEvent e) {
mouseX = e.getX();
mouseY = e.getY();
float dx = mouseX - joyCenterX;
float dy = mouseY - joyCenterY;
if (leftMouseButton) {
isMouseTracking = true;
} else {
isMouseTracking = false;
}
if (isMouseTracking) {
curJoyAngle = (float) Math.atan2(dy, dx);
curJoySize = (float) Point.distance(mouseX, mouseY,
joyCenterX, joyCenterY);
} else {
curJoySize = 0;
}
if (curJoySize > joySize) {
curJoySize = joySize;
}
position.x = (int) (joyOutputRange * (Math.cos(curJoyAngle)
* curJoySize) / joySize);
position.y = (int) (joyOutputRange * (-(Math.sin(curJoyAngle)
* curJoySize) / joySize));
SwingUtilities.getRoot(SimpleJoystick.this).repaint();
}
#Override
protected void paintComponent(final Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2.setColor(Color.LIGHT_GRAY);
g2.fillOval((int) (joyCenterX - joyWidth / 2),
(int) (joyCenterY - joyHeight / 2),
(int) joyWidth, (int) joyHeight);
//rotate and draw joystick line segment:
Graphics2D g3 = (Graphics2D) g2.create();
g3.translate(joyCenterX, joyCenterY);
g3.rotate(curJoyAngle);
g3.setColor(Color.GRAY);
g3.setStroke(lineStroke);
g3.drawLine(0, 0, (int) curJoySize, 0);
g3.dispose();
//
g2.setColor(Color.GRAY);
g2.fillOval((int) joyCenterX - 10, (int) joyCenterY - 10, 20, 20);
textHorizPos = 50;
textVertPos = (int) (joyCenterY - 50);
g2.drawString("Horizont:", textHorizPos, textVertPos);
textHorizPos += (4 * fontSpace);
g2.drawString(String.valueOf(position.x), textHorizPos, textVertPos);
textHorizPos = 50;
textVertPos += 12;
g2.drawString("Vertical :", textHorizPos, textVertPos);
textHorizPos += (4 * fontSpace);
g2.drawString(String.valueOf(position.y), textHorizPos, textVertPos);
}
}
Reworked code from #tutejszy. Warnings removed and the missing PointChangeEvent added.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
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 javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
public class VJoystick extends JPanel implements ChangeListener
{
private JLabel lblPosition;
private static final long serialVersionUID = 1L;
public VJoystick()
{
setLayout(new BorderLayout(0, 0));
SimpleJoystick myJoystick = new SimpleJoystick(150);
myJoystick.setPreferredSize(new Dimension(100,100));
myJoystick.addChangeListener(this);
add(myJoystick, BorderLayout.CENTER);
lblPosition = new JLabel("position");
add(lblPosition, BorderLayout.SOUTH);
}
#Override
public void stateChanged(ChangeEvent ev) {
Point p = null;
try {
p = ((PointChangeEvent)ev).p;
} catch (Exception e) {
return;
}
lblPosition.setText("x="+p.x+" y="+p.y);
}
}
class SimpleJoystick extends JPanel
{
private static final long serialVersionUID = 1L;
/** Maximum value for full horiz or vert position where centered is 0 */
private int joyOutputRange;
/** max x and y value, in pixels */
private int joyRadius;
/** Joystick displayed Center, in pixels */
private int joyCenterX, joyCenterY;
/** joystick output position scaled to given joyOutputRange */
private Point position = new Point();
/** joystick x axis value in pixels */
private int dx = 0;
/** joystick y axis value in pixels */
private int dy = 0;
/**
* #param joyOutputRange
*/
public SimpleJoystick(final int joyOutputRange) {
this.joyOutputRange = joyOutputRange;
setBackground(new Color(226, 226, 226));
MouseAdapter mouseAdapter = new MouseAdapter() {
#Override
public void mousePressed(final MouseEvent e) {
if (SwingUtilities.isLeftMouseButton(e) && cursorChanged(e.getX(), e.getY())) {
SwingUtilities.getRoot(SimpleJoystick.this).repaint();
fireStateChanged();
}
}
#Override
public void mouseDragged(MouseEvent e) {
if (SwingUtilities.isLeftMouseButton(e) && cursorChanged(e.getX(), e.getY())) {
SwingUtilities.getRoot(SimpleJoystick.this).repaint();
fireStateChanged();
}
}
};
addMouseMotionListener(mouseAdapter);
addMouseListener(mouseAdapter);
}
private boolean cursorChanged(int mouseX, int mouseY) {
if (joyRadius == 0) return false;
dx = mouseX - joyCenterX;
dy = mouseY - joyCenterY;
if (dx > joyRadius) dx = joyRadius;
if (dy > joyRadius) dy = joyRadius;
if (dx < -joyRadius) dx = -joyRadius;
if (dy < -joyRadius) dy = -joyRadius;
position.x = joyOutputRange * dx / joyRadius;
position.y = -joyOutputRange * dy / joyRadius;
return true;
}
#Override
protected void paintComponent(final Graphics g) {
super.paintComponent(g);
int joyWidth = getSize().width;
int joyHeight = getSize().height;
joyRadius = Math.min(joyWidth, joyHeight) / 2;
if (joyRadius == 0) return;
joyCenterX = joyWidth / 2;
joyCenterY = joyHeight / 2;
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
int diameter;
//background
g2.setColor(Color.LIGHT_GRAY);
diameter = joyRadius*2;
g2.fillOval(joyCenterX - diameter/2 , joyCenterY - diameter/2, diameter, diameter);
g2.setColor(Color.RED);
diameter = 40;
g2.fillOval(joyCenterX + dx - diameter/2 , joyCenterY + dy - diameter/2, diameter, diameter);
//thumb
g2.setColor(Color.GRAY);
diameter = 20;
g2.fillOval(joyCenterX - diameter/2 , joyCenterY - diameter/2, diameter, diameter);
}
void addChangeListener(ChangeListener listener) {
listenerList.add(ChangeListener.class, listener);
}
void removeChangeListener(ChangeListener listener) {
listenerList.remove(ChangeListener.class, listener);
}
protected void fireStateChanged() {
ChangeEvent e = new PointChangeEvent(this, position);
Object[] listeners = listenerList.getListenerList();
for (int i = listeners.length - 2; i >= 0; i -= 2) {
if (listeners[i] == ChangeListener.class) {
((ChangeListener) listeners[i + 1]).stateChanged(e);
}
}
}
}
class PointChangeEvent extends ChangeEvent
{
private static final long serialVersionUID = 1L;
public Point p;
public PointChangeEvent(Object source, Point p)
{
super(source);
this.p=p;
}
}
This is reworked code from #mKorbel, it's event based and more swing-oriented, without floats:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
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 javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
class JoyDemo extends JPanel implements ChangeListener {
private JLabel lblPosition;
public JoyDemo() {
setLayout(new BorderLayout(0, 0));
SimpleJoystick myJoystick = new SimpleJoystick(150);
myJoystick.setPreferredSize(new Dimension(100,100));
myJoystick.addChangeListener(this);
add(myJoystick, BorderLayout.CENTER);
lblPosition = new JLabel("position");
add(lblPosition, BorderLayout.SOUTH);
}
#Override
public void stateChanged(ChangeEvent ev) {
Point p = null;
try {
p = ((PointChangeEvent)ev).p;
} catch (Exception e) {
return;
}
lblPosition.setText("x="+p.x+" y="+p.y);
}
}
class SimpleJoystick extends JPanel {
/** Maximum value for full horiz or vert position where centered is 0 */
private int joyOutputRange;
/** max x and y value, in pixels */
private int joyRadius;
/** Joystick displayed Center, in pixels */
private int joyCenterX, joyCenterY;
/** joystick output position scaled to given joyOutputRange */
private Point position = new Point();
/** joystick x axis value in pixels */
private int dx = 0;
/** joystick y axis value in pixels */
private int dy = 0;
/**
* #param joyOutputRange
*/
public SimpleJoystick(final int joyOutputRange) {
this.joyOutputRange = joyOutputRange;
setBackground(new Color(226, 226, 226));
MouseAdapter mouseAdapter = new MouseAdapter() {
#Override
public void mousePressed(final MouseEvent e) {
if (SwingUtilities.isLeftMouseButton(e) && cursorChanged(e.getX(), e.getY())) {
SwingUtilities.getRoot(SimpleJoystick.this).repaint();
fireStateChanged();
}
}
#Override
public void mouseDragged(MouseEvent e) {
if (SwingUtilities.isLeftMouseButton(e) && cursorChanged(e.getX(), e.getY())) {
SwingUtilities.getRoot(SimpleJoystick.this).repaint();
fireStateChanged();
}
}
};
addMouseMotionListener(mouseAdapter);
addMouseListener(mouseAdapter);
}
private boolean cursorChanged(int mouseX, int mouseY) {
if (joyRadius == 0) return false;
dx = mouseX - joyCenterX;
dy = mouseY - joyCenterY;
if (dx > joyRadius) dx = joyRadius;
if (dy > joyRadius) dy = joyRadius;
if (dx < -joyRadius) dx = -joyRadius;
if (dy < -joyRadius) dy = -joyRadius;
position.x = joyOutputRange * dx / joyRadius;
position.y = -joyOutputRange * dy / joyRadius;
return true;
}
#Override
protected void paintComponent(final Graphics g) {
super.paintComponent(g);
int joyWidth = getSize().width;
int joyHeight = getSize().height;
joyRadius = Math.min(joyWidth, joyHeight) / 2;
if (joyRadius == 0) return;
joyCenterX = joyWidth / 2;
joyCenterY = joyHeight / 2;
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
int diameter;
//background
g2.setColor(Color.LIGHT_GRAY);
diameter = joyRadius*2;
g2.fillOval(joyCenterX - diameter/2 , joyCenterY - diameter/2, diameter, diameter);
g2.setColor(Color.RED);
diameter = 40;
g2.fillOval(joyCenterX + dx - diameter/2 , joyCenterY + dy - diameter/2, diameter, diameter);
//thumb
g2.setColor(Color.GRAY);
diameter = 20;
g2.fillOval(joyCenterX - diameter/2 , joyCenterY - diameter/2, diameter, diameter);
}
void addChangeListener(ChangeListener listener) {
listenerList.add(ChangeListener.class, listener);
}
void removeChangeListener(ChangeListener listener) {
listenerList.remove(ChangeListener.class, listener);
}
protected void fireStateChanged() {
ChangeEvent e = new PointChangeEvent(this, position);
Object[] listeners = listenerList.getListenerList();
for (int i = listeners.length - 2; i >= 0; i -= 2) {
if (listeners[i] == ChangeListener.class) {
((ChangeListener) listeners[i + 1]).stateChanged(e);
}
}
}
}
Here's my solution. Note, I reworked #Dark.Rider's answer, who reworked #tutejszy's answer, who reworked #mKorbel's answer...
I made 2 files. One called Joystick.java
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.beans.PropertyChangeListener;
import javax.swing.event.SwingPropertyChangeSupport;
import javax.swing.SwingUtilities;
public class Joystick extends javax.swing.JPanel {
private final int outputMax;
private final int thumbDiameter;
private final int thumbRadius;
private final int panelWidth;
private final int arrowRadius;
private final int BORDER_THICKNESS = 2;
private final Point thumbPos = new Point();
protected SwingPropertyChangeSupport propertySupporter = new SwingPropertyChangeSupport(this);
/**
* #param output_max The maximum value to scale output to. If this value was
* 5 and the joystick thumb was dragged to the top-left corner, the output
* would be (-5,5)
* #param panel_width how big the JPanel will be. The sizes of the joystick's
* visual components are proportional to this value
*/
public Joystick(int output_max, int panel_width) {
assert output_max > 0;
assert panel_width > 0;
outputMax = output_max;
panelWidth = panel_width;
thumbDiameter = panel_width/4;
thumbRadius = thumbDiameter/2;
arrowRadius = panel_width/24;
MouseAdapter mouseAdapter = new MouseAdapter() {
private void repaintAndTriggerListeners(){
SwingUtilities.getRoot(Joystick.this).repaint();
propertySupporter.firePropertyChange(null, null, getOutputPos());
}
#Override
public void mousePressed(final MouseEvent e) {
if (SwingUtilities.isLeftMouseButton(e)) {
updateThumbPos(e.getX(), e.getY());
repaintAndTriggerListeners();
}
}
#Override
public void mouseDragged(MouseEvent e) {
if (SwingUtilities.isLeftMouseButton(e)) {
updateThumbPos(e.getX(), e.getY());
repaintAndTriggerListeners();
}
}
#Override
public void mouseReleased(MouseEvent e) {
if (SwingUtilities.isLeftMouseButton(e)) {
centerThumbPad();
repaintAndTriggerListeners();
}
}
};
addMouseMotionListener(mouseAdapter);
addMouseListener(mouseAdapter);
setPreferredSize(new java.awt.Dimension(panel_width, panel_width));
setOpaque(false);
centerThumbPad();
}
private void centerThumbPad(){
thumbPos.x = panelWidth/2;
thumbPos.y = panelWidth/2;
}
/**
* update both thumbPos
* #param mouseX the x position of cursor that has clicked in the joystick panel
* #param mouseY the y position of cursor that has clicked in the joystick panel
* #return
*/
private void updateThumbPos(int mouseX, int mouseY) {
// if the cursor is clicked out of bounds, we'll modify the position
// to be the closest point where we can draw the thumb pad completely
if (mouseX < thumbRadius)
mouseX = thumbRadius;
else if(mouseX > panelWidth - thumbRadius)
mouseX = panelWidth - thumbRadius;
if (mouseY < thumbRadius)
mouseY = thumbRadius;
else if(mouseY > panelWidth - thumbRadius)
mouseY = panelWidth - thumbRadius;
thumbPos.x = mouseX;
thumbPos.y = mouseY;
}
/**
* #return the scaled position of the joystick thumb pad
*/
Point getOutputPos(){
Point result = new Point();
result.x = outputMax * (thumbPos.x - panelWidth/2) / (panelWidth/2-thumbDiameter/2);
result.y = -outputMax * (thumbPos.y - panelWidth/2) / (panelWidth/2-thumbDiameter/2);
return result;
}
#Override
protected void paintComponent(final Graphics g) {
super.paintComponent(g);
//joystick background border
g.setColor(Color.BLACK);
g.fillOval(thumbRadius, thumbRadius, panelWidth-thumbDiameter, panelWidth-thumbDiameter);
//joystick background color
g.setColor(Color.GRAY);
g.fillOval(thumbRadius+BORDER_THICKNESS, thumbRadius+BORDER_THICKNESS, panelWidth-thumbDiameter-BORDER_THICKNESS*2, panelWidth-thumbDiameter-BORDER_THICKNESS*2);
//joystick background arrows
g.setColor(Color.BLACK);
int[] left_x = {thumbDiameter-arrowRadius,thumbDiameter+arrowRadius,thumbDiameter+arrowRadius};
int[] left_y = {panelWidth/2,panelWidth/2+arrowRadius,panelWidth/2-arrowRadius};
g.fillPolygon(left_x, left_y,3);
int[] right_x = {panelWidth-thumbDiameter+arrowRadius,panelWidth-thumbDiameter-arrowRadius,panelWidth-thumbDiameter-arrowRadius};
int[] right_y = {panelWidth/2,panelWidth/2+arrowRadius,panelWidth/2-arrowRadius};
g.fillPolygon(right_x, right_y,3);
int[] up_x = left_y;
int[] up_y = left_x;
g.fillPolygon(up_x, up_y,3);
int[] down_x = right_y;
int[] down_y = right_x;
g.fillPolygon(down_x, down_y,3);
//thumb pad border
g.setColor(Color.BLACK);
g.fillOval(thumbPos.x - thumbRadius - BORDER_THICKNESS, thumbPos.y - thumbRadius - BORDER_THICKNESS, thumbRadius*2+BORDER_THICKNESS*2, thumbRadius*2+BORDER_THICKNESS*2);
//thumb pad color
g.setColor(Color.GRAY);
g.fillOval(thumbPos.x - thumbRadius, thumbPos.y - thumbRadius, thumbRadius*2, thumbRadius*2);
}
#Override
public void addPropertyChangeListener(PropertyChangeListener listener) {
propertySupporter.addPropertyChangeListener(listener);
}
#Override
public void removePropertyChangeListener(PropertyChangeListener listener) {
propertySupporter.removePropertyChangeListener(listener);
}
}
and one called JoystickOutputContainer.java (used to make example pictures)
import java.awt.BorderLayout;
import java.awt.Point;
import java.beans.PropertyChangeListener;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class JoystickOutputContainer extends JPanel {
private Joystick myJoystick = new Joystick(100, 140);
private JLabel lblPosition = new JLabel("x=0 y=0");
public JoystickOutputContainer() {
setLayout(new BorderLayout(0, 0));
add(myJoystick, BorderLayout.CENTER);
add(lblPosition, BorderLayout.SOUTH);
myJoystick.addPropertyChangeListener(updateConsoleListener);
}
private final PropertyChangeListener updateConsoleListener = (evt) -> {
updateConsoleCallback( (Point) evt.getNewValue() );
};
private void updateConsoleCallback(Point p){
lblPosition.setText("x="+p.x+" y="+p.y);
}
}