I currently have a program with a grid and lines that will be drawn between dots in the grid. It uses standard draw. I wish to limit the length of the lines, so that they can only go from the adjacent points, but do not know how I would do this.
Thanks
StdDraw.setCanvasSize(400, 400);
StdDraw.setXscale(0, 10);
StdDraw.setYscale(0, 10);
//dots
double radius = .15;
double spacing = 2.0;
for (int i = 0; i <= 4; i++) {
for (int j = 0; j <= 4; j++) {
StdDraw.setPenColor(StdDraw.GRAY);
StdDraw.filledCircle(i * spacing, j * spacing, radius );
}
}
StdDraw.setPenColor(StdDraw.BLUE);
StdDraw.text(0, 9.5, player1_name);
StdDraw.setPenColor(StdDraw.RED);
StdDraw.text(5, 9.5, player2_name);
int turn = 1;
for (int i = 0; i <= 40; i++) {
if (turn % 2 == 0)
StdDraw.setPenColor(StdDraw.RED);
else
StdDraw.setPenColor(StdDraw.BLUE);
while(!StdDraw.mousePressed()) { }
double x = StdDraw.mouseX();
double y = StdDraw.mouseY();
System.out.println(x + " " + y);
StdDraw.setPenRadius(.01);
StdDraw.show(200);
while(!StdDraw.mousePressed()) { }
double x2 = StdDraw.mouseX();
double y2 = StdDraw.mouseY();
StdDraw.show(200);
double xround = Math.round(x);
double yround = Math.round(y);
double x2round = Math.round(x2);
double y2round = Math.round(y2);
int xroundb = (int) xround;
int yroundb = (int) yround;
int x2roundb = (int) x2round;
int y2roundb = (int) y2round;
StdDraw.line(xround, yround, x2round, y2round);
System.out.println("Line Drawn");
StdDraw.show();
Ah I get it. You are not asking about the actual line method which does work correctly, you want logic such that line is not called if adjacent dots are not selected.
Well, first we need to know which adjacent connections are allowed. That is can we have Vertical? Horizontal? Diagonal? I will explain each just in case
So you have spacing = 2.0. Well, that should be sufficient to check for adjacency.
if (Math.abs(x2round - xround) > spacing) {
// don't draw
} else if (Math.abs(y2round - yround) > spacing)) {
// don't draw
} else if (Math.abs(y2round - yround) > 0.0) && Math.abs(x2round - xround) > 0.0) {
// don't draw if diagonal connections are forbidden
// if diagonal is allowed, remove this else if condition
} else {
StdDraw.line(xround, yround, x2round, y2round);
}
So if you don't draw, then you have to enforce your game logic. Perhaps the player forfeits a turn. Perhaps the player is given another chance to select adjacent dots. It is up to you. It is always a bit crazy comparing doubles due to roundoff, so instead of using 0.0, you might want to choose a very small epsilon double value to make sure you catch all cases.
Related
I need to create a simple Java program, that draws a bezier curve pixel by pixel through any amount of points. At the moment, everything seems to be ok except that the curve always ends at x=0 y=0 coordinates.
Screenshot 1
Screenshot 2
I need it to end at the last point. My brain is not quite working today, so I'm looking for some help.
Here is what I have:
private void drawScene(){
precision = Float.parseFloat(this.jTextField4.getText());
//Clears the screen and draws X and Y lines
g.setColor(Color.white);
g.fillRect(0, 0, pWidth, pHeight);
g.setColor(Color.gray);
g.drawLine(0, offsetY, pWidth, offsetY);
g.drawLine(offsetX, 0, offsetX, pHeight);
//Drawing the points
if(pointCount > 0){
for(int i = 0;i<pointCount;i++){
g.setColor(Color.red);
g.drawString(String.valueOf(i+1), points[i].x + offsetX, points[i].y - 6 + offsetY);
g.drawOval(points[i].x + offsetX, points[i].y - 6 + offsetY, 3, 3);
}
}
//Drawing the curve
if(pointCount > 1){
float t = 0;
while(t <= 1){
g.setColor(Color.gray);
this.besierCurvePixel(t);
t += precision;
}
}
}
//Factorial
private static int fact(int n) {
int fact = 1;
for (int i = 1; i <= n; i++) {
fact *= i;
}
return fact;
}
//Bernstein polynomial
private static double bernstein(float t, int n, int i){
return (fact(n) / (fact(i) * fact(n-i))) * Math.pow(1-t, n-i) * Math.pow(t, i);
}
private void besierCurvePixel(float t){
double bPoly[] = new double[pointCount];
for(int i = 0; i < pointCount; i++){
bPoly[i] = bernstein(t, pointCount, i+1);
}
double sumX = 0;
double sumY = 0;
for(int i = 0; i < pointCount; i++){
sumX += bPoly[i] * points[i].x;
sumY += bPoly[i] * points[i].y;
}
int x, y;
x = (int) Math.round(sumX);
y = (int) Math.round(sumY);
g.drawLine(x + offsetX, y + offsetY, x + offsetX, y + offsetY);
}
This is the method for adding the points (pointCount is 0 initially):
points[pointCount] = new Point();
points[pointCount].x = evt.getX() - this.offsetX;
points[pointCount].y = evt.getY() - this.offsetY;
pointCount++;
this.drawScene();
The problem was here
for(int i = 0; i < pointCount; i++){
bPoly[i] = bernstein(t, pointCount, i+1);
}
The second parameter in the bernstein method was incorrect. Basically If I have 3 points, it should be 2 not 3;
bPoly[i] = bernstein(t, pointCount-1, i+1);
Where does "pointcount" get set (and to what)?
Have you tried stepping through your code to see why it continues after reaching the last point?
Is it possible that you are stepping through a loop 1 extra time, which is why the last point would have a destination set to (0,0)?
Could you set the number of steps for the app to make to each point?
Hopefully I am bringing up points to help you find your answer
*Edit: If I had to guess- you are accidentally adding an additional point of (0,0) to points[]; Here is where I am seeing it go to (0,0) after the last point:
for(int i = 0; i < pointCount; i++){
sumX += bPoly[i] * **points[i]**.x;
sumY += bPoly[i] * **points[i]**.y;
}
Edit: Glad you were able to fix it, and hopefully i helped with finding that issue. Best of luck in the future!
I've made a simple code to get jumping ball positions, but I definitely missed something, because I get this:
Here's the code for getting x and y positions:
public Vector2f[] draw() {
float x = 0, y = height; // height - float value from class constructor;
ArrayList<Vector2f> graphic = new ArrayList<Vector2f>();
for(;;){
Vector2f a = new Vector2f(x, y);
graphic.add(a);
ySpeed -= 10;
y += ySpeed*Math.cos(angle);
x += xSpeed*Math.sin(angle);
if(y <= 0){
// float coef = -10 * y / ySpeed;
// ySpeed = ((ySpeed + coef) * (-1) ) * bouncyness;
ySpeed = (ySpeed * (-1)) * bouncyness;
System.out.println(ySpeed + " " + y);
y = 0;
}
if(x > Main.width)
break;
}
Vector2f[] graphicArray = new Vector2f[graphic.size()];
for (int i = 0; i < graphic.size(); i++) {
graphicArray[i] = graphic.get(i);
}
return graphicArray;
}
On its iteration y gets lower than the X axis on the first run. Then being zeroed,
So max height that you will get in that iteration is lower than original height.
The same happens later,
Until y will get to 0 without being set to it ( I think it will always converge to it ).
If you will set your height to be divided by 10 it should look alright.
For the bouncing case change if ( y <= 0) to if ( y<= 10 ) and remove y = 0 statement.
The correct situation ( not bouncing ), set y = Math.abs(y)
A few thoughts
I don't have a clue what you are doing with the angle. To me, it looks plain wrong. Try to get rid of it.
You should integrate the acceleration twice over one timestep to make it work physically correct.
x += v + acc * ∆time * ∆time * 0.5;
v += acc * ∆time;
Make y = -y when y < 0.
Where your ∆time is 1 and your acc is -10, I guess.
I am a visual learner working on a simple 2D game that requires balls to bounce off of each other with no spin.
I have followed the code in many links and chose to use the example found at Ball to Ball Collision - Detection and Handling
My code however results in both the ball accelerating out of control and also somehow removing balls from the playing field on striking. If someone could describe my errors to me that would be much appreiated. However I would find just as much help in step by step pictures describing the math behind the collisions found in the link above.
public void resolveCollision2(PoolBall ball) {
vector delta = new vector(getXPos() - ball.getXPos(), getYPos() - ball.getYPos());
float d = (float) delta.getMagnitude();
vector mtd = delta.scalerMultiply((32.0 - d) / d);
vector reset = mtd.scalerMultiply(0.5);
XPos = XPos + reset.getXlen();
YPos = YPos + reset.getYlen();
ball.setXPos(ball.getXPos() - reset.getXlen());
ball.setYPos(ball.getYPos() - reset.getYlen());
vector v1 = new vector(getXVel(), getYVel());
vector v2 = new vector(ball.getXVel(), ball.getYVel());
vector v = v1.subtract(v2);
float vn = v.dot(mtd.normalize());
if(vn > 0.0f) return;
float i = (-(1.0f + 0.1f) * vn);
vector impulse = mtd.scalerMultiply(i);
vector v1prime = v1.add(impulse);
vector v2prime = v2.subtract(impulse);
setXVel(Math.sqrt(v1prime.getXlen()));
setYVel(Math.sqrt(v1prime.getYlen()));
ball.setXVel(Math.sqrt(v2prime.getXlen()));
ball.setYVel(Math.sqrt(v2prime.getYlen()));
}
public void poolBallPoolBallCollision() {
double XDif = 0.0;
double YDif = 0.0;
double XDif2 = 0.0;
double YDif2 = 0.0;
for (int i = 0; i < 15; i++) {
for (int j = i + 1; j < 15; j++) {
if (colliding(ballList[i], ballList[j])) {
ballList[i].resolveCollision2(ballList[j]);
}
}
}
}
Things to note
balls have equal mass
no friction
no spin
How can I draw a line on the console if I have a 2D array of chars. The function I want to write is something like:
This is my first attempt, but it looks totally wrong
public static void line(char[][] mPixels, int startRow, int startColumn, int endRow, int endColumn)
{
double dY = endRow - startRow;
double dX = endColumn - startColumn;
double slope = dX / dY;
slope = Math.abs(slope);
if(slope >= 1)
{
double progress = -(dY / dX);
for(int i=startColumn; i<=endColumn; i++)
{
double j = startRow - (int) ((i-startColumn) * progress);
int yLoc = (int) (Math.round( j * 100.0 ) / 100.0);
mPixels[i][yLoc] = '*';
}
}
// print array
}
use DDA or Bresenham,...
What you have looks like DDA but you do not handle slopes correctly. You should divide by the axis with bigger amount of pixels and use it as control axis so:
if |dx|>|dy| then for goes through x = x0 -> x1 and y=y0+((x-x0)*dy/dx)
if |dx|<|dy| then for goes through y = y0 -> y1 and x=x0+((y-y0)*dx/dy)
if they are equal then use any of above.
if dx==0 and dy==0 draw just dot and no for is present
Do not forget to handle if main axis is ascending or descending (can be x++,y++ or x--,y--) also can be done on integer only without division or multiplication but that is another story
I jumped into Processing (the language) today. I've been trying to implement a line without the line() function. In other words, I'm trying to replicate the line() function with my own code. I'm almost there, but not. (There's a screen, and you can click around, and this function connects those clicks with lines.)
There are four different line slopes I'm dealing with (m>1, 0
If you could just glance at the following code, and tell me where I've gone wrong, I'd be grateful.
int xStart = -1; // Starting x and y are negative.
int yStart = -1; // No lines are drawn when mouseReleased() and x/y are negative.
boolean isReset = false; // Turns true when 'r' is pressed to reset polygon chain.
int clickCounter = 0; // Changes background color every 10 clicks (after c is pressed).
color backgroundColor = 0; // Starting background color. Changed occasionally.
color lineColor = 255;
int weight = 1;
void setup() {
size(800, 800); // Initial size is 800x800
background(backgroundColor); // ...background is black.
smooth();
stroke(lineColor); //... lines/points are white.
strokeWeight(weight);
}
void draw() {
}
void mousePressed(){
clickCounter++;
}
void mouseReleased(){ // mouseReleased used instead of mousePressed to avoid dragged clicks.
point(mouseX, mouseY); // Draws white point at clicked coordinates.
if((xStart < 0 && yStart < 0) || isReset){ // If x/y negative or if r was pressed, set start points and return. No line drawn.
xStart = mouseX;
yStart = mouseY;
isReset = false; // Reset isReset to false.
return;
}
// Sends starting and ending points to createLine function.
createLine(xStart, yStart, mouseX, mouseY); // createLine(...) - Creates line from start point to end point.
xStart = mouseX; // Start point = last click location. End point = Current click location.
yStart = mouseY; // Sets starting coordinates for next click at current click point.
}
void keyPressed(){
if(key == 'x') // EXTRA CREDIT ADDITION: If x is pressed -> Exit program.
exit();
else if(key == 'c'){ // EXTRA CREDIT ADDITTION: If c pressed -> Set background black to clear all lines/points on screen.
if(clickCounter > 10){
backgroundColor = color(random(255), random(255), random(255)); // EXTRA CREDIT ADDITION: If cleared and clickCounter is greater
clickCounter = 0; // ...than 10, background changes to random color.
}
background(backgroundColor);
xStart = -1; // Must set points negative so line is not drawn after next new point is made (since there will only be one point on the screen).
yStart = -1;
}
else if(key == 'r'){ // If r pressed -> Reset: Next click will create new point that isn't connected with line to current points.
isReset = true;
lineColor = color(random(255), random(255), random(255)); // EXTRA CREDIT ADDITION: When dot chain is "reset", line changes color.
weight = (int)random(10);
strokeWeight(weight); // EXTRA CREDIT ADDITION: ...and line/dot thickness changes.
stroke(lineColor);
}
else
return;
}
// createLine(): Function which draws line from (x0,y0) to (x1,y1).
void createLine(int x0, int y0, int x1, int y1){
// 1) Line function draws from left to right. (Does not work right to left.) Check and swap points if ending point is left of starting point.
if(x1 < x0){
print("LEFT TO RIGHT SWITCH. \n");
createLine(x1, y1, x0, y0); // Drawing the line left to right cuts the number of line types we have to deal with to 4 regions.
return; // Regions: slope > 1; 0 < slope < 1; -1 < slope < 0; slope < -1.
}
// Declare/Initialize data needed to draw line with midpoint algorithm.
int dx = x1 - x0;
int dy = y1 - y0; //dy = Negative when y0 is lower on screen than y2, because origin is top left.
print(y0 + " " + x0 + " " +y1 + " " + x1+ " x y \n");
print(dy + " " + dx + " dx dy\n");
// Handle vertical & horizontal lines...
if(dx == 0 || dy == 0){ // If slope is vertical or horizontal, create line with simple function.
while(y1 != y0){ // If vertical -> Paint by incrementing/decrementing y until points connect.
if(y1 > y0){ // If new point is above -> Draw upwards.
y0 = y0 + 1;
point(x0, y0);
}
else{ // It new point below -> Draw downwards.
y0 = y0 - 1;
point(x0, y0);
}
}
while(x1 != x0){ // If horizontal -> Paint by incrementing x until points connect (will be left to right line always).
x0 = x0 + 1;
point(x0, y0);
}
return;
}
// Handle slanted lines...
double tempDX = x1 - x0;
double tempDY = y1 - y0; // Had to create dx and dy as doubles because typecasting dy/dx to a double data type wasn't working.
double m = (-tempDY / tempDX); // m = line slope. (Note - The dy value is negative because positive y is downwards on the screen.)
print("SLOPE CALCULATED: " + m + "\n");
int deltaN = (2 * -dx); // deltaX is the amount to increment d after choosing the next pixel on the line.
int deltaNE = (2 * (-dy - dx)); // ...where X is the direction moved for that next pixel.
int deltaE = (2 * -dy); // deltaX variables are used below to plot line.
int deltaSE = (2 * (dy + dx));
int deltaS = (2 * dx);
int x = x0;
int y = y0;
int d = 0; // d = Amount d-value changes from pixel to pixel. Depends on slope.
int region = 0; // region = Variable to store slope region. Different regions require different formulas.
if(m > 1){ // if-statement: Initializes d, depending on the slope of the line.
d = -dy - (2 * dx); // If slope is 1-Infiniti. -> Use NE/N initialization for d.
region = 1;
}
else if(m == 1)
region = 2;
else if(m > 0 && m < 1){
d = (2 * -dy) - dx; // If slope is 0-1 -> Use NE/E initialization for d.
region = 3;
}
else if(m < 0 && m > -1){
d = (2 * dy) + dx; // If slope is 0-(-1) -> Use E/SE initliazation for d.
region = 4;
}
else if(m == -1)
region = 5;
else if(m < -1){
d = dy + (2 * dx); // If slope is (-1)-(-Infiniti) -> Use SE/S initialization for d.
region = 6;
}
while(x < x1){ // Until points are connected...
if(region == 1){ // If in region one...
if(d <= 0){ // and d<=0...
d += deltaNE; // Add deltaNE to d, and increment x and y.
x = x + 1;
y = y - 1;
}
else{
d += deltaN; // If d > 0 -> Add deltaN, and increment y.
y = y - 1;
}
}
else if(region == 2){
x = x + 1;
y = y - 1;
}
else if(region == 3){
if(d <= 0){
d += deltaE;
x = x + 1;
}
else{
d += deltaNE;
x = x + 1;
y = y - 1;
}
}
else if(region == 4){
if(d <= 0){
d += deltaSE;
x = x + 1;
y = y + 1;
}
else{
d += deltaE;
x = x + 1;
}
}
else if(region == 5){
x = x + 1;
y = y + 1;
}
else if(region == 6){
if(d <= 0){
d += deltaSE;
x = x + 1;
y = y + 1;
}
else{
d += deltaS;
y = y + 1;
}
}
point(x, y);
}
return;
}
When programs pause, look for while() loops that don't resolve properly. I inserted the following println statements into your while loop to print out what was happening. Then I recreated the problematic condition and quit the program, and checked the console for signs of what was going wrong.
println("top of the while loop to ya...");
println("x: " + x + ", x1: " + x1);
println("region: " + region + ", d: " + d);
It looks like region 6 is causing the pausing problem. If d > 0, it is never decreased and x is never increased, so there is no way to satisfy the while condition.
With the same set of statements you can troubleshoot the inaccurate line issue. It occurs in region 4, but I'll leave the details as an exercise.