1

Starting from this situation: enter image description here

I would like to create a boolean mask where all external points are considered as True while all internal point are False. Something like this :

enter image description here

The objective would be to create an array which would contain the indices of the external points. This time I don't even see where to start... so my question would be: "Is it possible?"

If so how would you approach this problem (regardless of the orientation and size of the shape?) Also I would like to know how we can approach this problem from a mathematical point of view?

2
  • What exactly is your input? Do you have the points as a 2-dimensional numpy array, do you have them unordered in a set, or something else? Commented Jul 4, 2024 at 11:26
  • @Aemyl Hi ! Good question, Thank you ! I have access to both informations, coordinates and indexes. Coordinates are arranged in the same order as the indices. in the specific case of the example here is the start of the array of coordinates [[-1.4142 -0. 0. ] [-0.7071 -0.7071 0. ] [ 0. -1.4142 0. ] [ 0.7071 -2.1213 0. ] [ 1.4142 -2.8284 0. ] [ 2.1213 -3.5355 0. ] [-1.1112 0.303 0. ] ...] therefore vertex of index 0 has coordinates [-1.4142 -0. 0. ]. If this is not clear, don't hesitate to tell me so... I will explain again. thank. Commented Jul 4, 2024 at 11:38

2 Answers 2

1

Assuming you are only dealing with rectangles and evenly-spaced points, a purely mathematical approach would be the following:

  1. Compute the first grid vector v1 as points[1] - points[0]
  2. Iterate over the points, always computing the difference between the current point and the last one. Compare that difference to the grid vector from step 1, if it is different, you hit a new row.
  3. Once you have the start point of the second row, compute the second grid vector v2 as the difference between this start point and the very first point of the point list.
  4. When you have both grid vectors, you can check if a point is on the edge by computing the four points point + v1, point - v1, point + v2, point - v2. If any of these four points is not in your list, the point is on the edge.

Note that this will only work as long as all numbers can be represented exactly, e.g. when you have integer coordinates.

The following approach can also handle float coordinates:

  1. Iterate over the points and compute the difference vector.
  2. Compute the dot product of the current difference vector and the previous one.
    • If the dot product is positive, we walked forward within the row
    • If the dot product is negative, the current point is at the start of a new row
  3. Keep track of the last row start. All points that follow it are in the last row and therefore part of the border.

The dot product v1 @ v2 is equal to the lengths of the two vectors and the cosine of the angle between them multiplied together. The lengths are never negative, so when the dot product is negative, the angle has to be larger than 90 degrees, meaning the vectors show in different directions.

In Python code, it could look like this (untested):

last_vec = None
first_row = True
last_row_start = None
result = [points[0]]
for p1, p2 in itertools.pairwise(points):
    current_vec = p2 - p1
    if last_vec is None:
        result.append(p2)
        last_vec = current_vec
        continue
    if current_vec @ last_vec < 0:
        if not first_row:
            result.append(p1)
        result.append(p2)
        last_row_start = False
        first_row = False
    if first_row:
        result.append(p2)
# include last row
result.extend(points[point.index(last_row_start)+1:])

This might give surprising results: consider a parallelogram of points like this: [[0, 0], [1, 0], [2, 0], [3, 1], [4, 1], [5, 1], [6, 2], [7, 2], [8, 2]]. The second approach I gave would see this as a 1x9 rectangle.

Sign up to request clarification or add additional context in comments.

1 Comment

I will post a second approach that works with float coordinates later, when I have more time
1

Maybe another way ... in two major steps

  1. find corners indices

To find the corners, I first thought of using argmax but the results differ depending on the orientation of the rectangle. So measuring the center then the distance from the center seemed safer to me. To find corners

centroid = coords.mean(axis=0)
dist = np.zeros(coords.shape[0])
for i in range(coords.shape[0]):
    dist[i]= np.linalg.norm(coords[i] - centroid)
corners =np.argwhere(np.round(dist,7) == np.amax(np.round(dist,7)))

in the example corners are therefore :

  • left low corner index = 0
  • left upper corner index = 42
  • right low corner index = 5
  • right high corner index = 47
  1. compute cross product to find colinear points: Since we've got corners indices, it's now possible to compute cross product and so find all colinear points.

I saw two options here :

a. compute all cross products = 0

b. stop computation when the first null cross product is found because it's correspond to the delta we are searching

for i in range(left_low_corner+1, coords.shape[0]):
    vec_1 = coords[left_high_corner]-coords[left_low_corner]
    vec_2 = coords[i]-coords[left_low_corner]
    cross_p = np.cross(vec_1,vec_2)
    if abs(cross_p[2]) < 0.001:
        VD = i
        break

To find colinear points

When VD is known, it's now easy to retrieve left and right sides indices

side_le_index = np.arange(left_low_corner,left_high_corner+delta_verti,delta_verti)
side_ri_index = np.arange(right_low_corner,right_high_corner+delta_verti,delta_verti)

Indices deductions

This also now possible to get the HD step value and therefore indices of vertices of lower and upper sides:

nb_column = int(coords.shape[0]/len(side_le_index))
step = int((right_low_corner+1)/nb_column)
side_lo_index = np.arange(left_low_corner,right_low_corner+step, step) 
side_up_index = np.arange(left_high_corner,right_high_corner+step, step) 

it may seem a little crooked but it works whatever the orientation of the rectangle and whatever the horizontal and vertical deltas.

If anyone has any tips for improving the code or a more efficient way to approach the problem please let me know!

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.