Why does Carpaint not move when all other sprites do? - java

I have a code for moving sprites in an animation - all but one of my sprites moves and I can't figure out why. Can someone explain?
My Sprite class:
abstract class SimpleSprite {
// basic x,y movement,keeps a master list of Sprites
public static final ArrayList<SimpleSprite> sprites = new ArrayList<SimpleSprite>();
float x, y, dx, dy; // position and velocity (pixels/TIMER_MSEC)
public SimpleSprite(float x, float y, float dx, float dy) {
// initial position and velocity
this.x = x;
this.y = y;
this.dx = dx;
this.dy = dy;
sprites.add(this);
}
public void update() { // update position and velocity every n milliSec
// default - just move at constant velocity
x += dx; // velocity in x direction
y += dy; // velocity in y direction
}
abstract public void draw(Graphics2D g2d);
// just draw at current position, no updating.
}
My sprite that doesn't work:
class Carpaint extends SimpleSprite {
public Carpaint(float x, float y, float dx, float dy) {
super(x, y, dx, dy);
}
#Override
public void draw(Graphics2D g2d){
g2d.setColor(Color.pink);
g2d.fillRect(50, 50, 40, 60);
g2d.setColor(Color.black);
g2d.drawRect(50, 50, 40, 60);
g2d.drawRect(60, 60, 20, 40);
g2d.fillRect(45, 50, 5, 15);
g2d.fillRect(90, 50, 5 , 15);
g2d.fillRect(45, 95, 5, 16);
g2d.fillRect(90, 95, 5, 16);
}
}
My main:
public static void main(String[] args) {
// create and display the animation in a JFrame
final JFrame frame = new JFrame("Animation 2 (close window to exit)");
Animation2 animationPanel = new Animation2(600, 500);
frame.add(animationPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
// add some sprites...
new Square(0, 0, 3, 2, 40);
new Ball(500, 0, -3, 3, 20);
new Ball(0, 500, 2, -5, 30);
new Carpaint(100, 100, 20, -6);
}
I expected all sprites to move but the car doesn't (see here: https://i.gyazo.com/0219127277d2543735b3a4727e7c7e72.mp4)

Answer came from #RealSkeptic - I wasn't painting the car with reference to x and y.
New Code:
class Carpaint extends SimpleSprite {
public Carpaint(float x, float y, float dx, float dy) {
super(x, y, dx, dy);
}
#Override
public void draw(Graphics2D g2d){
g2d.setColor(Color.pink);
g2d.fillRect((int) x, (int) y, 40, 60);
g2d.setColor(Color.black);
g2d.drawRect((int) x, (int) y, 40, 60);
g2d.drawRect((int) x + 10, (int) y + 10, 20, 40);
g2d.fillRect((int) x-5, (int) y, 5, 15);
g2d.fillRect((int) x + 40, (int) y, 5 , 15);
g2d.fillRect((int) x - 5, (int) y + 45, 5, 16);
g2d.fillRect((int) x + 40, (int) y + 45, 5, 16);
}
}

Related

Creating sliders with arrow buttons

How can I create a slider with two arrow buttons from the side? The arrows/triangle should turn white when clicked. And the slider should move each time the arrow button is clicked.
here's the link to what it should look like
and here's what I've done so far
int x=75;
void setup() {
size(600,400);
}
void draw() {
background(100);
fill (200);
rect (75, 25, 400, 50);
stroke(0);
if(mousePressed) {
if (mouseX >75 && mouseX <= 475)
{x=mouseX;}
}
fill(127,0,0);
rect (x, 20, 9, 60);
fill (255);
fill (200);
rect (10, 25, 50, 50);
{
if (mousePressed == true) {
fill(255);
} else {
fill(0);
}
triangle (50, 60, 50, 40, 15, 50);
}
fill (200);
rect (490, 25, 50, 50);
{
if (mousePressed == true) {
fill(255);
} else {
fill(0);
}
triangle (500, 60, 500, 40, 535, 50);
}
println(x);
}
When I click anywhere on the screen, my problem is that both arrows turn white. I need it to individually function. And the slider is not moving every time I click the arrow buttons
The following approach to this problem uses 4 different classes: ForwardArrow, BackArrow, Slider, and ValueField. Arrow fill color is controlled by the press() method of its respective class.
color BLUE = color(64, 124, 188);
color LTGRAY = color(185, 180, 180);
color YELLOW = color(245, 250, 13);
color RED = color(255, 0, 0);
color BLACK = color(0, 0, 0);
color WHITE = color(255, 255, 255);
color GREEN = color(32, 175, 47);
ForwardArrow _fwdArrw;
BackArrow _backArrw;
ValueField _valueFld;
Slider _slider;
final int _sliderX = 90;
final int _sliderY = 40;
final int _sliderW = 200;
final int _sliderH = 30;
final int _txtSize = 22;
final int _initValue = 40;
final int _maxValue = 200;
final int _minValue = 0;
int value = _initValue;
class ValueField {
float x, y, w, h;
String title;
color fldColor;
color txtColor;
// Constructor
ValueField(int xpos, int ypos, float wt, float ht, String valueStr, color background, color foreground) {
x = xpos;
y = ypos;
w = wt;
h = ht;
title = valueStr;
fldColor = background;
txtColor = foreground;
}
void display(int val) {
// **** Value Field **** //
fill(fldColor); // erase old value
rect(x, y, w, h);
fill(txtColor); // text color
textSize(_txtSize);
textAlign(CENTER);
text(str(val), x, y, w, h);
// **** Slider bar **** //
fill(255);
rect(_sliderX, _sliderY, _sliderW*val/_maxValue, _sliderH);
}
}
class ForwardArrow {
float x, y, w, h;
color arrwColor;
// Constructor
ForwardArrow(int xpos, int ypos, float wt, float ht, color background) {
x = xpos;
y = ypos;
w = wt;
h = ht;
arrwColor = background;
}
void display() {
fill(arrwColor); // arrow color
noStroke();
triangle(x, y, x, y + h, x + w, y + h/2 );
}
void press() {
fill(255); // arrow color
noStroke();
triangle(x, y, x, y + h, x + w, y + h/2 );
}
}
class BackArrow {
float x, y, w, h;
color arrwColor;
// Constructor
BackArrow(int xpos, int ypos, float wt, float ht, color background) {
x = xpos;
y = ypos;
w = wt;
h = ht;
arrwColor = background;
}
void display() {
fill(arrwColor);
noStroke();
triangle(x, y + h/2, x + w, y, x + w, y + h );
}
void press() {
fill(255);
noStroke();
triangle(x, y + h/2, x + w, y, x + w, y + h );
}
}
class Slider {
float x, y, w, h;
color barColor;
color trimColor;
// Constructor
Slider(int xpos, int ypos, float wt, float ht, color background, color foreground) {
x = xpos;
y = ypos;
w = wt;
h = ht;
barColor = background;
trimColor = foreground;
}
void display() {
stroke(0);
strokeWeight(1);
noFill();
rect(x, y, w, h);
}
}
void setup() {
size(500, 250);
background(BLUE);
_slider = new Slider(_sliderX, _sliderY, _sliderW, _sliderH, WHITE, BLACK);
_backArrw = new BackArrow(50, 40, 30, 30, GREEN);
_fwdArrw = new ForwardArrow(300, 40, 30, 30, GREEN);
_valueFld = new ValueField(380, 40, 60, 30, str(_initValue), WHITE, BLACK);
}
void draw() {
background(BLUE);
_valueFld.display(value);
_fwdArrw.display();
_backArrw.display();
_slider.display();
// FwdArrw Long Press
if ((mouseX >= _fwdArrw.x) && (mouseX <= _fwdArrw.x + _fwdArrw.w) && (mouseY >= _fwdArrw.y) && (mouseY <= _fwdArrw.y + _fwdArrw.h)) {
if (mousePressed == true) {
_fwdArrw.press();
value++;
if (value > _maxValue) {
value = _maxValue;
}
_valueFld.display(value);
}
}
// BackArrw Long Press
if ((mouseX >= _backArrw.x) && (mouseX <= _backArrw.x + _backArrw.w) && (mouseY >= _backArrw.y) && (mouseY <= _backArrw.y + _backArrw.h)) {
if (mousePressed == true) {
_backArrw.press();
value--;
if (value < _minValue) {
value = _minValue;
}
_valueFld.display(value);
}
}
}
A revision of your code follows;
int x=75;
void setup() {
size(600, 400);
}
void draw() {
background(100);
fill (200);
rect (75, 25, 400, 50); // slider bar
stroke(0);
fill(127, 0, 0);
rect (x, 20, 9, 60); // slider thumb
fill (200);
rect (10, 25, 50, 50); // back arrow
fill(0);
triangle (50, 60, 50, 40, 15, 50);
if ((mouseX >= 10) && (mouseX <= 10 + 50) && (mouseY >= 25) && (mouseY <= 25 + 50) ) {
if (mousePressed == true) {
fill(255);
triangle (50, 60, 50, 40, 15, 50);
x--;
if (x<75) {
x = 75; // minValue
}
} else {
fill(0);
triangle (50, 60, 50, 40, 15, 50);
}
}
fill (200);
rect (490, 25, 50, 50); //forward arrow
fill(0);
triangle (500, 60, 500, 40, 535, 50);
if ((mouseX >= 490) && (mouseX <= 490 + 50) && (mouseY >= 25) && (mouseY <= 25 + 50) ) {
if (mousePressed == true) {
fill(255);
triangle (500, 60, 500, 40, 535, 50);
x++;
if (x>466) {
x = 466; // maxValue
}
} else {
fill(0);
triangle (500, 60, 500, 40, 535, 50);
}
}
}
You've got part of the logic right for the slider/trackbar so it changes x only within a range. This happens horizontally only at this stage, but you can use the same logic to check horizontal limits as well. Similarly, you can check if the cursor is within the bounds of any rectangle (be it the slider or either of the buttons):
int x=75;
void setup() {
size(600, 400);
}
void draw() {
background(100);
// slider
fill (200);
rect (75, 25, 400, 50);
stroke(0);
if (mousePressed) {
if (mouseX >75 && mouseX <= 475)
{
x=mouseX;
}
}
fill(127, 0, 0);
rect (x, 20, 9, 60);
fill (255);
// left arrow button
fill (200);
rect (10, 25, 50, 50);
fill(0);
if (mousePressed == true) {
if (mouseX > 10 && mouseX <= 10 + 50 && mouseY > 25 && mouseY <= 25 + 50){
fill(255);
}
}
triangle (50, 60, 50, 40, 15, 50);
// right arrow button
fill (200);
rect (490, 25, 50, 50);
fill(0);
if (mousePressed == true) {
if (mouseX > 490 && mouseX <= 490 + 50 && mouseY > 25 && mouseY <= 25 + 50){
fill(255);
}
}
triangle (500, 60, 500, 40, 535, 50);
println(x);
}
Wouldn't it be nice if you could take that logic and instead of copy/pasting the different x,y,width,height parameters for the same 4 statements you could group that functionality in a reusable block of code ?
That what functions are for. You're already using them already (defining setup()/draw(), calling background()/fill()/etc.
The Processing Button example already provides the boolean overRect(int x, int y, int width, int height) function which is perfect for you're trying to achieve: pass in the x,y,width,height or a button and get back boolean value.
Here's your code using the overRect():
int x=75;
void setup() {
size(600, 400);
}
void draw() {
background(100);
// slider
fill (200);
rect (75, 25, 400, 50);
stroke(0);
if (mousePressed) {
if (mouseX >75 && mouseX <= 475)
{
x=mouseX;
}
}
fill(127, 0, 0);
rect (x, 20, 9, 60);
fill (255);
// left arrow button
fill (200);
rect (10, 25, 50, 50);
fill(0);
if (mousePressed && overRect(10, 25, 50, 50)) {
fill(255);
x--;
}
triangle (50, 60, 50, 40, 15, 50);
// right arrow button
fill (200);
rect (490, 25, 50, 50);
fill(0);
if (mousePressed && overRect(490, 25, 50, 50)){
fill(255);
x++;
}
triangle (500, 60, 500, 40, 535, 50);
// ensure x remains within the slide limits
x = constrain(x, 75, 475);
println(x);
}
boolean overRect(int x, int y, int width, int height) {
if (mouseX >= x && mouseX <= x+width &&
mouseY >= y && mouseY <= y+height) {
return true;
} else {
return false;
}
}

Need help adding a class and animations to a simple pet drawing?

float x = 100;
float y = 100;
float p = 150;
float l = 10;
float a = 100;
float b = 100;
float n =20;
int value = 255;
int r = 150;
int t = 100;
int s = 100;
int w = 60;
int h = 60;
int z = 11;
int eyeSize = 10;
int pigNose = 30;
int pigBody = 30;
int pigEars = 35;
int pigTail = 20;
int otherpigTail = 200;
int speed = 1;
void setup () {
size (600, 600);
a = width/2.5;
b = height/2;
}
void draw() {
background(184, 233, 249);
//Draw legs
stroke(0);
fill(249, 137, 244);
rect(x+(2*w), y+h/3.5, z, 2*z);
rect(x+(w), y+h/3, z, 2*z);
rect(x+(1.5*w), y+h/3, z, 2*z);
rect(x+(2.5*w), y+h/3.5, z, 2*z);
////draw body
stroke(0);
fill(249, 137, 244);
ellipse(110+x,y-pigBody, p, p-20);
//draw tail
fill(0);
line(185+x, y-pigTail, x+otherpigTail, y-(2*pigTail));
// Draw payer's head
fill(249, 137, 244);
ellipse(x,y-pigNose,t,t);
// Draw player's eyes
fill(0);
ellipse(x-w/3+1,y-h/2,eyeSize,eyeSize);
ellipse(x+w/3-1,y-h/2,eyeSize,eyeSize);
//Draw nose
stroke(0);
fill(198, 105, 194);
ellipse(x, y, pigNose, pigNose);
//draw ears
stroke(0);
fill(198, 105, 194);
ellipse(x-(w/2),y-h, pigEars, pigEars);
ellipse(x+(w/2),y-h, pigEars, pigEars);
//draw obstacles
fill(value);
ellipse(a, b, s, s);
ellipse(300+a, 200+b, s, s);
ellipse(300-a, 400+b, s, s);
ellipse(300-a, 600+b, s, s);
ellipse(300-a, b, s, s);
ellipse(300+a, 800+b, s, s);
}
I need help turning this code into something similar to this:
/*
This is a very rudimentary virtual pet. It can sit,
lie down, and wag it's tail.
*/
class Pet {
int x, y;
int pose;
int WAG = 1, SLEEP = 2, SIT = 3;
float tailWag, wagSpeed;
Pet(int x, int y) {
this.x = x;
this.y = y;
pose = SIT;
}
// adjust pose and stop tail wagging
void sit() {
pose = SIT;
wagSpeed = 0;
tailWag = 0;
}
// adjust pose and start tail wagging
void wag() {
pose = WAG;
wagSpeed = .1;
}
// adjust pose and stop tail wagging
void sleep() {
pose = SLEEP;
wagSpeed = 0;
tailWag = 0;
}
// draw in selected pose
void draw() {
pushMatrix();
translate(x, y);
if (pose == SIT) {
drawSitting();
}
else if (pose == WAG) {
wagTail();
drawSitting();
}
else {
drawLaying();
}
popMatrix();
}
void drawLaying() {
// needs work :-)
ellipse(0, 0, 150, 60);
}
void wagTail() {
float maxTailWag = .5; // controls how much the tail wags back and forth
tailWag = tailWag + wagSpeed;
// reverse wag direction if the wag limit is reached
if (tailWag > maxTailWag || tailWag < -maxTailWag) {
wagSpeed = -wagSpeed;
}
}
// not pretty but gets the idea across
// origin is the center of the torso
void drawSitting() {
// torso
pushMatrix();
rotate(radians(-30));
ellipse(0, 0, 80, 120);
popMatrix();
ellipse(-20, -70, 60, 60); // head
// nose
pushMatrix();
translate(-55, -55);
rotate(radians(-15));
arc(0, 0, 40, 30, radians(20), radians(310), OPEN);
popMatrix();
// eyes
ellipse(-40, -85, 15, 15); // left eye
ellipse(-25, -80, 15, 15); // right eye
//ear
pushMatrix();
translate(15, -50);
rotate(radians(-20));
ellipse(0, 0, 20, 40);
popMatrix();
//tail
pushMatrix();
translate(40, 30);
rotate(radians(45)+tailWag);
arc(0, -35, 30, 60, radians(-220)-tailWag, radians(80), OPEN);
popMatrix();
// back leg
ellipse(0, 60, 50, 20);
// front leg
pushMatrix();
translate(-50, 30);
rotate(radians(15));
ellipse(0, 0, 30, 60);
popMatrix();
}
}
with classes and whatnot so that I can start working on adding in my own animations for the pet. I'm just not sure where to put everything/how to organize it like that using my drawing.
If I were you, I would start with something simpler. For example, here's a program that uses 4 variables to show a ball bouncing around:
float circleX = 50;
float circleY = 50;
float xSpeed = 1;
float ySpeed = 2;
void draw() {
background(200);
circleX += xSpeed;
if (circleX < 0 || circleX > width) {
xSpeed *= -1;
}
circleY += ySpeed;
if (circleY < 0 || circleY > height) {
ySpeed *= -1;
}
ellipse(circleX, circleY, 20, 20);
}
(source: happycoding.io)
From here, we can encapsulate those 4 variables into a class:
class Circle{
float x;
float y;
float xSpeed;
float ySpeed;
Circle(float x, float y, float xSpeed, float ySpeed){
this.x = x;
this.y = y;
this.xSpeed = xSpeed;
this.ySpeed = ySpeed;
}
}
Now that we have a class, we can use an instance of that class to control our ball.
Circle circle = new Circle(50, 50, 1, 2);
void draw() {
background(200);
circle.x += circle.xSpeed;
if (circle.x < 0 || circle.x > width) {
circle.xSpeed *= -1;
}
circle.y += circle.ySpeed;
if (circle.y < 0 || circle.y > height) {
circle.ySpeed *= -1;
}
ellipse(circle.x, circle.y, 20, 20);
}
class Circle{
float x;
float y;
float xSpeed;
float ySpeed;
Circle(float x, float y, float xSpeed, float ySpeed){
this.x = x;
this.y = y;
this.xSpeed = xSpeed;
this.ySpeed = ySpeed;
}
}
Your code would follow a similar pattern: create a class, encapsulate your data by moving your variables inside the class, and then create an instance of that class and call its methods to draw your figure. Start with something simpler, and just create a class that draws a single circle. Get that working first, and then add variables to that class to draw two circles (a head and a body), and keep working in small steps like that until you're drawing your whole figure.
I really suggest trying something out and posting an MCVE if you get stuck. Good luck.
Shameless self-promotion: I've written a tutorial on creating classes in Processing available here.

Graphics keep resetting in JFrame

I recently started with Java and now I got stuck with a simple project. I want to draw a line and that works, but when i draw another line the first line disappears. I have no idea how to get it so I can keep drawing lines.
This is my code:
package com.example.paint;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
public class ClickEvent extends MouseAdapter {
private Paint paint;
boolean click = false;
boolean clear = false;
int startX;
int startY;
int endX;
int endY;
int firstTime = 1;
public ClickEvent(Paint paint, Handler handler, Line line) {
this.paint = paint;
}
public void mousePressed(MouseEvent e) {
int mx = e.getX();
int my = e.getY();
if (mouseOver(mx, my, 750, 560, 83, 40)) {
clear = true;
}
if (mouseOver(mx, my, 0, 0, 40, 40)) {
paint.color = "Black";
}
if (mouseOver(mx, my, 0, 40, 40, 40)) {
paint.color = "Blue";
}
if (mouseOver(mx, my, 0, 80, 40, 40)) {
paint.color = "Green";
}
if (mouseOver(mx, my, 0, 120, 40, 40)) {
paint.color = "Red";
}
if (!mouseOver(mx, my, 0, 0, 40, 160) && !mouseOver(mx, my, 750, 560, 83, 40)) {
clear = false;
startX = mx;
startY = my;
click = true;
}
}
public void mouseReleased(MouseEvent e) {
int mx = e.getX();
int my = e.getY();
if (!mouseOver(mx, my, 0, 0, 40, 160) && !mouseOver(mx, my, 750, 560, 83, 40)) {
endX = mx;
endY = my;
click = false;
}
}
private boolean mouseOver(int mx, int my, int x, int y, int width, int height) {
if (mx > x && mx < x + width) {
if (my > y && my < y + height) {
return true;
} else
return false;
} else
return false;
}
public void tick() {
}
public void render(Graphics g) {
Font font = new Font("Arial", 1, 30);
Font font1 = new Font("Arial", 1, 13);
g.setColor(Color.black);
g.fillRect(0, 0, 40, 40);
g.setColor(Color.blue);
g.fillRect(0, 40, 40, 40);
g.setColor(Color.green);
g.fillRect(0, 80, 40, 40);
g.setColor(Color.red);
g.fillRect(0, 120, 40, 40);
g.setColor(Color.black);
g.drawRect(750, 560, 83, 40);
g.setFont(font);
g.drawString("Clear", 755, 590);
g.setFont(font1);
if (paint.color == "Red") {
g.setColor(Color.red);
g.drawString("Color: " + paint.color, 745, 15);
} else if (paint.color == "Blue") {
g.setColor(Color.blue);
g.drawString("Color: " + paint.color, 745, 15);
} else if (paint.color == "Green") {
g.setColor(Color.green);
g.drawString("Color: " + paint.color, 745, 15);
} else if (paint.color == "Black") {
g.setColor(Color.black);
g.drawString("Color: " + paint.color, 745, 15);
} else {
g.setColor(Color.black);
g.drawString("Color: ", 745, 15);
}
if (!clear) {
if ("Red".equals(paint.color) && !click) {
g.setColor(Color.red);
g.drawLine(startX, startY, endX, endY);
} else if ("Green".equals(paint.color) && !click) {
g.setColor(Color.green);
g.drawLine(startX, startY, endX, endY);
} else if ("Blue".equals(paint.color) && !click) {
g.setColor(Color.blue);
g.drawLine(startX, startY, endX, endY);
} else if ("Black".equals(paint.color) && !click) {
g.setColor(Color.black);
g.drawLine(startX, startY, endX, endY);
}
}
}
}
Some of the code is part of a basic tutorial, but the rest I tried to do myself. If anyone knows how I can get it to keep drawing lines or if anyone has any other improvements I would really appreciate it.
The problem is that you're only saving one line to draw later, in the form of startX, startY, endX, endY.
Rather you could, at the end of each click, save the line as a new line object. Something like this:
class MyLine {
private final int startX, startY;
private final int endX, endY;
private final Color color;
public MyLine(int startX, int startY, int endX, int endY, Color color) {
this.startX = startX;
this.startY = startY;
this.endX = endX;
this.endY = endY;
this.color = color;
}
public void draw(Graphics g) {
g.setColor(color);
g.drawLine(startX, startY, endX, endY);
}
}
Add a list as a field to your class:
List<MyLine> lineList = new ArrayList<>();
And then, in the mouseReleased method, add a new Line to the list:
if (!mouseOver(mx, my, 0, 0, 40, 160) && !mouseOver(mx, my, 750, 560, 83, 40)) {
endX = mx;
endY = my;
click = false;
lineList.add(new MyLine(startX, starY, endX, endY, paint.color));
}
Additionally, you would have to change Paint, so that it holds the color as a Color object:
class Paint {
public Color color;
...
}
~~~~
paint.color = Color.black;
Which is much easier than saving strings.
Finally, loop over all of your lines to draw them, within your render method:
for(MyLine l : lineList) {
l.draw(g);
}

Using JSlider to Scale Drawing

First question ever here. I'm trying to scale a custom drawing with JSlider. However, it doesn't do anything and I cannot for the life of me figure out why. My code grabs a custom shape and draws it fine initially, but it won't scale.
class DrawFrame extends JFrame {
private int CarWidth = 50;
private CarShape shape = new CarShape(150, 150, CarWidth);
public DrawFrame()
{
setTitle("Draw a Car");
setSize(400, 400);
JSlider slider = new JSlider(JSlider.VERTICAL, 1, 100, 50);
slider.setMajorTickSpacing(5);
slider.setPaintTicks(true);
slider.addChangeListener((new ChangeListener() {
public void stateChanged(ChangeEvent e) {
JSlider source = (JSlider) e.getSource();
int x = (int)source.getValue();
CarWidth = x;
repaint();
}
}));
add(slider, BorderLayout.WEST);
add(shape);
}
}
public class CarShape extends JPanel {
private int x;
private int y;
private int width;
public CarShape(int x, int y, int width)
{
this.x = x;
this.y = y;
this.width = width;
}
public void update(int x){
x = width;
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
Rectangle2D.Double body
= new Rectangle2D.Double(x, y + width / 6,
width - 1, width / 6);
Ellipse2D.Double frontTire
= new Ellipse2D.Double(x + width / 6, y + width / 3,
width / 6, width / 6);
Ellipse2D.Double rearTire
= new Ellipse2D.Double(x + width * 2 / 3, y + width / 3,
width / 6, width / 6);
// The bottom of the front windshield
Point2D.Double r1
= new Point2D.Double(x + width / 6, y + width / 6);
// The front of the roof
Point2D.Double r2
= new Point2D.Double(x + width / 3, y);
// The rear of the roof
Point2D.Double r3
= new Point2D.Double(x + width * 2 / 3, y);
// The bottom of the rear windshield
Point2D.Double r4
= new Point2D.Double(x + width * 5 / 6, y + width / 6);
Line2D.Double frontWindshield
= new Line2D.Double(r1, r2);
Line2D.Double roofTop
= new Line2D.Double(r2, r3);
Line2D.Double rearWindshield
= new Line2D.Double(r3, r4);
g2.draw(body);
g2.draw(frontTire);
g2.draw(rearTire);
g2.draw(frontWindshield);
g2.draw(roofTop);
g2.draw(rearWindshield);
}
}
public class SliderTester {
public static void main(String[] args)
{
DrawFrame frame = new DrawFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
Every time the change listener is called, it creates a new CarShape object, but this has no effect on the CarShape object that is displayed. Better perhaps would be to resize the visualized object... OK, did you just change the code on me or am I imagining things?
Now you're changing CarWidth (which should be re-named carWidth), but that's not going to change the state of the visualized CarShape object. Instead give your CarShape class a setCarWidth(int width) method, one that changes its state, and then call that method within your stateChange method.
e.g.,
import java.awt.*;
import java.awt.geom.*;
import javax.swing.*;
import javax.swing.event.*;
#SuppressWarnings("serial")
class DrawFrame extends JFrame {
private int carWidth = 50;
private CarShape shape = new CarShape(150, 150, carWidth);
public DrawFrame() {
setTitle("Draw a Car");
setSize(400, 400);
JSlider slider = new JSlider(JSlider.VERTICAL, 1, 100, 50);
slider.setMajorTickSpacing(5);
slider.setPaintTicks(true);
slider.addChangeListener((new ChangeListener() {
public void stateChanged(ChangeEvent e) {
JSlider source = (JSlider) e.getSource();
carWidth = (int) source.getValue();
shape.setCarWidth(carWidth);
repaint();
}
}));
add(slider, BorderLayout.WEST);
add(shape);
}
}
#SuppressWarnings("serial")
class CarShape extends JPanel {
private int x;
private int y;
private int width;
public CarShape(int x, int y, int width) {
this.x = x;
this.y = y;
this.width = width;
}
public void setCarWidth(int w) {
this.width = w;
}
// this method is just messed up -- you're setting the parameter!
public void update(int x) {
x = width; // no!!!
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
Rectangle2D.Double body = new Rectangle2D.Double(x, y + width / 6,
width - 1, width / 6);
Ellipse2D.Double frontTire = new Ellipse2D.Double(x + width / 6, y
+ width / 3, width / 6, width / 6);
Ellipse2D.Double rearTire = new Ellipse2D.Double(x + width * 2 / 3, y
+ width / 3, width / 6, width / 6);
// The bottom of the front windshield
Point2D.Double r1 = new Point2D.Double(x + width / 6, y + width / 6);
// The front of the roof
Point2D.Double r2 = new Point2D.Double(x + width / 3, y);
// The rear of the roof
Point2D.Double r3 = new Point2D.Double(x + width * 2 / 3, y);
// The bottom of the rear windshield
Point2D.Double r4 = new Point2D.Double(x + width * 5 / 6, y + width / 6);
Line2D.Double frontWindshield = new Line2D.Double(r1, r2);
Line2D.Double roofTop = new Line2D.Double(r2, r3);
Line2D.Double rearWindshield = new Line2D.Double(r3, r4);
g2.draw(body);
g2.draw(frontTire);
g2.draw(rearTire);
g2.draw(frontWindshield);
g2.draw(roofTop);
g2.draw(rearWindshield);
}
}
public class SliderTester {
public static void main(String[] args) {
DrawFrame frame = new DrawFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}

Transparent circular dialog in Java

I'm building a Poker Odds Calc app in Java. I want to select a new card by clicking the card's placeholder which is basically an extended JPanel that I "draw" the card's face and has a mouseListener.
What I have imagined to do is that when I clicked the card, I would like a round menu to pop up around the mouse cursor having a circle in the middle cut in four with each suite in a quarter and a ring around it cut in thirteen for the value of the card. Then I will select suit and value and it would disappear. Do you know any way I could do this? I researched a bit and I think it can be done with JavaFX by making a transparent JDialog but I'm not sure.
Is there a way to draw a totally custom shaped JComponent like a JButton shaped for each quarter of the circle etc.? I have some experience in Java but not GUI building.
Thanks in advance for your time.
edit: Used your comment and have answered my question about the circular dialog (don't know if it's the best way to do it but works for now). Now, is there anyway I know in which area the click belongs (if the click was on a useful area) without hardcoding the coordinates?
I would suggest doing custom graphics rather than trying to customize JButton and so on. When you click on the JPanel you can draw the circle and so on using the java.awt.Shape interfaces and its various implementations such as java.awt.geom.Ellipse2D.
These shapes come with contains() method that can tell you if a point is in the Shape or not. This way, when the user next clicks on the JPanel, you can determine which shape the user clicked on by going through all the shapes and checking.
The code to create the graphics is this in case anyone needs it:
import java.awt.Color;
import java.awt.Font;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Paint;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D.Double;
import javax.swing.JDialog;
/**
*
* #author Dimitris Klimis <dnklimis at gmail.com>
*/
public class CardChooser extends JDialog implements MouseListener {
int sizeX = 140;
int sizeY = sizeX; //in case I don't want it to be circle
int x, y;
Point point;
public CardChooser(Point point) {
x = point.x;
y = point.y;
this.point = point;
this.initComponents();
}
public static int[] getCard(Point point) {
int[] output = {0, 0};
CardChooser chooser = new CardChooser(point);
return output;
}
#Override
public void paint(Graphics g) {
if (g instanceof Graphics2D) {
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
//Drawing the transparent dialog
g2.setPaint(new Color(0.0f, 0.0f, 0.0f, 0.0f));
g2.fillRect(0, 0, getWidth(), getHeight());
//Drawing the circles
g2.setColor(Color.BLACK);
drawCircle(g2, 100, new GradientPaint(0.0f, 0.0f, Color.darkGray, (float) getWidth(), (float) getHeight(), Color.lightGray, false));
drawLines(g2, 13, 100);
int smallCircle = 38;
drawCircle(g2, smallCircle + 3, Color.GRAY);
drawCircle(g2, smallCircle, new GradientPaint((float) (getWidth() * 0.25), (float) (getHeight() * 0.25), Color.lightGray, (float) (getWidth() * 0.75), (float) (getHeight() * 0.75), Color.darkGray, false));
drawLines(g2, 4, smallCircle);
drawCircle(g2, 10, Color.LIGHT_GRAY);
drawSuiteLetters(g2);
drawCardValues(g2);
drawClosingX(g2);
} else {
super.paint(g);
}
}
private void drawCircle(Graphics2D g2, int percentage, Paint fill) {
double perc = (double) percentage / 100.0;
Ellipse2D ellipse = new Ellipse2D.Double(((1 - perc) / 2) * sizeX, ((1 - perc) / 2) * sizeY, perc * sizeX, perc * sizeY);
g2.setPaint(fill);
g2.fill(ellipse);
g2.setColor(Color.BLACK);
g2.draw(ellipse);
}
private void drawLines(Graphics2D g2, int outOf, int percentage) {
double rads = Math.toRadians(360.0 / outOf);
double perc = (double) percentage / 100.0;
Double zeroAxis = new Point.Double(sizeX / 2.0, sizeY / 2.0);
for (int i = 0; i < outOf; i++) {
g2.draw(new Line2D.Double(zeroAxis.x, zeroAxis.y, zeroAxis.x + (zeroAxis.x * perc * Math.sin(rads * i)), zeroAxis.y + (zeroAxis.y * perc * Math.cos(rads * i))));
}
}
private void drawSuiteLetters(Graphics2D g2) {
Double zeroAxis = new Point.Double(sizeX / 2.0, sizeY / 2.0);
g2.setFont(new Font("Courier New", Font.BOLD, 25));
g2.drawString("\u2660", (float) zeroAxis.x - 18, (float) zeroAxis.y - 5);//spades
g2.drawString("\u2663", (float) zeroAxis.x + 3, (float) zeroAxis.y + 20);//clubs
g2.setColor(Color.RED);
g2.drawString("\u2665", (float) zeroAxis.x + 3, (float) zeroAxis.y - 3);//hearts
g2.drawString("\u2666", (float) zeroAxis.x - 18, (float) zeroAxis.y + 19);//diamonds
g2.setColor(Color.BLACK);
}
private void drawCardValues(Graphics2D g2) {
Double zeroAxis = new Point.Double((sizeX / 2.0) - 8, 21);
float xx = (float) zeroAxis.x;
float yy = (float) zeroAxis.y;
g2.setFont(new Font("Arial", Font.BOLD, 24));
String[] letters = {"A", "K", "Q", "J", "T", "9", "8", "7", "6", "5", "4", "3", "2"};
float[] xPosition = {0, 25, 46, 63, 58, 42, 15, -10, -37, -53, -58, -46, -25};
float[] yPosition = {0, 7, 23, 50, 80, 102, 115, 115, 102, 80, 50, 23, 7};
for (int i = 0; i < 13; i++) {
g2.drawString(letters[i], xx + xPosition[i], yy + yPosition[i]);
}
}
private void drawClosingX(Graphics2D g2) {
Double zeroAxis = new Point.Double(sizeX / 2.0, sizeY / 2.0);
g2.draw(new Line2D.Double(zeroAxis.x - 5, zeroAxis.y - 5, zeroAxis.x + 5, zeroAxis.y + 5));
g2.draw(new Line2D.Double(zeroAxis.x - 5, zeroAxis.y + 5, zeroAxis.x + 5, zeroAxis.y - 5));
}
private void initComponents() {
this.addMouseListener(this);
this.setBounds(x - (sizeX / 2), y - (sizeY / 2), sizeX + 1, sizeX + 1);
this.setUndecorated(true);
this.setModal(true);
this.setDefaultCloseOperation(DISPOSE_ON_CLOSE);
this.setVisible(true);
}
public void mouseClicked(MouseEvent e) {
this.dispose();
}
public void mousePressed(MouseEvent e) {
}
public void mouseReleased(MouseEvent e) {
}
public void mouseEntered(MouseEvent e) {
}
public void mouseExited(MouseEvent e) {
}
}
PS. I extended JDialog cause I couldn't get JPanel to show up...

Categories