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.
Related
What I'm trying to do is take 2 mouceclick input which gives me pixel coordinate x[0],y[0] and x[1],y[1]. Then I get a queue of array containing pixels coordinate of every pixel where the line joining these points would make. Don't need the line to be seen at all.
I decided to take the slope prospective such that 1 pixel change in x coordinate would change
(x[1]-x[0])]/(y[1]-y[0]) in y coordinate. I keep getting arithmetic error.
Edit: Used the DDA algorithm and still getting / by zero error even if all values is pre-asigned to something non-zero.
Queue<int[]> queue=new LinkedList<int[]>();
int dx = Math.abs(x[1] - x[0]);
int dy = Math.abs(y[1] - y[0]);
int sx = (x[0] < x[1]) ? 1 : -1;
int sy = (y[0] < y[1]) ? 1 : -1;
int err = dx / dy;
int[] tog= {x[0],y[0]};
queue.add(tog); //1st pixel into queue. nothing else
while(true) {
if (x[0] == x[1] && y[0] == y[1]) {
break;
}
int e2 = 2 * err;
if (e2 > -dy) {
err = err - dy;
x[0] = x[0] + sx;
}
if (e2 < dx) {
err = err + dx;
y[0] = y[0] + sy;
}
tog[0]= x[0];
tog[1]= y[0];
queue.add(tog);
}
System.out.println(queue);
Thanks to the comment on using DDA, the problem I got is now fixed. I have used the following code for the counting of the pixels and storing their coordinates.
I put this code inside the action listener for a mouse click.
private void counter() {//---------counter takes arguments x and y which are array that contain x1x2 and y1y2 coordinates of 1st and 2nd click
int dx = (x[1] - x[0]);
int dy = (y[1] - y[0]);//---------makes it applicable for both inclinations (if we add up there a math.abs it would work on only the positive inclination line )
step = Math.abs(dx) > Math.abs(dy) ? Math.abs(dx) : Math.abs(dy);
//------counts howmany pixels are to be recorded
float Xinc = dx / (float) step;//----slope change with respect to x axis
float Yinc = dy / (float) step;//----slope change with respect to y axis
tog= new int[step][3];
tog[0][0]=x[0]; tog[0][1]=y[0];
tog[0][2]= (black[0]!=0) ? 1 : 0;//------------Tertiary operator where the condition is true, then while is true
//---------------------------------------------------------------send value of x1 and y1 to listOfCoordinates
float xt=x[0],yt=y[0]; int i=0, j=1;
//-------------to get all the coordinates between the 2 points1111
System.out.println(tog[0][0]+" "+tog[0][1]+" "+tog[0][2]);
while (j<step){
if(i==2) i=0;
xt += Xinc;
yt += Yinc;
tog[j][i] = (int)xt;//------tog is the array where you store the coordinates of each pixel that overlaps the line made if the clicked points are connected
tog[j][i+1] = (int)yt;
j++;
}
//-------print tog here to see if it has the coordinates or not for check
}
I have a 3D model and I need to rotate its vertices around the Y axis (The axis going straight up in my case). For example lets say i have the vert
(3,2,3)(x,y,z) and when i rotate around the Y axis only the x and z's will change. how could I implement this in java using degrees? Thanks in advance!
(FYI) this is for rotating the points on my hitbox. Each "box" is just a triangle but wrapped in a cube so i can just check if a point is in the cube. This is done per triangle per model. This works perfectly because im able to walk through meshes with holes in them and everything. However, if any rotation is applied weird things start to happen.
Edit: here is my code using Andys method
public static boolean checkPointCollision(Vector3f pos){
boolean hit=false;
float px=Math.round(pos.x);
float py=Math.round(pos.y);
float pz=Math.round(pos.z);
px=pos.x;
py=pos.y;
pz=pos.z;
long startTime=System.currentTimeMillis();
float xmin,ymin,zmin,xmax,ymax,zmax,scale,rot;
//Cube Collisions
for (Entity entity : entities) {
int colID=entity.getCollisionIndex();
boolean entHasHitbox = entity.hasHitbox();
if(colID!=-1 && hit==false && entHasHitbox){
//Gets the entitys variables
scale = entity.getScale();
rot = entity.getRotY();
//Converts to radians
rot = (float) Math.toRadians(rot);
xmin = 0;
ymin = 0;
zmin = 0;
xmax = 0;
ymax = 0;
zmax = 0;
switch(entity.getCollisionType()){
case 1:
if(entHasHitbox){
//Gets the entities hitbox
List<Vector3f> hitboxMins = entity.getHitboxMin();
List<Vector3f> hitboxMaxs = entity.getHitboxMax();
for (int i = 0; i < hitboxMins.size(); i++) {
//Gets the entities hitbox points
Vector3f min = hitboxMins.get(i);
Vector3f max = hitboxMaxs.get(i);
//Sets all local position vars to the hitboxes mins and maxes
xmin = min.x;
ymin = min.y;
zmin = min.z;
xmax = max.x;
ymax = max.y;
zmax = max.z;
//Applies the models scale
xmin *=scale;
ymin *=scale;
zmin *=scale;
xmax *=scale;
ymax *=scale;
zmax *=scale;
//Rotates points
float nxmin = (float) (Math.cos(rot) * xmin - Math.sin(rot) * zmin);
float nzmin = (float) (Math.sin(rot) * xmin + Math.cos(rot) * zmin);
float nxmax = (float) (Math.cos(rot) * xmax - Math.sin(rot) * zmax);
float nzmax = (float) (Math.sin(rot) * xmax + Math.cos(rot) * zmax);
//Sets old points to new ones
xmin = nxmin;
zmin = nzmin;
xmax = nxmax;
zmax = nzmax;
//Increase local points to the entitys world position
xmin += entity.getPosition().x;
xmax += entity.getPosition().x;
ymin += entity.getPosition().y;
ymax += entity.getPosition().y;
zmin += entity.getPosition().z;
zmax += entity.getPosition().z;
//Debug
if(entities.get(17)==entity){//entities.get(17).increaseRotation(0, 10, 0);
System.out.println(xmin+","+ymin+","+zmin);
}
//Check if point is in the hitbox
if(px>=xmin && px<=xmax
&& py>=ymin && py<=ymax
&& pz>=zmin && pz<=zmax)
{
hit=true;
//Ends to loop
i=hitboxMins.size();
}
}
}
break;
}
}
}
long endTime = System.currentTimeMillis()-startTime;
if(endTime>10){
System.out.println("Delay in Point Collision");
}
return hit;
}
Multiply your points by the following matrix:
[ c 0 -s ]
[ 0 1 0 ]
[ s 0 c ]
i.e.
[newx] [ c 0 -s ] [x]
[newy] = [ 0 1 0 ] [y]
[newz] [ s 0 c ] [z]
where (x, y, z) are your original coordinates, (newx, newy, newz) are your rotated coordinates, and c = cos(angle) and s = sin(angle). Note that Java's trig functions take their parameters as radians, so you need to convert the angle in degrees appropriately.
If you've not used matrices before, this is equivalent to the following three expressions:
newx = c * x - s * z
newy = y
newz = s * x + c * z
Let's first start off with what I am trying to do. I would like to be able to take PNG file with a transparent background and find anywhere from 90 to 360 points along the edge of the subject of the image. Here is a rough example of what I mean. Given this image of Mario and Yoshi:
I want to make a circle that is centered at the center of the image with a diameter slightly larger than the largest side of the image to serve as a reference. Then, I want to go around the circle at set intervals, and trace a line towards the center until it hits a non-transparent pixel. Here is what that would look like:
I have attempted to implement this a few different times, all of which failed, and I was hoping to get some guidance or insight as to what I am doing wrong. Here is an image of the math I am using behind the code (sorry if the quality is not great, I don't have a scanner):
The Line 1 is either the top, bottom, left or right edge of the image, and Line 2 goes through the center of the circle at the given angle. The point where lines 1 and 2 intersect should be on the edge of the image, and is where we should start looking for the edge of the image's subject.
Here is the code that I came up with from this idea. I did it in Java because BufferedImage is really easy to use, but I am going to translate this over to C# (XNA) for the final product.
public class Mesh {
private int angleA, angleB, angleC, angleD;
private BufferedImage image;
private Point center;
public ArrayList<Point> points = new ArrayList<>();
public Mesh(BufferedImage image) {
center = new Point(image.getWidth() / 2, image.getHeight() / 2);
angleA = (int) (Math.atan(center.y / center.x) * (180 / Math.PI));
angleB = 180 - angleA;
angleC = 180 + angleA;
angleD = 360 - angleA;
this.image = image;
for(int angle = 0; angle <= 360; angle+=4){
Point point = getNext(angle);
if(point != null) points.add(point);
}
}
private Point getNext(int angle) {
double radians = angle * Math.PI / 180;
double xStep = Math.cos(radians);
double yStep = Math.sin(radians);
int addX = angle >= 90 && angle <= 270 ? 1 : -1;
int addY = angle >= 0 && angle <= 180 ? 1 : -1;
double x, y;
if (xStep != 0) {
double slope = yStep / xStep;
double intercept = center.y - (slope * center.x);
if (angle >= angleA && angle <= angleB) {
y = 0;
x = -intercept / slope;
} else if (angle > angleB && angle < angleC) {
x = 0;
y = intercept;
} else if (angle >= angleC && angle <= angleD) {
y = image.getHeight() - 1;
x = (y - intercept) / slope;
} else {
x = image.getWidth() - 1;
y = slope * x + intercept;
}
} else {
x = center.x;
y = angle <= angleB ? 0 : image.getHeight();
}
if (x < 0) x = 0;
if (x > image.getWidth() - 1) x = image.getWidth() - 1;
if (y < 0) y = 0;
if (y > image.getHeight() - 1) y = image.getHeight() - 1;
double distance = Math.sqrt(Math.pow(x - center.x, 2) + Math.pow(y - center.y, 2));
double stepSize = Math.sqrt(Math.pow(xStep, 2) + Math.pow(yStep, 2));
int totalSteps = (int) Math.floor(distance / stepSize);
for (int step = 0; step < totalSteps; step++) {
int xVal = (int) x;
int yVal = (int) y;
if(xVal < 0) xVal = 0;
if(xVal > image.getWidth() -1) xVal = image.getWidth() -1;
if(yVal < 0) yVal = 0;
if(yVal > image.getHeight()-1) yVal = image.getHeight() -1;
int pixel = image.getRGB(xVal, yVal);
if ((pixel >> 24) == 0x00) {
x += (Math.abs(xStep) * addX);
y += (Math.abs(yStep) * addY);
} else {
return new Point(xVal, yVal);
}
}
return null;
}
}
The algorithm should be returning all positive points that are all ordered in counterclockwise rotation (and non-overlapping) but I have failed to get the desired output (this being my most recent attempt) so just to restate the question, is there a formalized way of doing this, or can someone find the mistake I made in my logic. For visual reference, the Mario and Yoshi Traced image is sort of what the final output should look like, but with many more points (which would give more detail to the mesh).
I am trying to find image in an image. I do this for desktop automation. At this moment, I'm trying to be fast, not precise. As such, I have decided to match similar image solely based on the same average color.
If I pick several icons on my desktop, for example:
And I will search for the last one (I'm still wondering what this file is):
You can clearly see what is most likely to be the match:
In different situations, this may not work. However when image size is given, it should be pretty reliable and lightning fast.
I can get a screenshot as BufferedImage object:
MSWindow window = MSWindow.windowFromName("Firefox", false);
BufferedImage img = window.screenshot();
//Or, if I can estimate smaller region for searching:
BufferedImage img2 = window.screenshotCrop(20,20,50,50);
Of course, the image to search image will be loaded from template saved in a file:
BufferedImage img = ImageIO.read(...whatever goes in there, I'm still confused...);
I explained what all I know so that we can focus on the only problem:
Q: How can I get average color on buffered image? How can I get such average color on sub-rectangle of that image?
Speed wins here. In this exceptional case, I consider it more valuable than code readability.
I think that no matter what you do, you are going to have an O(wh) operation, where w is your width and h is your height.
Therefore, I'm going to post this (naive) solution to fulfil the first part of your question as I do not believe there is a faster solution.
/*
* Where bi is your image, (x0,y0) is your upper left coordinate, and (w,h)
* are your width and height respectively
*/
public static Color averageColor(BufferedImage bi, int x0, int y0, int w,
int h) {
int x1 = x0 + w;
int y1 = y0 + h;
long sumr = 0, sumg = 0, sumb = 0;
for (int x = x0; x < x1; x++) {
for (int y = y0; y < y1; y++) {
Color pixel = new Color(bi.getRGB(x, y));
sumr += pixel.getRed();
sumg += pixel.getGreen();
sumb += pixel.getBlue();
}
}
int num = w * h;
return new Color(sumr / num, sumg / num, sumb / num);
}
There is a constant time method for finding the mean colour of a rectangular section of an image but it requires a linear preprocess. This should be fine in your case. This method can also be used to find the mean value of a rectangular prism in a 3d array or any higher dimensional analog of the problem. I will be using a gray scale example but this can be easily extended to 3 or more channels simply by repeating the process.
Lets say we have a 2 dimensional array of numbers we will call "img".
The first step is to generate a new array of the same dimensions where each element contains the sum of all values in the original image that lie within the rectangle that bounds that element and the top left element of the image.
You can use the following method to construct such an image in linear time:
int width = 1920;
int height = 1080;
//source data
int[] img = GrayScaleScreenCapture();
int[] helperImg = int[width * height]
for(int y = 0; y < height; ++y)
{
for(int x = 0; x < width; ++x)
{
int total = img[y * width + x];
if(x > 0)
{
//Add value from the pixel to the left in helperImg
total += helperImg[y * width + (x - 1)];
}
if(y > 0)
{
//Add value from the pixel above in helperImg
total += helperImg[(y - 1) * width + x];
}
if(x > 0 && y > 0)
{
//Subtract value from the pixel above and to the left in helperImg
total -= helperImg[(y - 1) * width + (x - 1)];
}
helperImg[y * width + x] = total;
}
}
Now we can use helperImg to find the total of all values within a given rectangle of img in constant time:
//Some Rectangle with corners (x0, y0), (x1, y0) , (x0, y1), (x1, y1)
int x0 = 50;
int x1 = 150;
int y0 = 25;
int y1 = 200;
int totalOfRect = helperImg[y1 * width + x1];
if(x0 > 0)
{
totalOfRect -= helperImg[y1 * width + (x0 - 1)];
}
if(y0 > 0)
{
totalOfRect -= helperImg[(y0 - 1) * width + x1];
}
if(x0 > 0 && y0 > 0)
{
totalOfRect += helperImg[(y0 - 1) * width + (x0 - 1)];
}
Finally, we simply divide totalOfRect by the area of the rectangle to get the mean value:
int rWidth = x1 - x0 + 1;
int rheight = y1 - y0 + 1;
int meanOfRect = totalOfRect / (rWidth * rHeight);
Here's a version based on k_g's answer for a full BufferedImage with adjustable sample precision (step).
public static Color getAverageColor(BufferedImage bi) {
int step = 5;
int sampled = 0;
long sumr = 0, sumg = 0, sumb = 0;
for (int x = 0; x < bi.getWidth(); x++) {
for (int y = 0; y < bi.getHeight(); y++) {
if (x % step == 0 && y % step == 0) {
Color pixel = new Color(bi.getRGB(x, y));
sumr += pixel.getRed();
sumg += pixel.getGreen();
sumb += pixel.getBlue();
sampled++;
}
}
}
int dim = bi.getWidth()*bi.getHeight();
// Log.info("step=" + step + " sampled " + sampled + " out of " + dim + " pixels (" + String.format("%.1f", (float)(100*sampled/dim)) + " %)");
return new Color(Math.round(sumr / sampled), Math.round(sumg / sampled), Math.round(sumb / sampled));
}
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.