Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to find correct neighbors for any giving coordinate?

Update: this question is seeking guidance on how to get a set of neighbors for any given coordinate.

I created a 2d array that contains coordinates,

 int[][] coordinates= { { -1, -1 }, { -1, 0 }, { -1, +1 },
            { 0, -1 }, { 0, +1 }, { +1, -1 }, { +1, 0 }, { +1, -1 } };

As you can tell, these are the neighbors for coordinates (0,0).

Now I am trying to implement a method that takes two parameters (int positionX, int positionY), and use the input parameters value coordiante(x,y) as the starting coordinate and find all the neighbors for this coordinate.

I am thinking about something like this:

    int getNearCoordinates(int positionX, int positionY) {

        for (int[] coordinate: coordinates) {
               //I am not sure what to do after this
        }

    } 

I am trying to use a loop to get the individual coordinate from the 2d array I created and I am stuck at here. How do I find a way to appropriately find positionX's and positionY's neighbor?

What are neighbours?

All orange points in diagram below are neighbours of Origin (0,0) enter image description here

like image 839
OPK Avatar asked Aug 01 '15 17:08

OPK


3 Answers

I'd recommend to

  • Use a dedicated class (Coordinate) instead of int[]. This makes your code easier to extend (3rd dimention, etc) or to change (using double instead of int, etc.). In the example you can see an imutable class - this hinders code to have side effects.
  • Use Collection instead of Array. This makes handling much easier (you can simply add and remove items)
  • Use java8-Streaming-API. It is lightning fast and makes your code better readable.

Additional ideas:

  • You could even make getNearCoordinates part of the Coordinate class. This would make new Coordinate(27,35).getNearCoordinates() available.
  • Instead of storing x and y in separate fields you could also use a Map<Axis, Integer>. This would make your code a little bit harder to understand - but would reduce duplicated code.
  • You could also generate the collection of directions by using two nested loops for (int x = -1; x <= 1; x++) for (int y = -1; y <= 1; y++) use(new Coordinate(x,y)). This would make your code cleaner, but might be harder to understand.

Example code:

import java.util.*;
import java.util.stream.Collectors;

public class Snippet {

    // make a class to be more flexible
    class Coordinate {

        // final fields are making this an "imutable"
        final int x;
        final int y;

        /** constructor to take coordinate values */
        Coordinate(int x, int y) {
            this.x = x;
            this.y = y;
        }

        /** moves this coordinate by another coordinate */
        Coordinate move(Coordinate vector) {
            return new Coordinate(x + vector.x, y + vector.y);
        }
    }

    /** using Collection instead of Array makes your live easier. Consider renaming this to "directions". */
    Collection<Coordinate> coordinates = Arrays.asList(
            new Coordinate( -1, -1 ), // left top
            new Coordinate( -1,  0 ), // left middle
            new Coordinate( -1, +1 ), // left bottom
            new Coordinate(  0, -1 ), // top
            new Coordinate(  0, +1 ), // bottom
            new Coordinate( +1, -1 ), // right top
            new Coordinate( +1,  0 ), // right middle
            new Coordinate( +1, +1 )  // right bottom
            );

    /** @return a collection of eight nearest coordinates near origin */
    Collection<Coordinate> getNearCoordinates(Coordinate origin) {
        return
                // turn collection into stream
                coordinates.stream()

                // move the origin into every direction
                .map(origin::move)

                // turn stream to collection
                .collect(Collectors.toList());
    }

}

Same behaviour without Java8-streaming API would look like this:

/** @return a collection of eight nearest coordinates near origin */
Collection<Coordinate> getNearCoordinates(Coordinate origin) {
    Collection<Coordinate> neighbours = new ArrayList<>();

    for (Coordinate direction : coordinates)
        neighbours.add(origin.move(direction));

    return neighbours;
}
like image 147
slartidan Avatar answered Nov 20 '22 08:11

slartidan


Two points A(x1,y1), B(x2,y2) are neighbours if this expression is true:

 Math.abs(x1-x2) <= 1 && Math.abs(y1-y2) <= 1 

Here if both differences are equal to zero then A equals B.

like image 33
ka4eli Avatar answered Nov 20 '22 07:11

ka4eli


This is not the best way to implement it (using int[] for points), the purpose of this answer is to show the algorithms.

If you are talking about an unbounded plane then you will always have 8 points, so you could implement it the following way:

// first point index, 2nd: 0 = x, 1 = y
public int[][] getNeighbours(int x, int y) {
   int[][] ret = new int[8][2];
   int count = 0;
   for (int i = -1; i <= 1; i++)
      for (int j = -1; j <= 1; j++) {
         if (i == 0 && j == 0)
            continue;
         ret[count][0] = x + i;
         ret[count++][1] = y + j;
      }
   return ret;
}

It gets more interesting if the plane is bounded, using an ArrayList this time:

public List<int[]> getNeighbours(int x, int y, int minX, int maxX, int minY, int maxY) {
   List<int[]> ret = new ArrayList<int[]>(8); // default initial capacity is 100
   for (int i = Math.max(x - 1, minX); i <= Math.min(x + 1, maxX); i++)
      for (int j = Math.max(y - 1, minY); j <= Math.min(y + 1, maxY); j++) {
         if (i == x && j == y)
            continue;
         ret.add(new int[] {i, j});
      }
   return ret;
}

The latter will work for any point, also outside of the plane or just at the border.

like image 45
maraca Avatar answered Nov 20 '22 07:11

maraca