I am trying to create a class in Java which will add a simple box shadow to any given UI element. Normally, it displays correctly, however when it is applied to a button, the shadow gets darker each time the mouse enters or exits the button.
The paint function is as follows:
public void paint(Graphics g)
{
Rectangle bounds = g.getClipBounds();
g.setClip(bounds.x + (SHADOW_OFFSET + shadowOffsetMod) - (SHADOW_SIZE + shadowSizeMod), bounds.y + (SHADOW_OFFSET + shadowOffsetMod) - (SHADOW_SIZE + shadowSizeMod),
bounds.width + 2 * (SHADOW_SIZE + shadowSizeMod), bounds.height + 2 * (SHADOW_SIZE + shadowSizeMod));
int red = UIConstants.SHADOW_COLOR.getRed();
int green = UIConstants.SHADOW_COLOR.getGreen();
int blue = UIConstants.SHADOW_COLOR.getBlue();
int alpha = UIConstants.SHADOW_COLOR.getAlpha();
for (int x = (SHADOW_OFFSET + shadowOffsetMod) - (SHADOW_SIZE + shadowSizeMod); x < bounds.width + (SHADOW_OFFSET + shadowOffsetMod) + (SHADOW_SIZE + shadowSizeMod); x++)
{
for (int y = (SHADOW_OFFSET + shadowOffsetMod) - (SHADOW_SIZE + shadowSizeMod); y < bounds.width + (SHADOW_OFFSET + shadowOffsetMod) + (SHADOW_SIZE + shadowSizeMod); y++)
{
int dx = Math.abs(x - Math.max(Math.min(x, bounds.width + (SHADOW_OFFSET + shadowOffsetMod)), (SHADOW_OFFSET + shadowOffsetMod)));
int dy = Math.abs(y - Math.max(Math.min(y, bounds.height + (SHADOW_OFFSET + shadowOffsetMod)), (SHADOW_OFFSET + shadowOffsetMod)));
float dist = (float) Math.sqrt(dx * dx + dy * dy);
dist /= (SHADOW_SIZE + shadowSizeMod);
if (dist > 1)
dist = 1;
dist = 1 - dist;
g.setColor(new Color(red, green, blue, ((float)alpha * dist) / 255));
g.fillRect(x, y, 1, 1);
}
}
g.setClip(bounds);
super.paint(g);
}
the shadow gets darker each time the mouse enters or exits the button.
Not sure exactly how your code is used. But (assuming you are usign Swing) this is a problem resulting from using a background color with transparency.
Swing components are either
opaque, in which case they guarantee to paint the background
non-opaque, in which case the parent component is used to paint the background.
In your case it sounds like the component is opaque, but the background is transparent so you break rule and you have painting issues.
Check out Backgrouds With Transparency fore more information and a couple of solutions:
make the component non-opaque and paint the background yourself
use a custom component as a wrapper to paint the background for you.
Related
I need to fill with color an area of given polygon on a custom view. I can't find a method with which I can use for this matter.
I can actually only draw lines connecting those point but I'm unsure how to fill the area surrounded by them.
The following code I use to draw lines:
for (Path p : paths) {
Paint color = p.getColor();
float strokeW = color.getStrokeWidth();
color.setStrokeWidth(pthStrokeWdth);
List<Point> currentPath = p.getCoordinateList();
for (int i = 1; i < currentPath.size(); i++) {
float startX = currentPath.get(i - 1).x;
float startY = currentPath.get(i - 1).y;
float endX = currentPath.get(i).x;
float endY = currentPath.get(i).y;
canvas.drawLine((startX * cellWidth) + xOffset + halfCellWidth, (startY * cellHeight) + yOffset + halfCellHeight,
(endX * cellWidth) + xOffset + halfCellWidth, (endY * cellHeight) + yOffset + halfCellHeight, color);
}
color.setStrokeWidth(originalStrokeWidth);
}
The above draw lines between every point forming polygons with diverse shapes. See below screenshot.
So I would likne to fill the area inside the polygone like below:
Thanks for your time.
You need to set the Paint.Style to FILL or FILL_AND_STROKE
paint.setStyle(Paint.Style.FILL);
Edit:
Paint color = p.getColor();
float strokeW = color.getStrokeWidth();
color.setStrokeWidth(pthStrokeWdth);
color.setStyle(Paint.Style.FILL);
List<Point> currentPath = p.getCoordinateList();
//for each path we want to draw a line which consist of 4 points
float[] points = new float[currentPath.size() * 4];
int count = 0;
for (int i = 1; i < currentPath.size(); i++) {
float startX = currentPath.get(i - 1).x;
float startY = currentPath.get(i - 1).y;
float endX = currentPath.get(i).x;
float endY = currentPath.get(i).y;
points[count++] = (startX * cellWidth) + xOffset + halfCellWidth;
points[count++] = (startY * cellHeight) + yOffset + halfCellHeight;
points[count++] = (endX * cellWidth) + xOffset + halfCellWidth;
points[count++] = (endY * cellHeight) + yOffset + halfCellHeight, color;
}
canvas.drawLines(points, color);
I am trying to draw an arc on Jpanel in swing from user input having the center of arc, starting point and end point of arc.
here is my current
int x1 = 300; //start point
int y1 = 300;
int x2 = 350; //center point of arc
int y2 = 350;
int x3 = 300; //end point of arc
int y3 = 400;
int h1 = y1 - y2; //calculate with and height from start-center and center-end
int d1 = x2 - x1;
int h2 = y2 - y3;
int d2 = x3 - x2;
int startangle = (int)(Math.atan(h1 / d1) * 180 / Math.PI);
if (x2 > x1 && y2 > y1) {
startangle = 180 - startangle;
} else if (x2 < x1) {
//change sign
} else if (y1 < y2) {
//change sign
}
System.out.println("x1,y1\n" + x1 + "\n" + y1 + "\n" + d2 / h2 + "\n" + Math.atan(d2 / h2) * 180 / Math.PI);
int endangle = (int)(Math.atan2(x3, y3) * 180 / Math.PI);
System.out.println("args: " + "\n" + x2 + "\n" + y2 + "\n" + startangle + "\n" + endangle + "\n");
g2.drawArc(x1, y1, d1, h1, startangle, startangle);
g2.drawArc(x2, y2, d2, h2, 0, endangle);
However i am not getting the arc on screen, literally nothing related to it (other shapes work but not this one). No errors or exceptions were thrown.
Edit: Thanks to #MadProgrammer's comment, i am getting a shape but not what i expect.
What i get:
What i expect from the same set of coordinates:
Edit 2: managed to make it work by using a bezier curve instead of an arc
It worked by using a bezier curve and drawing quadcurve in two phases (start-middle,middle-end) using the calculated control points instead of the drawArc method.
I think the bounding rectangle of drawarc is the height and width of the ellipse that your arc is part of.
I am working on a Pong-like game, and have implemented the code that moves the paddle (via user input) and the ball. I am currently working on the code for the collision between the ball and the paddle.
Everything seems to be working fine, except when I move the paddle directly onto the ball. This means that the ball collides with the bottom part or top part of the paddle, instead of the side of the paddle, which results in the ball getting stuck inside the paddle and stuttering rapidly.
I know that the motion is caused by the ball changing velocity every frame. I am still having trouble figuring out how to solve it.
I have tried implement a boolean variable to determine whether or not the ball already collided with the paddle during a frame. However, I am having some trouble going about implementing this logic into my code. In my main class, you will see some comments pointing out this boolean variable and what I intended to do with it.
Below are some of the major code snippets pertaining to my problem.
MainApp:
// ...
new AnimationTimer() {
#Override
public void handle(long currentTime) {
ballInfo.setText("Ball (" + ball.getX() + ", " + ball.getY() + ")");
leftPaddleInfo.setText("Left Paddle (" + leftPaddle.getX() + ", " + leftPaddle.getY() + ")");
rightPaddleInfo.setText("Right Paddle (" + rightPaddle.getX() + ", " + rightPaddle.getY() + ")");
// Give point and reset ball.
if (ball.didCollideWithLeftWall(canvas)) {
givePoint(rightPaddle);
rightPaddleScore = rightPaddle.getPoints();
rightPaddleScoreLabel.setText("Player 2 Score: " + rightPaddleScore);
reset();
}
if (ball.didCollideWithRightWall(canvas)) {
givePoint(leftPaddle);
leftPaddleScore = leftPaddle.getPoints();
leftPaddleScoreLabel.setText("Player 1 Score: " + leftPaddleScore);
reset();
}
// Handle bouncing off of top and bottom walls
ball.didCollideWithWalls(canvas);
// Keep paddle in screen bounds
if (leftPaddle.getY() <= canvasBounds.getMinY() + 5)
leftPaddle.setY(canvasBounds.getMinY() + 5);
if (leftPaddle.getY() >= canvasBounds.getMaxY() - 105)
leftPaddle.setY(canvasBounds.getMaxY() - 105);
if (rightPaddle.getY() <= canvasBounds.getMinY() + 5)
rightPaddle.setY(canvasBounds.getMinY() + 5);
if (rightPaddle.getY() >= canvasBounds.getMaxY() - 105)
rightPaddle.setY(canvasBounds.getMaxY() - 105);
if (leftPaddle.didCollideWith(ball)) {
ball.setvX(-ball.getvX());
ball.setX(ball.getX() + ball.getvX());
}
if (rightPaddle.didCollideWith(ball)) {
ball.setvX(-ball.getvX());
ball.setX(ball.getX() + ball.getvX());
}
// Update and render
ball.update(ELAPSED_TIME_SPEED);
leftPaddle.update(ELAPSED_TIME_SPEED);
rightPaddle.update(ELAPSED_TIME_SPEED);
gc.clearRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT);
gc.setFill(BALL_COLOR);
ball.render(gc);
gc.setFill(LEFT_PADDLE_COLOR);
leftPaddle.render(gc);
gc.setFill(RIGHT_PADDLE_COLOR);
rightPaddle.render(gc);
}
}.start();
Paddle:
#Override
public boolean didCollideWith(Sprite other) {
Ball ball = (Ball) other;
double ballCenterX = ball.getCenterX();
double ballRadius = ball.getRadius();
double ballCenterY = ball.getCenterY();
double halfWidth = this.getHalfWidth();
double halfHeight = this.getHalfHeight();
double centerX = this.getCenterX();
double centerY = this.getCenterY();
if (getName().toLowerCase().equals("left")) {
boolean hitXBounds = ballCenterX - ballRadius <= centerX + halfWidth
&& ballCenterX - ballRadius >= centerX - halfWidth;
boolean hitTopPartOfBall = ballCenterY - ballRadius <= centerY + halfHeight
&& ballCenterY - ballRadius >= centerY - halfHeight;
boolean hitBotPartOfBall = ballCenterY + ballRadius <= centerY + halfHeight
&& ballCenterY + ballRadius >= centerY - halfHeight;
return hitXBounds && (hitTopPartOfBall || hitBotPartOfBall);
}
if (getName().toLowerCase().equals("right")) {
boolean hitXBounds = ballCenterX + ballRadius >= centerX - halfWidth
&& ballCenterX + ballRadius <= centerX + halfWidth;
boolean hitTopPartOfBall = ballCenterY - ballRadius <= centerY + halfHeight
&& ballCenterY - ballRadius >= centerY - halfHeight;
boolean hitBotPartOfBall = ballCenterY + ballRadius <= centerY + halfHeight
&& ballCenterY + ballRadius >= centerY - halfHeight;
return hitXBounds && (hitTopPartOfBall || hitBotPartOfBall);
}
return false;
}
Thank you!
EDIT
I have somewhat fixed the collision code in the Paddle class, so that now it is much more difficult to perform the mentioned bug. However, I am still getting the stuttering bug if the, for instance, the leftmost point of the ball comes right into contact with the rightmost point of the left paddle. If I were to time it just right, I can still get the ball stuck inside the paddle the same way as I could before (by moving it directly on top of/ bottom of it), but it will only create this bug IF the ball is still in front of the paddle.
I have changed the code above as well.
I have a method in my project that runs only if both of the following conditions are true: the user presses the E key, and a pixel in the player's immediate vicinity is red on the unseen bitmap. The bitmap governs collision in my game. Here is the method:
private void doors(int x, int y) {
int doorColor = 0xFFFF0000;
int w = 0;
int h = 0;
while (bitmap.getRGB(x - 1, y) == doorColor)
x--;
while (bitmap.getRGB(x, y - 1) == doorColor)
y--;
while (bitmap.getRGB(x + w + 1, y) == doorColor)
w++;
while (bitmap.getRGB(x, y + h + 1) == doorColor)
h++;
for (int x2 = x; x2 < x + w + 1; x2++) {
for (int y2 = y; y2 < y + h + 1; y2++) {
foreground.setRGB(x2, y2, 0x00FFFFFF);
bitmap.setRGB(x2, y2, 0xFFFFFFFF);
}
}
}
}
This method, when given the x and y coordinates of the found red pixel, finds the top left corner of the red rectangle on the bitmap, and then finds the width and height of said rectangle. Once it has done this, it changes the colors of all the pixels in the rectangle to white, and clears the section of the foreground image to give the appearance of an opened door.
This method of opening the door works, but it takes long enough that the game visibly freezes for a few frames. Is there any way to avoid this?
Here is my bitmap:
And here is my foreground image:
So I'm trying to implement a test where a oval can connect with a circle, but it's not working.
edist = (float) Math.sqrt(
Math.pow((px + ((pwidth/2) )) - (bx + (bsize/2)), 2 ) +
Math.pow(-((py + ((pwidth/2)) ) - (bx + (bsize/2))), 2 )
);
and here is the full code (requires Slick2D):
import org.newdawn.slick.AppGameContainer;
import org.newdawn.slick.BasicGame;
import org.newdawn.slick.Color;
import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.Input;
import org.newdawn.slick.SlickException;
public class ColTest extends BasicGame{
float px = 50;
float py = 50;
float pheight = 50;
float pwidth = 50;
float bx = 200;
float by = 200;
float bsize = 200;
float edist;
float pspeed = 3;
Input input;
public ColTest()
{
super("ColTest");
}
#Override
public void init(GameContainer gc)
throws SlickException {
}
#Override
public void update(GameContainer gc, int delta)
throws SlickException
{
input = gc.getInput();
try{
if(input.isKeyDown(Input.KEY_UP))
py-=pspeed;
if(input.isKeyDown(Input.KEY_DOWN))
py+=pspeed;
if(input.isKeyDown(Input.KEY_LEFT))
px-=pspeed;
if(input.isKeyDown(Input.KEY_RIGHT))
px+=pspeed;
}
catch(Exception e){}
}
public void render(GameContainer gc, Graphics g)
throws SlickException
{
g.setColor(new Color(255,255,255));
g.drawString("col: " + col(), 10, 10);
g.drawString("edist: " + edist + " dist: " + dist, 10, 100);
g.fillRect(px, py, pwidth, pheight);
g.setColor(new Color(255,0,255));
g.fillOval(px, py, pwidth, pheight);
g.setColor(new Color(255,255,255));
g.fillOval(200, 200, 200, 200);
}
public boolean col(){
edist = (float) Math.sqrt(Math.pow((px + ((pwidth/2) )) - (bx + (bsize/2)), 2) + Math.pow(-((py + ((pwidth/2)) ) - (bx + (bsize/2))), 2));
if(edist <= (bsize/2) + (px + (pwidth/2)))
return true;
else
return false;
}
public float rotate(float x, float y, float ox, float oy, float a, boolean b)
{
float dst = (float) Math.sqrt(Math.pow(x-ox,2.0)+ Math.pow(y-oy,2.0));
float oa = (float) Math.atan2(y-oy,x-ox);
if(b)
return (float) Math.cos(oa + Math.toRadians(a))*dst+ox;
else
return (float) Math.sin(oa + Math.toRadians(a))*dst+oy;
}
public static void main(String[] args)
throws SlickException
{
AppGameContainer app =
new AppGameContainer( new ColTest() );
app.setShowFPS(false);
app.setAlwaysRender(true);
app.setTargetFrameRate(60);
app.setDisplayMode(800, 600, false);
app.start();
}
}
Is using ovals an absolute requirement? You can approximate collisions between fancier shapes by representing them with multiple circles. That way you can use very a simple collision detection between circles and still achieve a high level of accuracy for the viewer.
collision(c1, c2) {
dx = c1.x - c2.x;
dy = c1.y - c2.y;
dist = c1.radius + c2.radius;
return (dx * dx + dy * dy <= dist * dist)
}
(source: strd6.com)
Finding the intersection is harder than you think. Your col() method is a bit off, but that approach will at best be able to tell you if a single point is within the circle. It won't be able to really detect intersections.
I Googled up some code for computing the actual intersections. I found one in JavaScript that's really interesting and really complicated. Take a look at the source.
If you wanted something a bit simpler (but less accurate), you could check a few points around the ellipse to see if they're within the circle.
private boolean isInCircle(float x, float y) {
float r = bsize / 2;
float center_x = bx + r;
float center_y = by + r;
float dist = (float) Math.sqrt(Math.pow(x - center_x, 2) + Math.pow(y - center_y, 2));
return dist < r;
}
public boolean col() {
return
isInCircle(px + pwidth / 2, py ) || // top
isInCircle(px + pwidth , py + pheight / 2) || // right
isInCircle(px + pwidth / 2, py + pheight ) || // bottom
isInCircle(px , py + pheight / 2); // left
}
If you plan on implementing more shapes and/or need the minimum distance between your shapes, you could start using GJK : you would only need to implement the support functions for each new shape. If computation time is also critical, GJK is definitely something you should look at, but it would surely require some more programming on your side.
If you can find your foci you can check for collision with the pseudo code below.
WARNING this only works for two ellipse collisions (ellipse and circle collisions work also).
r = length of semi major axis
a_x = x coordinate of foci 1 of the first ellipse
a_y = y coordinate of foci 1 of the first ellipse
b_x = x coordinate of foci 2 of the first ellipse
b_y = y coordinate of foci 2 of the first ellipse
c_x = x coordinate of foci 1 of the second ellipse
c_y = y coordinate of foci 1 of the second ellipse
d_x = x coordinate of foci 2 of the second ellipse
d_y = y coordinate of foci 2 of the second ellipse
p_x = (a_x+b_x+c_x+d_x)/4 // i.e. the average of the foci x values
p_y = (a_y+b_y+c_y+d_y)/4 // i.e. the average of the foci y values
if r >= ( sqrt( (p_x + a_x)^2+(p_y + a_y)^2 ) + sqrt( (p_x + a_x)^2+(p_y + a_y)^2 ) )
then collision
If you really want the derivation of this let me know and I'll provide it. But it uses the idea that the sum of the distances between the foci of an ellipse and any point on the edge of an ellipse are a set distance apart (the semi major axis). And solves for a point that is on the edge of both ellipsoids and if one exist then their is a collision.