Android canvas. How to set circles at random position? - java

Below code generated and save position x y to HashMap and check collision two circles;
HashMap<Integer, Float> posX = new HashMap<>();
HashMap<Integer, Float> posY = new HashMap<>();
int numberOfCircle = 8;
for(int i=0; i < numberOfCircle; i ++){
// boolean flag = false;
while (true){
float x =random.nextInt(width - raduis/2) + raduis/2f;
float y =random.nextInt(height - raduis/2) + raduis/2f;
if(!posX.containsValue(x) && !posY.containsValue(y)){
if(i == 0){
posX.put(i, x);
posY.put(i, y);
break;
}
if(i > 0){
double distance = Math.sqrt(((posX.get(i - 1) - x) * (posX.get(i - 1) - x)) + ((posY.get(i - 1) - y) * ( posY.get(i - 1) - y)));
if (distance > raduis+raduis) {
posX.put(i, x);
posY.put(i, y);
Log.d(TAG, i + " xPos=" + posX.get(i) + " yPos=" + posY.get(i) + " distance=" + distance);
break;
}
if(numberOfCircle == posX.size()) break;
}
}
}
}
This code work only if circle count=2; But when circle count > 2 i have collision; How to check current generated position for each in HashMap?
For example:
xPos = {5, 10, 3}
yPos = {10, 33, 5}
generated position x=6, y=10;
calculate distance between x=6, y=10 with all positions in Map. If distance < radius+radius generate new position while distance > radius+radius;
Update ========================>
My code work like
I want like this
output: distance equal between current generated position(X, Y) and previous position(X, Y). I want to check between current generated x, y with all added positons in HashMap.
D/DEBUG DATA ===>: 1 xPos=432.0 yPos=411.0 distance=390.6430595825299
D/DEBUG DATA ===>: 2 xPos=316.0 yPos=666.0 distance=280.1446055165082
D/DEBUG DATA ===>: 3 xPos=244.0 yPos=83.0 distance=587.4291446634223
D/DEBUG DATA ===>: 4 xPos=214.0 yPos=551.0 distance=468.96055271205915
D/DEBUG DATA ===>: 5 xPos=76.0 yPos=1011.0 distance=480.2540994098853
D/DEBUG DATA ===>: 6 xPos=289.0 yPos=868.0 distance=256.55019002136794
D/DEBUG DATA ===>: 7 xPos=494.0 yPos=988.0 distance=237.53947040439405
P.s Sorry so poor English.

Maybe something closer to this will work? I'm not entirely sure if it's what you want or if it will help, but I did a quick rewrite for clarity.
HashMap<Integer, Integer> posX = new HashMap<>();
HashMap<Integer, Integer> posY = new HashMap<>();
final int circlesToPlace = 8;
for(int i = 0 ; i < circlesToPlace ; i++){
// boolean flag = false;
while (true){
final int x = ThreadLocalRandom.current().nextInt((radius/2f), width + 1);
final int y = ThreadLocalRandom.current().nextInt((radius/2f), height + 1);
// Iterate over all other positions to ensure no circle intersects with
// the new circle.
for (int index = 0 ; index < posX.size() ; index++) {
// Calculate distance where d = sqrt((x2 - x1)^2 + (y2 - y1)^2)
final int otherX = posX.get(index);
final int otherY = posY.get(index);
int differenceX = otherX - x;
differenceX *= differenceX;
int differenceY = otherY - y;
differenceY *= differenceY;
final double distance = Math.sqrt(differenceX + differenceY);
if (distance > (radius * 2)) {
posX.put(i, x);
posY.put(i, y);
Log.d(TAG, i + " xPos=" + posX.get(i) + " yPos=" + posY.get(i) + " distance=" + distance);
break;
}
}
}
}

My variant. You don't need to do sqrt to compare distance, you can instead square the constant (d2 in my case).
Random random = new Random();
int numberOfCircle = 8, width = 400, height = 300;
int diameter = 51;
final float radius = diameter * 0.5f;
final float d2 = diameter * diameter;
List<Float> posX = new ArrayList<>(numberOfCircle);
List<Float> posY = new ArrayList<>(numberOfCircle);
while (posX.size() < numberOfCircle) { // till enough generated
// generate new coordinates
float x = random.nextInt(width - diameter) + radius;
float y = random.nextInt(height - diameter) + radius;
System.out.printf("Generated [%3.3f, %3.3f] ... ", x, y);
// verify it does not overlap/touch with previous circles
int j = 0;
while (j < posX.size()) {
float dx = posX.get(j) - x, dy = posY.get(j) - y;
float diffSquare = (dx * dx) + (dy * dy);
if (diffSquare <= d2) break;
++j;
}
// generate another pair of coordinates, if it does touch previous
if (j != posX.size()) {
System.out.println("collided.");
continue;
}
System.out.println("added.");
// not overlapping/touch, add as new circle
posX.add(x);
posY.add(y);
} // while (posX.size() < numberOfCircle)

I resolve like this
private class MyView extends View implements View.OnTouchListener{
List<Circle> randomCircles = new ArrayList<>();
int radius = new Circle().getRadius();
int randomCircleCount = 3;
Random random = new Random();
public MyView(Context context) {
super(context);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//Integer width = canvas.getWidth();
Integer width = canvas.getWidth();
Integer height = canvas.getHeight() - (radius);
// Integer height = canvas.getHeight()/2 + canvas.getHeight();
///randomCircles.clear();
Paint paint = new Paint();
paint.setStyle(Paint.Style.FILL);
paint.setColor(Color.BLACK);
canvas.drawPaint(paint);
Paint paintT = new Paint();
paintT.setTextSize(18f);
paintT.setAntiAlias(true);
paintT.setTextAlign(Paint.Align.CENTER);
while(randomCircles.size() < randomCircleCount){
randomCircle(width, height);
}
for(int i=0; i < randomCircleCount; i ++){
Circle circle = randomCircles.get(i);
float curPosX = randomCircles.get(i).getCx().floatValue();
float curPosY = randomCircles.get(i).getCy().floatValue();
int r = random.nextInt(256);
int g = random.nextInt(256);
int b = random.nextInt(256);
paint.setARGB(175, 77, 2, 200);
//if(r != 0 && g != 0 && b != 0) paint.setARGB(255, r, g, b);
canvas.drawCircle(curPosX, curPosY, radius, paint);
Rect bounds = new Rect();
String text = "" +i;
paintT.getTextBounds(text, 0, text.length(), bounds);
paint.setAntiAlias(true);
canvas.drawText(text, curPosX, curPosY, paintT);
circle.update(width, height);
//Log.d(TAG, "REDRAW");
}
///invalidate();
postInvalidateDelayed(100);
}
private void randomCircle(int width, int height) {
double x = getPosX(width, radius);
double y = getPosY(height,radius);
boolean hit = false;
for (int i = 0; i < randomCircles.size(); i++) {
Circle circle = randomCircles.get(i);
double dx = circle.getCx() - x;
double dy = circle.getCy() - y;
int r = circle.getRadius() + radius;
if (dx * dx + dy * dy <= r * r) {
Log.d(TAG, "dx=" + dx + " dy=" + dy);
hit = true;
}
}
if (!hit) {
Log.d(TAG, "Here!!!!!");
randomCircles.add(new Circle(x, y));
}
}
private Float getPosX(Integer width, Integer radius){
return random.nextInt(width - radius/2) + radius/2f;
}
private Float getPosY(Integer height, Integer radius){
return random.nextInt(height - radius/2) + radius/2f;
}

Related

circle collision practice, advice?

can anyone tell me what i'm doing wrong? i'm trying to get the circles to bounce off each other but they don't seem to be working.i keep making changes to fix the issue but that only makes more issues, whilst the main issue isn't resolved. have i used the wrong math's algorithm to check for collisions? or is it right and i have just made an error i cant seem to find? any help would be appreciated.
public float[][] CreateDots() {
if (first == true) {
for (int i = 0; i < dotNumber; i++) {
do{
dotX = r.nextInt(300);
dotY = r.nextInt(300);
dotWidth = r.nextFloat() * 50;
dotRadius = dotWidth / 2;
dotMass = r.nextFloat() / 10;
dotCentreX = dotX + dotRadius;
dotCentreY = dotY + dotRadius;
dotVelocityX = r.nextFloat();
dotVelocityY = r.nextFloat();
dots[i][0] = dotX;
dots[i][1] = dotY;
dots[i][2] = dotVelocityX;
dots[i][3] = dotVelocityY;
dots[i][4] = dotRadius;
dots[i][5] = dotCentreX;
dots[i][6] = dotCentreY;
dots[i][7] = dotMass;
dots[i][8] = dotWidth;
}while(collision == true);
}
first = false;
} else {
for (int i = 0; i < dotNumber; i++) {
dots[i][0] = dots[i][0] + dots[i][2];
dots[i][1] = dots[i][1] + dots[i][3];
if (dots[i][0] + dots[i][8] >= wallX) {
dots[i][2] = -dots[i][2];
}
if (dots[i][1] + dots[i][8] >= wallY) {
dots[i][3] = -dots[i][3];
}
if (dots[i][0] < 0) {
dots[i][2] = -dots[i][2];
}
if (dots[i][1] < 0) {
dots[i][3] = -dots[i][3];
}
}
}
repaint();
return dots;
}
public void bounce() {
collisionDot = false;
for (int i = 0; i < dotNumber; i++) {
for (int a = i + 1; a < dotNumber; a++) {
// difference between the x and y velocity of two dots
float xVelDiff = dots[i][2] - dots[a][2];
float yVelDiff = dots[i][3] - dots[a][3];
//difference between the centre x and y of two dots
float xDist = dots[i][5] - dots[a][5];
float yDist = dots[i][6] - dots[a][6];
System.out.println(xVelDiff + " * " + xDist + " + " + yVelDiff + " * " + yDist + " = "+ (xVelDiff * xDist + yVelDiff * yDist));
//not quite sure yet
if (xVelDiff * xDist + yVelDiff * yDist <= 0) {
angleCollision = (float) -Math.atan2(dots[a][0] - dots[i][0], dots[a][1] - dots[i][1]);
float mass = (dots[i][7] + dots[a][7]);
float mass2 = (dots[i][7] - dots[a][7]);
// x and y velocity and angle of collision for the two dots
float[] u1 = rotate(dots[i][2], dots[i][3], (float) angleCollision);
float[] u2 = rotate(dots[a][2], dots[a][3], (float) angleCollision);
//Velocity of dot 1
float[] v1 = new float[2];
v1[0] = u1[0] * mass2 / mass + u2[0] * 2 * dots[a][7] / (mass);
v1[1] = u1[1];
// velocity of dot 2
float[] v2 = new float[2];
v2[0] = u2[0] * mass2 / mass + u1[0] * 2 * dots[a][7] / (mass);
v2[1] = u2[1];
// final velocity of two colliding dots is:
float[] vFinal1 = rotate(v1[0], v1[1], (float) -angleCollision);;
float[] vFinal2 = rotate(v2[0], v2[1], (float) -angleCollision);;
if (a != i && !(dots[a][0] == 0 && dots[a][1] == 0)) {
// if the x and y distance between the two dots centres is less than their radii combined then the dots have collided
boolean thisCollision = Math.pow(xDist, 2) + Math.pow(yDist, 2) <= Math.pow((dots[a][4] + dots[i][4]), 2);
//if the dots collided, create new final velocity's from the angle of collision and the x and y velocitys at collision
if (thisCollision) {
collisionDot = true;
dots[i][2] = vFinal1[0];
dots[i][3] = vFinal1[1];
dots[a][2] = vFinal2[0];
dots[a][3] = vFinal2[1];
return;
}
}
}
}
}
}
public float[] rotate(float velocityX, float velocityY, float angle) {
float x1 = (float) (velocityX * Math.cos(angle) - velocityY * Math.sin(angle));
float y1 = (float) (velocityX * Math.sin(angle) - velocityY * Math.cos(angle));
float vel[] = new float[2];
vel[0] = x1;
vel[1] = y1;
return vel;
}

Dynamic drawing on canvas taking too long

I wanted to draw a grid of quads with an animated colour property but ended up with code that is too slow just to create a basic structure of an image, not to mention to animate it. Also, the image is drawing, not as it finished to compute, but in the process by parts. How can I fix it? Here's onDraw(Canvas canvas) method:
protected void onDraw(Canvas canvas) {
canvas.drawColor(Color.BLACK);
final int height = canvas.getHeight();
final int width = canvas.getWidth();
Bitmap.Config conf = Bitmap.Config.RGB_565;
Bitmap bitmap = Bitmap.createBitmap(width, height, conf);
new Thread(() -> {
int cells_amount = 0;
float cells_vertical = 0;
float cells_horizontal = 0;
cells_vertical = (float) height / CELL_SIZE;
int y = 0;
if (cells_vertical % 1 > 0) {
y = Math.round(CELL_SIZE * -(cells_vertical % 1));
cells_vertical = (int) (cells_vertical + 1);
}
cells_horizontal = (float) width / CELL_SIZE;
int x = 0;
if (cells_horizontal % 1 > 0) {
x = Math.round(CELL_SIZE * -(cells_horizontal % 1));
cells_horizontal = (int) (cells_horizontal + 1);
}
cells_amount = (int) (cells_horizontal * cells_vertical);
Canvas c = new Canvas(bitmap);
c.drawColor(Color.BLACK);
int x_preserved = x;
Paint textPaint = new Paint();
textPaint.setColor(Color.parseColor("#EEEEEE"));
for (int i = 0; i < cells_amount; i++) {
Rect rect = new Rect(x, y, x + CELL_SIZE, y + CELL_SIZE);
Paint framePaint = new Paint();
framePaint.setColor(Color.parseColor("#1F1F1F"));
framePaint.setStyle(Paint.Style.STROKE);
framePaint.setStrokeWidth(1);
c.drawRect(rect, framePaint);
if (random.nextBoolean()) {
String output = String.format(Locale.getDefault(), "%d", "+");
int cx = (int) (x + (CELL_SIZE / 2) - (textPaint.measureText(output) / 2));
int cy = (int) ((y + (CELL_SIZE / 2)) - ((textPaint.descent() + textPaint.ascent()) / 2));
c.drawText(output, cx, cy, textPaint);
}
if (i % cells_horizontal == 0 && i >= cells_horizontal) {
y += CELL_SIZE;
x = x_preserved;
} else {
x += CELL_SIZE;
}
}
}).start();
canvas.drawBitmap(bitmap, 0, 0, null);
}

I try to set some values to an array of integers but the values won't stick

Here is my code:
int[][] coordX = new int[10000][10000];
int[][] coordY = new int[10000][10000];
int cX = 0;
int cY = 0;
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
int width = (int) (screenSize.getWidth());
int height = (int) (screenSize.getHeight());
BufferedImage dirt = null;
{
try {
dirt = ImageIO.read(new File("res/dirt.png"));
} catch (IOException e) {
}
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.RED);
for (int y = 0; y <= height; y = y + 50) {
for (int x = 0; x <= width; x = x + 50) {
g.drawImage(dirt, x, y, 50, 50, Color.BLACK, this);
coordX[cX][cY] = cX;
coordY[cX][cY] = cY;
cX = cX + 1;
System.out.println("X=" + coordX[cX][cY]);
}
cY = cY + 1;
cX = 0;
System.out.println("Y=" + coordY[cX][cY]);
}
}
The console outputs all the "X=" values as zero, and same for the "Y=", however, when I print the "cX" and "cY" values on their own, they are counting as intended. The problem only seems to arise when I try to store the "cX" and "cY" values in an array.
You're incrementing cX prior to printing
cX = cX + 1;
System.out.println("X=" + coordX[cX][cY]);
i.e. you're not printing the element you've just populated. You'll always get the default (uninitialised) int element value (0).
You're incrementing cX and cY before printing. Try this:
coordX[cX][cY] = cX;
coordY[cX][cY] = cY;
System.out.println("X=" + coordX[cX][cY]);
System.out.println("Y=" + coordY[cX][cY]);
cX = cX + 1;

Java perlin noise

I need to make perlin noise like in AS3.0:
bitmapData.perlinNoise(baseX, baseY, numOctaves,
randomSeed, stitch, fractalNoise, grayScale, offsets);
It's seamless noise:
I found a lot of material about it, but I can't make it like in my as3.0 image. Java code:
import java.awt.Color;
import java.awt.Graphics;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
#SuppressWarnings("serial")
public class Noise extends JPanel{
public static int octaves = 4;
public static int size = 128;
public static float[][][] noise = new float[size][size][octaves];
public static float[][] perlinnoise = new float[size][size];
public static float p = (float) 1/4;
public static Random gen = new Random();
public static float GenerateNoise() {
return gen.nextFloat();
}
public static float SmoothNoise(int x, int y, int z) {
try{
float corners = (noise[x - 1][y - 1][z] + noise[x + 1][y - 1][z] + noise[x - 1][y + 1][z] + noise[x + 1][y + 1][z]) / 16;
float sides = (noise[x - 1][y][z] + noise[x + 1][y][z] + noise[x][y - 1][z] + noise[x][y + 1][z]) / 8;
float center = noise[x][y][z] / 4;
return corners + sides + center;
}catch(Exception e) {
return 0;
}
}
public static float InterpolatedNoise(float x, float y, int pX, int pY, int pZ) {
int intX = (int) x;
int intY = (int) y;
float fracX = x - intX;
float fracY = y - intY;
float v1 = SmoothNoise(pX, pY, pZ);
float v2 = SmoothNoise(pX + 1, pY, pZ);
float v3 = SmoothNoise(pX, pY + 1, pZ);
float v4 = SmoothNoise(pX + 1, pY + 1, pZ);
float i1 = Interpolate(v1, v2, fracX);
float i2 = Interpolate(v3, v4, fracX);
return Interpolate(i1, i2, fracY);
}
public static float Interpolate(float a, float b, float x) {
float ft = (float) (x * 3.1415927);
float f = (float) ((1 - Math.cos(ft)) * 0.5);
return (float) (a * (1 - f) + b * f);
}
public static float Perlin2D(float x, float y, int posX, int posY, int posZ) {
float total = 0;
for(int i = 0; i < octaves; i++) {
double f = Math.pow(2, i);
double a = Math.pow(p, i);
total = (float) (total + InterpolatedNoise((float)(x * f), (float)(y * f), posX, posY, posZ) * a);
}
return total;
}
public static void main(String [] args) {
for(int z = 0; z < octaves; z++) {
for(int y = 0; y < size; y++) {
for(int x = 0; x < size; x++) {
noise[x][y][z] = GenerateNoise();
}
}
}
for(int z = 0; z < octaves; z++) {
for(int y = 0; y < size; y++) {
for(int x = 0; x < size; x++) {
perlinnoise[x][y] = Perlin2D(x / (size - 1), y / (size - 1), x, y, z) / octaves;
}
}
}
JFrame f = new JFrame("Perlin Noise");
f.setSize(400, 400);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(new Noise());
f.setVisible(true);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
for(int y = 0; y < size; y++) {
for(int x = 0; x < size; x++) {
g.setColor(new Color(perlinnoise[x][y], perlinnoise[x][y], perlinnoise[x][y]));
g.fillRect(x * 2, y * 2, 2, 2);
}
}
repaint();
}
}
Help please!
The trick is, the Perlin noise does not use pseudo-random generator, it uses a function that takes an argument and returns predefined value for that argument, but when argument shifts by 1, the value jumps almost randomly. Check the sources for the permutation formulae, the init() method makes a permutation that then is used to make the entire noise.

Application not working as expected

I have a standalone Java application below that is:
Generating a random line
Applied to a 2D grid where each cell value is the distance along the line perpindicular to the line
Finds the rise/run and attempts to calculate the original linear equation from the grid
Applies new line to another grid and prints out the greatest difference compared to the first grid
I expected the two grids to have identical values. The gradient lines may be different since the lines can extend outside the area of the grid, but should be similar and in two cases identical.
So is the problem a poor understanding of math, a bug in my code or a misunderstanding of floating point values?
import java.awt.geom.Point2D;
import java.awt.geom.Line2D;
import java.util.Iterator;
import java.util.ArrayList;
public final class TestGradientLine {
private static int SIZE = 3;
public TestGradientLine() {
super();
}
//y = mx + b
//b = y - mx
//m is rise / run = gradient
//width and height of bounding box
//for a box 10x10 then width and height are 9,9
public static Line2D getGradientLine(double run, double rise, double width, double height, double x, double y) {
if (run == 0 && rise == 0) {
return new Line2D.Double(x, y, x + width, y + height);
}
//calculate hypotenuse
//check for a vertical line
if (run == 0) {
return new Line2D.Double(x, y, x, y + height);
}
//check for a horizontal line
if (rise == 0) {
return new Line2D.Double(x, y, x + width, y);
}
//calculate gradient
double m = rise / run;
Point2D start;
Point2D opposite;
if (m < 0) {
//lower left
start = new Point2D.Double(x, y + height);
opposite = new Point2D.Double(x + width, y);
} else {
//upper left
start = new Point2D.Double(x, y);
opposite = new Point2D.Double(x + width, y + height);
}
double b = start.getY() - (m * start.getX());
//now calculate another point along the slope
Point2D next = null;
if (m > 0) {
next = new Point2D.Double(start.getX() + Math.abs(run), start.getY() + Math.abs(rise));
} else {
if (rise < 0) {
next = new Point2D.Double(start.getX() + run, start.getY() + rise);
} else {
next = new Point2D.Double(start.getX() - run, start.getY() - rise);
}
}
final double actualWidth = width;
final double actualHeight = height;
final double a = Math.sqrt((actualWidth * actualWidth) + (actualHeight * actualHeight));
extendLine(start, next, a);
Line2D gradientLine = new Line2D.Double(start, next);
return gradientLine;
}
public static void extendLine(Point2D p0, Point2D p1, double toLength) {
final double oldLength = p0.distance(p1);
final double lengthFraction =
oldLength != 0.0 ? toLength / oldLength : 0.0;
p1.setLocation(p0.getX() + (p1.getX() - p0.getX()) * lengthFraction,
p0.getY() + (p1.getY() - p0.getY()) * lengthFraction);
}
public static Line2D generateRandomGradientLine(int width, int height) {
//so true means lower and false means upper
final boolean isLower = Math.random() > .5;
final Point2D start = new Point2D.Float(0, 0);
if (isLower) {
//change origin for lower left corner
start.setLocation(start.getX(), height - 1);
}
//radius of our circle
double radius = Math.sqrt(width * width + height * height);
//now we want a random theta
//x = r * cos(theta)
//y = r * sin(theta)
double theta = 0.0;
if (isLower) {
theta = Math.random() * (Math.PI / 2);
} else {
theta = Math.random() * (Math.PI / 2) + (Math.PI / 2);
}
int endX = (int)Math.round(radius * Math.sin(theta));
int endY = (int)Math.round(radius * Math.cos(theta)) * -1;
if (isLower) {
endY = endY + (height - 1);
}
final Point2D end = new Point2D.Float(endX, endY);
extendLine(start, end, radius);
return new Line2D.Float(start, end);
}
public static Point2D getNearestPointOnLine(Point2D end, Line2D line) {
final Point2D point = line.getP1();
final Point2D start = line.getP2();
double a = (end.getX() - point.getX()) * (start.getX() - point.getX()) + (end.getY() - point.getY()) * (start.getY() - point.getY());
double b = (end.getX() - start.getX()) * (point.getX() - start.getX()) + (end.getY() - start.getY()) * (point.getY() - start.getY());
final double x = point.getX() + ((start.getX() - point.getX()) * a)/(a + b);
final double y = point.getY() + ((start.getY() - point.getY()) * a)/(a + b);
final Point2D result = new Point2D.Double(x, y);
return result;
}
public static double length(double x0, double y0, double x1, double y1) {
final double dx = x1 - x0;
final double dy = y1 - y0;
return Math.sqrt(dx * dx + dy * dy);
}
public static void main(String[] args) {
final Line2D line = generateRandomGradientLine(SIZE, SIZE);
System.out.println("we're starting with line " + line.getP1() + " " + line.getP2());
double[][] region = new double[SIZE][SIZE];
//load up the region with data from our generated line
for (int x = 0; x < SIZE; x++) {
for (int y = 0; y < SIZE; y++) {
final Point2D point = new Point2D.Double(x, y);
final Point2D nearestPoint = getNearestPointOnLine(point, line);
if (nearestPoint == null) {
System.err.println("uh -oh!");
return;
}
final double distance = length(line.getP1().getX(),
line.getP1().getY(), nearestPoint.getX() + 1,
nearestPoint.getY() + 1);
region[x][y] = distance;
}
}
//now figure out what our line is from the region
double runTotal = 0;
double riseTotal = 0;
double runCount = 0;
double riseCount = 0;
for (int x = 0; x < SIZE; x++) {
for (int y = 0; y < SIZE; y++) {
if (x < SIZE - 1) {
runTotal += region[x + 1][y] - region[x][y];
runCount++;
}
if (y < SIZE - 1) {
riseTotal += region[x][y + 1] - region[x][y];
riseCount++;
}
}
}
double run = 0;
if (runCount > 0) {
run = runTotal / runCount;
}
double rise = 0;
if (riseCount > 0) {
rise = riseTotal / riseCount;
}
System.out.println("rise is " + rise + " run is " + run);
Line2D newLine = getGradientLine(run, rise, SIZE - 1, SIZE - 1, 0, 0);
System.out.println("ending with line " + newLine.getP1() + " " + newLine.getP2());
double worst = 0.0;
int worstX = 0;
int worstY = 0;
for (int x = 0; x < SIZE; x++) {
for (int y = 0; y < SIZE; y++) {
final Point2D point = new Point2D.Double(x, y);
final Point2D nearestPoint = getNearestPointOnLine(point, newLine);
if (nearestPoint == null) {
System.err.println("uh -oh!");
return;
}
final double distance = length(line.getP1().getX(),
line.getP1().getY(), nearestPoint.getX() + 1,
nearestPoint.getY() + 1);
final double diff = Math.abs(region[x][y] - distance);
if (diff > worst) {
worst = diff;
worstX = x;
worstY = y;
}
}
}
System.out.println("worst is " + worst + " x: " + worstX + " y: " + worstY);
}
}
I think I have fixed your program.
a) I took out the integer cast.
b) I removed all the 'x + 1' and 'x - 1' fudges you had used.
I think when dealing with floats and doubles, subtracting '1' from the end of a line is a No-No! What is 1 anyway? - it's ok to do this just before you plot it on the screen once it's an integer. But not while calculating! line length is a 'zero-based' quantity.
This version returns approx 4E-16 always.
import java.awt.geom.Point2D;
import java.awt.geom.Line2D;
import java.awt.geom.QuadCurve2D;
import java.util.Iterator;
import java.util.ArrayList;
public final class TestGradientLine {
private static int SIZE = 3;
public TestGradientLine() {
super();
}
//y = mx + b
//b = y - mx
//m is rise / run = gradient
//width and height of bounding box
//for a box 10x10 then width and height are 9,9
public static Line2D getGradientLine(double run, double rise, double width, double height, double x, double y) {
if (run == 0 && rise == 0) {
return new Line2D.Double(x, y, x + width, y + height);
}
//calculate hypotenuse
//check for a vertical line
if (run == 0) {
return new Line2D.Double(x, y, x, y + height);
}
//check for a horizontal line
if (rise == 0) {
return new Line2D.Double(x, y, x + width, y);
}
//calculate gradient
double m = rise / run;
Point2D start;
Point2D opposite;
if (m < 0) {
//lower left
start = new Point2D.Double(x, y + height);
opposite = new Point2D.Double(x + width, y);
} else {
//upper left
start = new Point2D.Double(x, y);
opposite = new Point2D.Double(x + width, y + height);
}
double b = start.getY() - (m * start.getX());
//now calculate another point along the slope
Point2D next = null;
if (m > 0) {
next = new Point2D.Double(start.getX() + Math.abs(run), start.getY() + Math.abs(rise));
} else {
if (rise < 0) {
next = new Point2D.Double(start.getX() + run, start.getY() + rise);
} else {
next = new Point2D.Double(start.getX() - run, start.getY() - rise);
}
}
final double actualWidth = width;
final double actualHeight = height;
final double a = Math.sqrt((actualWidth * actualWidth) + (actualHeight * actualHeight));
extendLine(start, next, a);
Line2D gradientLine = new Line2D.Double(start, next);
return gradientLine;
}
public static void extendLine(Point2D p0, Point2D p1, double toLength) {
final double oldLength = p0.distance(p1);
final double lengthFraction =
oldLength != 0.0 ? toLength / oldLength : 0.0;
p1.setLocation(p0.getX() + (p1.getX() - p0.getX()) * lengthFraction,
p0.getY() + (p1.getY() - p0.getY()) * lengthFraction);
}
public static Line2D generateRandomGradientLine(int width, int height) {
//so true means lower and false means upper
final boolean isLower = Math.random() > .5;
final Point2D start = new Point2D.Float(0, 0);
if (isLower) {
//change origin for lower left corner
start.setLocation(start.getX(), height );
}
//radius of our circle
double radius = Math.sqrt(width * width + height * height);
//now we want a random theta
//x = r * cos(theta)
//y = r * sin(theta)
double theta = 0.0;
if (isLower) {
theta = Math.random() * (Math.PI / 2);
} else {
theta = Math.random() * (Math.PI / 2) + (Math.PI / 2);
}
float endX = (float)(radius * Math.sin(theta));
float endY = (float)(radius * Math.cos(theta)) * -1;
if (isLower) {
endY = endY + (height );
}
final Point2D end = new Point2D.Float(endX, endY);
extendLine(start, end, radius);
return new Line2D.Float(start, end);
}
public static Point2D getNearestPointOnLine(Point2D end, Line2D line) {
final Point2D point = line.getP1();
final Point2D start = line.getP2();
double a = (end.getX() - point.getX()) * (start.getX() - point.getX()) + (end.getY() - point.getY()) * (start.getY() - point.getY());
double b = (end.getX() - start.getX()) * (point.getX() - start.getX()) + (end.getY() - start.getY()) * (point.getY() - start.getY());
final double x = point.getX() + ((start.getX() - point.getX()) * a)/(a+b);
final double y = point.getY() + ((start.getY() - point.getY()) * a)/(a+b);
final Point2D result = new Point2D.Double(x, y);
return result;
}
public static double length(double x0, double y0, double x1, double y1) {
final double dx = x1 - x0;
final double dy = y1 - y0;
return Math.sqrt(dx * dx + dy * dy);
}
public static void main(String[] args) {
final Line2D line = generateRandomGradientLine(SIZE, SIZE);
System.out.println("we're starting with line " + line.getP1() + " " + line.getP2());
double[][] region = new double[SIZE][SIZE];
//load up the region with data from our generated line
for (int x = 0; x < SIZE; x++) {
for (int y = 0; y < SIZE; y++) {
final Point2D point = new Point2D.Double(x, y);
final Point2D nearestPoint = getNearestPointOnLine(point, line);
if (nearestPoint == null) {
System.err.println("uh -oh!");
return;
}
final double distance = length(line.getP1().getX(),
line.getP1().getY(), nearestPoint.getX() ,
nearestPoint.getY() );
region[x][y] = distance;
}
}
//now figure out what our line is from the region
double runTotal = 0;
double riseTotal = 0;
double runCount = 0;
double riseCount = 0;
for (int x = 0; x < SIZE; x++) {
for (int y = 0; y < SIZE; y++) {
if (x < SIZE - 1) {
runTotal += region[x + 1][y] - region[x][y];
runCount++;
}
if (y < SIZE - 1) {
riseTotal += region[x][y + 1] - region[x][y];
riseCount++;
}
}
}
double run = 0;
if (runCount > 0) {
run = runTotal / runCount;
}
double rise = 0;
if (riseCount > 0) {
rise = riseTotal / riseCount;
}
System.out.println("rise is " + rise + " run is " + run);
Line2D newLine = getGradientLine(run, rise, SIZE, SIZE , 0, 0);
System.out.println("ending with line " + newLine.getP1() + " " + newLine.getP2());
double worst = 0.0;
int worstX = 0;
int worstY = 0;
for (int x = 0; x < SIZE; x++) {
for (int y = 0; y < SIZE; y++) {
final Point2D point = new Point2D.Double(x, y);
final Point2D nearestPoint = getNearestPointOnLine(point, newLine);
if (nearestPoint == null) {
System.err.println("uh -oh!");
return;
}
final double distance = length(line.getP1().getX(),
line.getP1().getY(), nearestPoint.getX() ,
nearestPoint.getY() );
final double diff = Math.abs(region[x][y] - distance);
if (diff > worst) {
worst = diff;
worstX = x;
worstY = y;
}
}
}
System.out.println("worst is " + worst + " x: " + worstX + " y: " + worstY);
}
}
why do you multiply by -1 at the end of this line?
int endY = (int)Math.round(radius * Math.cos(theta)) * -1;
this means that endY is always negative except radius is below 0. (cosinus always returns positive value)
is this intended or am i getting something wrong?
regards
You probably misunderstand float and/or double. This is a common problem with any language that implements the ieee spec for floats and doubles, which Java, C, C++ and just about every other language does.
Essentially
double val = 0;
for(int i=0;i<10;i++) {
val+=0.1;
System.out.println(val);
}
results in
0.1
0.2
0.30000000000000004
0.4
0.5
0.6
0.7
0.7999999999999999
0.8999999999999999
0.9999999999999999
And sometimes even worse. Either use BigDecimal, which alleviates a lot of the problem, or use integers.

Categories

Resources