How to draw rectangular in J2ME by canvas through drawline method - java-me

This is first time I at a question in here.
Im new in J2ME, and now im developing a small application, but i get problem when i wanna show data into table. But in J2me not support table there for that i know another way can represent for table such as create table by Canvas or CustomItem.
In Canvas i can draw 2 lines something like:
-----------------------
|
|
|
|
but i dont know how can get coordinate of 2 lines remain such as like:
|
|
|
|
|
--------------------------
two draw a rectangular in whole screen,
i know drawline method has 4 factors x1,y1,x2,y2.
but i can not calculate x point and y point to draw two lines above
I need you help me explain or give me example
My Code:
package test;
import javax.microedition.lcdui.Canvas;
import javax.microedition.lcdui.Graphics;
/**
*
* #author J2MENewBie
*/
public class TableCanvasExample extends Canvas {
private int cols=3;
private int rows =50;
protected void paint(Graphics g) {
g.setColor(0x94b2ff);
g.fillRect(0, 0, this.getWidth(), this.getHeight());
//draw two lines
g.setColor(0xf8011e);
g.drawLine(0, 0, 0, this.getWidth());
g.drawLine(0, 0, this.getHeight(), 0);
}
}
package test;
import javax.microedition.lcdui.Display;
import javax.microedition.midlet.*;
/**
* #author J2ME NewBie
*/
public class TableCanvasMidlet extends MIDlet {
private TableCanvasExample tbcve;
public TableCanvasMidlet(){
tbcve = new TableCanvasExample();
}
public void startApp() {
Display.getDisplay(this).setCurrent(tbcve);
}
public void pauseApp() {
}
public void destroyApp(boolean unconditional) {
}
}
P/s: the vertical line doesn't full size i dont know why ???
Thank you!

too much same-looking zeroes in your code - try using descriptive names instead:
int w = getWidth(), h = getHeight(); // I think this way it's easier to read
int xLeft = 0, yTop = 0; // descriptive names for zeroes
// below, replace w - 1 -> w and h - 1 -> h if lines drawn are off-by-one
int xRight = w - 1, yBottom = h - 1; // names for top - right coordinates
g.drawLine(xLeft, yTop, xLeft, yBottom); // your left vertical
g.drawLine(xLeft, yTop, xRight, yTop); // your top horizontal
g.drawLine(xRight, yTop, xRight, yBottom); // add right vertical
g.drawLine(xLeft, yBottom, xRight, yBottom); // add bottom horizontal
if rectangle drawn doesn't look like you expect find where there is wrong semantic in code above

Related

Placing canvas inside Div element

I am trying to create a circle in itext 7 and then place this circle anywhere in I need to in the document.
The document is laid out using divs and I have managed to create the circle using a PdfCanvas.
Below is a snippet of what I am trying to achieve and there may well be a better way to do this:
PdfDocument pdfDoc = new PdfDocument(writer);
Document doc = new Document(pdfDoc);
PdfPage pdfPage = pdfDoc.addNewPage();
Div div = new Div();
div.setBackgroundColor(Color.CYAN);
div.setHeight(10.0F);
div.add(new Paragraph(" ").setFont(PdfFontFactory.createFont(FontConstants.COURIER_BOLD)));
doc.add(div);
PdfCanvas canvas = new PdfCanvas(pdfPage);
Color white = Color.WHITE;
Color black = Color.BLACK;
canvas.setColor(white, true)
.setStrokeColor(black)
.circle(15, 800, 8)
.fillStroke();
canvas.beginText()
.setFontAndSize(PdfFontFactory.createFont(FontConstants.COURIER_BOLD), 10)
.setColor(black, true)
.moveText(15 - 3, 800 - 3)
.showText("1")
.endText();
doc.close();
If there is a correct way of wrapping some text (number) in a circle that can be positioned inside a div then I will happily change to this method if someone can point me towards a tutorial or some documentation I can follow.
If anybody is looking for an easy way to do this then you can override the draw method of Div.
public class Circle extends Div
{
#Override
public IRenderer getRenderer()
{
return new CircleRenderer(this);
}
private class CircleRenderer extends DivRenderer
{
public CircleRenderer(final Circle circle)
{
super(circle);
setPadding(PADDING);
}
#Override
public void draw(final DrawContext drawContext)
{
final PdfCanvas canvas = drawContext.getCanvas();
final Rectangle area = this.occupiedArea.getBBox();
final float x = area.getX();
final float y = area.getY();
canvas.circle(x, y, 8);
canvas.fillStroke();
super.draw(drawContext);
}
}
}
After I draw the canvas I need to do some correction of the x and y positions and same for any text placed inside the circle.

Trying to draw Rotated text with CGAffineTransform and MakeRotation appears at wrong location

I'm trying to draw some rotated texts by using the CGAffineTransform.MakeRotation method at specifc location. I also make use of the TranslateCTM, but something must be wrong as rotated texts do not appear aligned and at the correct x, y position where they should appear, here is simple the code I'm using, anyone know where the problem is? :
public override void Draw (RectangleF rect)
{
DrawTextRotated("Hello1",10,100,30);
DrawTextRotated("Hello2",50,100,60);
SetNeedsDisplay();
}
static public float DegreesToRadians(float x)
{
return (float) (Math.PI * x / 180.0);
}
public void DrawTextRotated(string text,int x, int y, int rotDegree)
{
CGContext c = UIGraphics.GetCurrentContext();
c.SaveState();
c.TextMatrix = CGAffineTransform.MakeRotation((float)DegreesToRadians((float)(-rotDegree)));
c.ConcatCTM(c.TextMatrix);
float xxx = ((float)Math.Sin(DegreesToRadians((float)rotDegree))*y);
float yyy = ((float)Math.Sin(DegreesToRadians((float)rotDegree))*x);
// Move the context back into the view
c.TranslateCTM(-xxx,yyy);
c.SetTextDrawingMode(CGTextDrawingMode.Fill);
c.SetShouldSmoothFonts(true);
MonoTouch.Foundation.NSString str = new MonoTouch.Foundation.NSString(text);
SizeF strSize = new SizeF();
strSize = str.StringSize(UIFont.SystemFontOfSize(12));
RectangleF tmpR = new RectangleF(x,y,strSize.Width,strSize.Height);
str.DrawString(tmpR,UIFont.SystemFontOfSize(12),UILineBreakMode.WordWrap,UITextAlignment.Right);
c.RestoreState();
}
Thanks !
Here's some code that will draw text rotated properly about the top-left corner of the text. For the moment, I'm disregarding your use of text alignment.
First, a utility method to draw a marker where we expect the text to show up:
public void DrawMarker(float x, float y)
{
float SZ = 20;
CGContext c = UIGraphics.GetCurrentContext();
c.BeginPath();
c.AddLines( new [] { new PointF(x-SZ,y), new PointF(x+SZ,y) });
c.AddLines( new [] { new PointF(x,y-SZ), new PointF(x,y+SZ) });
c.StrokePath();
}
And the code to draw the text (note I've replaced all int rotations with float, and you may want negate your rotation):
public void DrawTextRotated(string text, float x, float y, float rotDegree)
{
CGContext c = UIGraphics.GetCurrentContext();
c.SaveState();
DrawMarker(x,y);
// Proper rotation about a point
var m = CGAffineTransform.MakeTranslation(-x,-y);
m.Multiply( CGAffineTransform.MakeRotation(DegreesToRadians(rotDegree)));
m.Multiply( CGAffineTransform.MakeTranslation(x,y));
c.ConcatCTM( m );
// Draws text UNDER the point
// "This point represents the top-left corner of the string’s bounding box."
//http://developer.apple.com/library/ios/#documentation/UIKit/Reference/NSString_UIKit_Additions/Reference/Reference.html
NSString ns = new NSString(text);
UIFont font = UIFont.SystemFontOfSize(12);
SizeF sz = ns.StringSize(font);
RectangleF rect = new RectangleF(x,y,sz.Width,sz.Height);
ns.DrawString( rect, font);
c.RestoreState();
}
Rotation about a point requires translation of the point to the origin followed by rotation, followed by rotation back to the original point. CGContext.TextMatrix has no effect on NSString.DrawString so you can just use ConcatCTM.
The alignment and line break modes don't have any effect. Since you're using NSString.StringSize, the bounding rectangle fits the entirety of the text, snug up against the left and right edges. If you make the width of the bounding rectangle wider and use UITextAlignment.Right, you'll get proper right alignment, but the text will still rotate around the top left corner of the entire bounding rectangle. Which is not, I'm guessing, what you're expecting.
If you want the text to rotate around the top right corner, let me know and I'll adjust the code accordingly.
Here's the code I used in my test:
DrawTextRotated("Hello 0",100, 50, 0);
DrawTextRotated("Hello 30",100,100,30);
DrawTextRotated("Hello 60",100,150,60);
DrawTextRotated("Hello 90",100,200,90);
Cheers.

how to implement gesture-based menu

I am supposed to implement a gesture-based menu in which you scroll through a horizontal list of items by panning or flinging through them. This kind of menus is very common in smart phone games. Example case would be Cut the Rope where you select box (Cardboard box, Fabric box) or Angry Birds where you select the set of levels (Poached Eggs, Mighty Hoax).
What I am thinking is that I'll have to do some complex physics calculations and give velocities and accelerations to menu items based on the gestures. Any better solutions? I am using libgdx btw.
I don't think you'd need to go through all that to implement a simple menu! It's all about defining offsets for various items (I'll just assume you want Cut the Rope-style menus, with only one entry in sight at a given moment (excluding transitions)) and then tweening between those offsets whenever a flick is detected!
You seem to have the gesture system all wired up, so right now, we just need to figure out how to display the menu. For simplicity's sake, we'll just assume that we don't want the menu to wrap around.
We'll start by envisioning what this menu will look like, in our heads. It would be just like a filmstrip which passes through the phone and can be seen through the screen.
phone ("stuff" is currently selected)
========
|---------------| |-------|
| Start | About | Stuff | Quit |
|---------------| |-------|
| |
| |
| |
========
We'll just assume that the screen width is w and, consequently, all menu entries are exactly that width (think Cut the Rope again!).
Now, when "Start", is to be displayed, we should just render the flimstrip on the screen starting with the first element, "Start", while the rest would, theoretically, lie to the right of the screen. This will be considered the basic case, rendering the menu with the offset = 0.
Yes, yes, this offset will be the key to our little slidey-slidey menu! Now, it's pretty obvious that when about is selected, we'll just have to offset the "filmstrip" to the left by one "frame", and here offset = - 1 * frameWidth. Our example case illustrated by my brilliant ASCII art has the third menu item selected, and since the frames are indexed starting from 0, we'll just subtract two times the frameWidth and get the desired offset. We'll just render the menu starting at offset = -2 * frameWidth.
(Obviously you can just compute frameWidth in advance, by using the API to fetch the screen width, and then just drawing the menu element text/ graphic centered).
So this is pretty simple:
the user sweeps to the left, we need to get to the menu closer to offset 0, we reduce the index of the selected entity by one and the menu then jumps to the right position
the user sweeps to the right, we increase the index (obviously as long as it doesn't go over the number of menu elements - 1)
But what about smooth tweens?
Libgdx thankfully has interpolations all set for nice little tweens. We just need to take care of a few things so we don't shoot ourselves in the leg. I'll list them here.
One quick note:
The Cut the Rope level selector works a tad differently than what I'm saying here. It doesn't just react to flicks (pre-defined gestures), rather it's more sensitive. You can probably achieve a similar effect by playing with offsets and tracking the position of the finger on the screen. (If the user dragged a menu entry too much to the left/right, transition to the previous/next automatically) Friendly advice: just set up a simple, working menu, and leave details like this towards the end, since they can end up taking a lot of time! :P
Alright, back on track!
What we have now is a way to quickly switch between offsets. We just need to tween. There are some additional members that come into play, but I think they're pretty self-explanatory. While we're transitioning between two elements, we remember the "old" offset, and the one we're heading towards, as well as remembering the time we have left from the transition, and we use these four variables to compute the offset (using a libgdx interpolation, exp10 in this case) at the current moment, resulting in a smooth animation.
Let's see, I've created a quick'n'dirty mock-up. I've commented the code as best as I could, so I hope the following snippet speaks for itself! :D
import java.util.ArrayList;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.math.Interpolation;
public class MenuManager {
// The list of the entries being iterated over
private ArrayList<MenuEntry> entries = new ArrayList<>();
// The current selected thingy
private int index;
// The menu offset
private float offset = 0.0f;
// Offset of the old menu position, before it started tweening to the new one
private float oldOffset = 0.0f;
// What we're tweening towards
private float targetOffset = 0.0f;
// Hardcoded, I know, you can set this in a smarter fashion to suit your
// needs - it's basically as wide as the screen in my case
private float entryWidth = 400.0f;
// Whether we've finished tweening
private boolean finished = true;
// How much time a single transition should take
private float transitionTimeTotal = 0.33f;
// How much time we have left from the current transition
private float transitionTimeLeft = 0.0f;
// libgdx helper to nicely interpolate between the current and the next
// positions
private Interpolation interpolation = Interpolation.exp10;
public void addEntry(MenuEntry entry) {
entries.add(entry);
}
// Called to initiate transition to the next element in the menu
public void selectNext() {
// Don't do anything if we're still animationg
if(!finished) return;
if(index < entries.size() - 1) {
index++;
// We need to head towards the next "frame" of the "filmstrip"
targetOffset = oldOffset + entryWidth;
finished = false;
transitionTimeLeft = transitionTimeTotal;
} else {
// (does nothing now, menu doesn't wrap around)
System.out.println("Cannot go to menu entry > entries.size()!");
}
}
// see selectNext()
public void selectPrevious() {
if(!finished) return;
if(index > 0) {
index --;
targetOffset = oldOffset - entryWidth;
finished = false;
transitionTimeLeft = transitionTimeTotal;
} else {
System.out.println("Cannot go to menu entry <0!");
}
}
// Called when the user selects someting (taps the menu, presses a button, whatever)
public void selectCurrent() {
if(!finished) {
System.out.println("Still moving, hold yer pants!");
} else {
entries.get(index).select();
}
}
public void update(float delta) {
if(transitionTimeLeft > 0.0f) {
// if we're still transitioning
transitionTimeLeft -= delta;
offset = interpolation.apply(oldOffset, targetOffset, 1 - transitionTimeLeft / transitionTimeTotal);
} else {
// Transition is over but we haven't handled it yet
if(!finished) {
transitionTimeLeft = 0.0f;
finished = true;
oldOffset = targetOffset;
}
}
}
// Todo make font belong to menu
public void draw(SpriteBatch spriteBatch, BitmapFont font) {
if(!finished) {
// We're animating, just iterate through everything and draw it,
// it's not like we're wasting *too* much CPU power
for(int i = 0; i < entries.size(); i++) {
entries.get(i).draw((int)(i * entryWidth - offset), 100, spriteBatch, font);
}
} else {
// We're not animating, just draw the active thingy
entries.get(index).draw(0, 100, spriteBatch, font);
}
}
}
And I believe a simple text-based menu entry that can draw itself would suffice! (do mind the dirty hard-coded text-wrap width!)
public class MenuEntry {
private String label;
// private RenderNode2D graphic;
private Action action;
public MenuEntry(String label, Action action) {
this.label = label;
this.action = action;
}
public void select() {
this.action.execute();
}
public void draw(int x, int y, SpriteBatch spriteBatch, BitmapFont font) {
font.drawMultiLine(spriteBatch, label, x, y, 400, HAlignment.CENTER);
}
}
Oh, and Action is just a thingy that has an execute method and, well, represents an action.
public interface Action {
abstract void execute();
}
Feel free to ask any related question in the comments, and I'll try to clarify what's needed.
Hope this helps!

How to write Scrollable for Table in j2me by canvas?

Now i try to draw a table by Canvas, but i have a problem, my table doesnt has scrollable.
How can i write some code to help my table can scrollable?
My Code here
import javax.microedition.lcdui.Canvas;
import javax.microedition.lcdui.Graphics;
/**
*
* #author Kency
*/
public class TableCanvas extends Canvas{
private int w,h;
private int pad;
private int cols = 3;
private int rows = 10;
public TableCanvas() {
w = getWidth();
h = getHeight();
}
protected void paint(Graphics g) {
g.setColor(148, 178, 255);
g.fillRect(0, 0, w, h);
for(int i =0 ; i <= cols ; i++){
g.setColor(0x00D0D0D0);
for(int j = 0 ; j <= rows ; j++){
g.drawLine(0, j * h/rows, cols * w, j* h/rows);
g.drawLine(i * w/cols, 0, i * w/cols, w * rows);
}
}
}
}
For code like in your excerpt, most straightforward way would probably be with Graphics#translate API.
One would also have to handle key pressed / pointer events to allow user to scroll. Eg when key press corresponds to game action right/left/up/down, you scroll respectively. When pointer is dragged, you find out the direction and, again, scroll respectively
Painting scrollbar(s) would also require "handmade" code.
Another option would be using 3rd party library like LWUIT or J2ME Polish

How to handle ball to ball collision with trigonometry instead of vectors?

I am making a simplified version of the ball physics app found at this SO question. I gained a lot from reading through the code and links provided at that question, but in my program I do not use vectors, I just update the coordinates of my balls (each of which has a random angle and speed) with trigonometry functions when they hit the walls.
There is info all over the place for how to handle ball to ball collisions without trigonometry, but I have found none that explain how it can be done with trigonometry.
--EDIT 9/13/2010--
Success... kind of... I got done what I wanted to do, but I was unable to do it how I wanted. If there is a way to calculate ball to ball collisions without the use of vectors it has eluded me. Even so, vectors do seem to be an easier way to handle collisions of all types... I just wish I would have known that when I started my program... would have saved me two or three days of work :) All the code for my (complete?) program is below. I added some neat features like shadows and a decreasing ball radius which really lets you see the difference in the mass of two balls when a big ball hits a small ball. In total there are five class files, AddLogic.java, Ball.java, BallBuilder.java, MouseEventHandler.java, and Vector2D.java.
AddLogic.java
import java.awt.*;
import java.awt.Graphics2D;
import java.awt.image.BufferStrategy;
import java.util.ArrayList;
public class AddLogic implements Runnable {//Make AddLogic a runnable task.
private BallBuilder ballBuilder;
private BufferStrategy strategy;
private static ArrayList objectsToDraw = new ArrayList();
private int floorHeight = 33;
public AddLogic(BallBuilder ballBuilder, BufferStrategy strategy) {
this.ballBuilder = ballBuilder;
this.strategy = strategy;
}
private void logic(BallBuilder ballBuilder, BufferStrategy strategy) {
this.ballBuilder = ballBuilder;
this.strategy = strategy;
while (true) {//Main loop. Draws all objects on screen and calls update methods.
Graphics2D g = (Graphics2D) strategy.getDrawGraphics();//Creates the Graphics2D object g and uses it with the double buffer.
g.setColor(Color.gray);
g.fillRect(0, 0, ballBuilder.getWidth(), ballBuilder.getHeight());//Draw the wall.
g.setColor(Color.lightGray);
g.fillRect(0, ballBuilder.getHeight() - floorHeight, ballBuilder.getWidth(), floorHeight);//Draw the floor.
g.setColor(Color.black);
g.drawLine(0, ballBuilder.getHeight() - floorHeight, ballBuilder.getWidth(), ballBuilder.getHeight() - floorHeight);//Draw the line between the wall and floor.
if (objectsToDrawIsEmpty() == true) {//If no balls have been made display message telling users how to make new ball.
g.setColor(Color.red);
g.drawString("Click Mouse For New Ball", (ballBuilder.getWidth() / 2) - 70, ballBuilder.getHeight() / 2);
}
for (int i = 0; i < objectsToDraw.size(); i++) {//Draw shadows for all balls.
Ball ball = (Ball) objectsToDraw.get(i);
g.setColor(Color.darkGray);
g.fillOval(
(int) ball.ballPosition.getX() - (int) ((ball.ballPosition.getY() / (350 / ball.getBallRadius())) / 2),
ballBuilder.getHeight() - (floorHeight / 2) - (int) ((ball.ballPosition.getY() / (1250 / ball.getBallRadius())) / 2),
(int) ball.ballPosition.getY() / (350 / ball.getBallRadius()),
(int) ball.ballPosition.getY() / (1250 / ball.getBallRadius()));
}
for (int i = 0; i < objectsToDraw.size(); i++) {//Draw all balls by looping through them and checking for any vector or collision updates that need to be made.
Ball ball = (Ball) objectsToDraw.get(i);
g.setColor(ball.getBallColor());
g.fillOval(
(int) ball.ballPosition.getX() - ball.getBallRadius(),
(int) ball.ballPosition.getY() - ball.getBallRadius(),
ball.getBallRadius() * 2,
ball.getBallRadius() * 2);
vectorUpdate(ball);//Update ball vector coordinates.
collisionCheck(ball);//Check ball to ball and ball to wall collisions.
}
if (MouseEventHandler.mouseEventCheck() == true) {// Creates a new ball when mouse is clicked.
Ball ball = new Ball(ballBuilder);
objectsToDraw.add(ball); //Adds the new ball to the array list.
MouseEventHandler.mouseEventUpdate(); //Resets the mouse click event to false.
}
g.dispose();//To aid Java in garbage collection.
strategy.show();//Show all graphics drawn on the buffer.
try {//Try to make thread sleep for 5ms. Results in a frame rate of 200FPS.
Thread.sleep(5);
}
catch (Exception e) {//Catch any exceptions if try fails.
}
}
}
private void vectorUpdate(Ball ball) {//Update the ball vector based upon the ball's current position and its velocity.
ball.ballPosition.setX(ball.ballPosition.getX() + ball.ballVelocity.getX());
ball.ballPosition.setY(ball.ballPosition.getY() + ball.ballVelocity.getY());
}
private void collisionCheck(Ball ball) {//Check for ball to wall collisions. Call check for ball to ball collisions at end of method.
if (ball.ballPosition.getX() - ball.getBallRadius() < 0) {//Check for ball to left wall collision.
ball.ballPosition.setX(ball.getBallRadius());
ball.ballVelocity.setX(-(ball.ballVelocity.getX()));
ball.decreaseBallRadius(ball);//Decrease ball radius by one pixel. Called on left, top, and right walls, but not bottom because it looks weird watching shadow get smaller during bottom bounce.
}
else if (ball.ballPosition.getX() + ball.getBallRadius() > ballBuilder.getWidth()) {//Check for ball to right wall collision.
ball.ballPosition.setX(ballBuilder.getWidth() - ball.getBallRadius());
ball.ballVelocity.setX(-(ball.ballVelocity.getX()));
ball.decreaseBallRadius(ball);//Decrease ball radius by one pixel. Called on left, top, and right walls, but not bottom because it looks weird watching shadow get smaller during bottom bounce.
}
if (ball.ballPosition.getY() - ball.getBallRadius() < 0) {//Check for ball to top wall collision.
ball.ballPosition.setY(ball.getBallRadius());
ball.ballVelocity.setY(-(ball.ballVelocity.getY()));
ball.decreaseBallRadius(ball);//Decrease ball radius by one pixel. Called on left, top, and right walls, but not bottom because it looks weird watching shadow get smaller during bottom bounce.
}
else if (ball.ballPosition.getY() + ball.getBallRadius() + (floorHeight / 2) > ballBuilder.getHeight()) {//Check for ball to bottom wall collision. Floor height is accounted for to give the appearance that ball is bouncing in the center of the floor strip.
ball.ballPosition.setY(ballBuilder.getHeight() - ball.getBallRadius() - (floorHeight / 2));
ball.ballVelocity.setY(-(ball.ballVelocity.getY()));
}
for (int i = 0; i < objectsToDraw.size(); i++) {//Check to see if a ball is touching any other balls by looping through all balls and checking their positions.
Ball otherBall = (Ball) objectsToDraw.get(i);
if (ball != otherBall && Math.sqrt(Math.pow(ball.ballPosition.getX() - otherBall.ballPosition.getX(), 2.0) + Math.pow(ball.ballPosition.getY() - otherBall.ballPosition.getY(), 2.0)) < ball.getBallRadius() + otherBall.getBallRadius()) {
resolveBallToBallCollision(ball, otherBall);//If the ball is hitting another ball calculate the new vectors based on the variables of the balls involved.
}
}
}
private void resolveBallToBallCollision(Ball ball, Ball otherBall) {//Calculate the new vectors after ball to ball collisions.
Vector2D delta = (ball.ballPosition.subtract(otherBall.ballPosition));//Difference between the position of the two balls involved in the collision.
float deltaLength = delta.getLength();//The (x, y) of the delta squared.
Vector2D minimumTranslationDistance = delta.multiply(((ball.getBallRadius() + otherBall.getBallRadius()) - deltaLength) / deltaLength);//The minimum distance the balls should move apart once they.
float ballInverseMass = 1 / ball.getBallMass();//half the ball mass.
float otherBallInverseMass = 1 / otherBall.getBallMass();//half the other ball mass.
ball.ballPosition = ball.ballPosition.add(minimumTranslationDistance.multiply(ballInverseMass / (ballInverseMass + otherBallInverseMass)));//Calculate the new position of the ball.
otherBall.ballPosition = otherBall.ballPosition.subtract(minimumTranslationDistance.multiply(otherBallInverseMass / (ballInverseMass + otherBallInverseMass)));//Calculate the new position of the other ball.
Vector2D impactVelocity = (ball.ballVelocity.subtract(otherBall.ballVelocity));//Find the veloicity of the impact based upon the velocities of the two balls involved.
float normalizedImpactVelocity = impactVelocity.dot(minimumTranslationDistance.normalize());//
if (normalizedImpactVelocity > 0.0f) {//Returns control to calling object if ball and other ball are intersecting, but moving away from each other.
return;
}
float restitution = 2.0f;//The constraint representing friction. A value of 2.0 is 0 friction, a value smaller than 2.0 is more friction, and a value over 2.0 is negative friction.
float i = (-(restitution) * normalizedImpactVelocity) / (ballInverseMass + otherBallInverseMass);
Vector2D impulse = minimumTranslationDistance.multiply(i);
ball.ballVelocity = ball.ballVelocity.add(impulse.multiply(ballInverseMass));//Change the velocity of the ball based upon its mass.
otherBall.ballVelocity = otherBall.ballVelocity.subtract(impulse.multiply(otherBallInverseMass));//Change the velocity of the other ball based upon its mass.
}
public static boolean objectsToDrawIsEmpty() {//Checks to see if there are any balls to draw.
boolean empty = false;
if (objectsToDraw.isEmpty()) {
empty = true;
}
return empty;
}
public void run() {//Runs the AddLogic instance logic in a new thread.
logic(ballBuilder, strategy);
}
}
Ball.java
import java.awt.*;
public class Ball {
private int ballRadius;
private float ballMass;
public Vector2D ballPosition = new Vector2D();
public Vector2D ballVelocity = new Vector2D();
private Color ballColor;
public Ball(BallBuilder ballBuilder) {//Construct a new ball.
this.ballRadius = 75;//When ball is created make its radius 75 pixels.
this.ballMass = ((float)(4 / 3 * Math.PI * Math.pow(ballRadius, 3.0)));//When ball is created make its mass that the volume of a sphere the same size.
this.ballPosition.set(ballRadius, ballBuilder.getHeight() - ballRadius);//When ball is created make its starting coordinates the bottom left hand corner of the screen.
this.ballVelocity.set(randomVelocity(), randomVelocity());//When ball is created make its (x, y) velocity a random value between 0 and 2.
if (AddLogic.objectsToDrawIsEmpty() == true) {//If the ball being created is the first ball, make it blue, otherwise pick a random color.
this.ballColor = Color.blue;
} else {
this.ballColor = randomColor();
}
}
public void decreaseBallRadius(Ball ball){//Decrease the ball radius.
if(ball.getBallRadius() <= 15){//If the ball radius is less than or equal to 15 return control to calling object, else continue.
return;
}
ball.setBallRadius(ball.getBallRadius() - 1);//Decrease the ball radius by 1 pixel.
ball.setBallMass((float)(4 / 3 * Math.PI * Math.pow(ballRadius, 3.0)));//Recalcualte the mass based on the new radius.
}
public int getBallRadius() {
return ballRadius;
}
public float getBallMass(){
return ballMass;
}
public Color getBallColor() {
return ballColor;
}
private void setBallRadius(int newBallRadius) {
this.ballRadius = newBallRadius;
}
private void setBallMass(float newBallMass){
this.ballMass = newBallMass;
}
private float randomVelocity() {//Generate a random number between 0 and 2 for the (x, y) velocity of a ball.
float speed = (float)(Math.random() * 2);
return speed;
}
private Color randomColor() {//Generate a random color for a new ball based on the generation of a random red, green, and blue value.
int red = (int) (Math.random() * 255);
int green = (int) (Math.random() * 255);
int blue = (int) (Math.random() * 255);
ballColor = new Color(red, green, blue);
return ballColor;
}
}
BallBuilder.java
import java.awt.*;
import java.awt.image.*;
import java.util.concurrent.*;
import javax.swing.*;
public class BallBuilder extends Canvas{
private int frameHeight = 600;
private int frameWidth = 800;
private static BufferStrategy strategy;//Create a buffer strategy named strategy.
public BallBuilder(){
setIgnoreRepaint(true);//Tell OS that we will handle any repainting manually.
setBounds(0,0,frameWidth,frameHeight);
JFrame frame = new JFrame("Bouncing Balls");
JPanel panel = new JPanel();
panel.setPreferredSize(new Dimension(frameWidth, frameHeight));
panel.add(this);
frame.setContentPane(panel);
frame.pack();
frame.setResizable(false);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
addMouseListener(new MouseEventHandler());
createBufferStrategy(2);//Create a double buffer for smooth graphics.
strategy = getBufferStrategy();//Apply the double buffer to the buffer strategy named strategy.
}
public static void main(String[] args) {
BallBuilder ballBuilder = new BallBuilder(); // Creates a new ball builder.
ExecutorService executor = Executors.newSingleThreadExecutor();//Creates a thread executor that uses a single thread.
executor.execute(new AddLogic(ballBuilder, strategy));//Executes the runnable task AddLogic on the previously created thread.
}
}
MouseEventHandler.java
import java.awt.event.*;
public class MouseEventHandler extends MouseAdapter{
private static boolean mouseClicked = false;
public void mousePressed(MouseEvent e){//If either of the mouse buttons is pressed the mouse clicked variable is set to true.
mouseClicked = true;
}
public static void mouseEventUpdate(){//When called, sets the mouse clicked variable back to false.
mouseClicked = false;
}
public static boolean mouseEventCheck(){//Returns the state of the mouse clicked variable.
if(mouseClicked){
return true;
}
else{
return false;
}
}
}
Vector2D
public class Vector2D {//A class that takes care of ball position and speed vectors.
private float x;
private float y;
public Vector2D() {
this.setX(0);
this.setY(0);
}
public Vector2D(float x, float y) {
this.setX(x);
this.setY(y);
}
public void set(float x, float y) {
this.setX(x);
this.setY(y);
}
public void setX(float x) {
this.x = x;
}
public void setY(float y) {
this.y = y;
}
public float getX() {
return x;
}
public float getY() {
return y;
}
public float dot(Vector2D v2) {//Speciality method used during calculations of ball to ball collisions.
float result = 0.0f;
result = this.getX() * v2.getX() + this.getY() * v2.getY();
return result;
}
public float getLength() {
return (float) Math.sqrt(getX() * getX() + getY() * getY());
}
public Vector2D add(Vector2D v2) {
Vector2D result = new Vector2D();
result.setX(getX() + v2.getX());
result.setY(getY() + v2.getY());
return result;
}
public Vector2D subtract(Vector2D v2) {
Vector2D result = new Vector2D();
result.setX(this.getX() - v2.getX());
result.setY(this.getY() - v2.getY());
return result;
}
public Vector2D multiply(float scaleFactor) {
Vector2D result = new Vector2D();
result.setX(this.getX() * scaleFactor);
result.setY(this.getY() * scaleFactor);
return result;
}
public Vector2D normalize() {//Speciality method used during calculations of ball to ball collisions.
float length = getLength();
if (length != 0.0f) {
this.setX(this.getX() / length);
this.setY(this.getY() / length);
} else {
this.setX(0.0f);
this.setY(0.0f);
}
return this;
}
}
The speed angle changes when the ball bounces, you should change it after each bounce.
Also, as you'll see later, the speed VALUE (called "modulus") also changes when two balls collide.
EDIT:::
It seems to me that you are accelerating the balls
The
int x = (int) (ball.getBallTempX() +
(ball.getBallPathLength() * Math.cos(ball.getBallAngle())));
Seems to correspond to
int x = (int) (ball.getBallTempX() +
(ball.getBallSpeed() * Math.cos(ball.getBallAngle())));
And the same for "y"
Am I right?
Edit 2::
In fact, you don't need the TempX,TempY,PreviousCenterX and PreviousCenterY either.
Try these methods
private int xCoordinateUpdate(Ball ball) {
int x = (int) (ball.getBallCenterX()+ (ball.Speed()
* Math.cos(ball.getBallAngle())));
return x;
(the same for Y)
Edit 3 ::
One more ... :)
What you need is to code is this formula (any other will fail when you attempt to collide balls):
Pseudocode >
BallCenterX = BallCenterX + BallSpeed * Math.cos(angle)
BallCenterY = BallCenterY + BallSpeed * Math.sin(angle)
Forget any "temp" and "old" values. They will impair your ball colliding feature.
I'll start this answer and keep editing it step by step until we are done. I'll try to guide you to a "semi-vectorized" version of the program, trying to minimize the effort.
Please keep updating the code as you progress, and commenting on my suggestions.
First, a few things:
In
private double randomBallAngle(){
ballAngle = Math.toRadians((Math.random()*90));
return ballAngle;
}
You are working in radians, but in yCoordinateUpdate and yCoordinateUpdate it seems that you are using the same angle in grads (because you are comparing with 90).
Using radians is easier for all the math involved.
Also, the vars GoingUp, etc are not needed since the speed angle will take care of that.
You may set the initial random angle in the interval (0 ... 2 Pi).
You should change (in fact reflect) the speed angle after each border collission (and afterwards, when we are done, after each inter-ball caollission too).
For this reflection, the laws are:
Upper or lower wall: New Angle = - OldAngle
For Right or left wall: New Angle = Pi - OldAngle
We'll keep reflections on the vertex for later ...

Resources