2
\$\begingroup\$

Using only Python 2.7 and the standard library (no imports):

Determine if a point is inside the rotated rectangle.

Test point 1: 670831  4867989
Test point 2: 675097  4869543

Rectangle:

Vertex 1:     670273   4879507
Vertex 2:     677241   4859302
Vertex 3:     670388   4856938
Vertex 4:     663420   4877144

Can the following code be improved?

vertices = [(670273, 4879507), (677241, 4859302), (670388, 4856938), (663420, 4877144)]

def dot_pdt(point1, point2, pointT):
    return ((point2[0] - point1[0]) * (pointT[0] - point1[0])) + ((point2[1] - point1[1]) * (pointT[1] - point1[1]))

def check_point(pointT):
    for i in range(len(vertices)):
        k = dot_pdt(vertices[i], vertices[(i + 1) % len(vertices)], pointT)
        if k <= 0:
            return False

    return True

point1 = (676455.270, 4861545.437)

print(check_point(point1))
\$\endgroup\$
3
  • 1
    \$\begingroup\$ return not any(dot_pdt(v, v_next, pointT) <= 0 for v, v_next in zip(vertices, vertices[1:] + vertices[:0])) \$\endgroup\$ – Stephen Rauch Aug 23 '20 at 4:00
  • \$\begingroup\$ The answer provided by Yves Daoust in the link you gave is pretty much as good as you'll find. Is there a reason you don't believe so? \$\endgroup\$ – IEatBagels Aug 27 '20 at 17:24
  • \$\begingroup\$ @IEatBagels : It's a good answer, but Yves Daoust didn't actually provide code, so that's why I made my own. I'm not a great coder so I thought I'd get it reviewed. \$\endgroup\$ – User1973 Aug 27 '20 at 18:05
1
\$\begingroup\$

Code Review

  • Main guard

When running code outside a function / class, it is a good practice to put the code inside the main guard. See here for more explanation.

  • Docstrings

It is a good practice to provide docstrings for functions. See here for more details. In Python 3.5+, adding type hints to function parameters and return values is also a good habit. (As a side remark, Python 2 has officially reached the end of life. It is recommended to move to Python 3.)

  • Variable & function names should be clear, descriptive, and unambiguous

For example, check_point could be named as is_inside_rectangle.

  • Function definition should include all necessary parameters

vertices should be a parameter of the function check_point.

  • Other minor improvements

By exploiting the fact that vertices[-1] is the same as vertices[len(vertices)-1], the explicit modulo operation vertices[(i + 1) % len(vertices)] could be avoided.

The for-loop logic can be simplified using an all statement.

Here is an improved version of the code:

def dot_prod_with_shared_start(start, end1, end2):
    """
    Compute the dot product of the vectors pointing from start to end1 and end2

    Parameters
        start: starting point of both vectors
        end1: end point of first vector
        end2: end point of second vector

    Returns
        dot product of (end1 - start) and (end2 - start)
    """
    return (end1[0] - start[0]) * (end2[0] - start[0]) + (end1[1] - start[1]) * (end2[1] - start[1])

def is_inside_rectangle(vertices, point):
    """
    Given the vertices of a rectangle, determine whether a point is inside it.

    Parameters
        vertices: a list of tuples representing rectangle vertices in clockwise or
                  counter-clockwise order
        point: a tuple representing the point to check

    Returns
        True if the point is inside the rectangle. False otherwise.
    """
    return all(dot_prod_with_shared_start(vertices[i - 1], v, point) > 0 for i, v in enumerate(vertices))

if __name__ == "__main__":
   rect_vertices = [(670273, 4879507), (677241, 4859302), (670388, 4856938), (663420, 4877144)]
   test_point = (676455.270, 4861545.437)

   print(is_inside_rectangle(rect_vertices, test_point))

Another Solution

An alternative solution is to represent the vertices and the point as complex values, and apply complex number arithmetics to solve the problem.

def is_directed_angle_acute(start, end1, end2):
    """
    Check whether the directed angle from (end1 - start) to (end2 - start) is an acute angle, 
    i.e. within (0, pi / 4).

    Parameters
        start: a complex value representing the starting point of both vectors
        end1: a complex value representing the end point of first vector
        end2: a complex value representing the end point of second vector

    Returns
        True if the directed angle from (end1 - start) to (end2 - start) is within (0, pi / 4).
        False otherwise.
    """
    q = (end2 - start) / (end1 - start)
    return q.real > 0 and q.imag > 0

def is_inside_rectangle(vertices, point):
    """
    Given the vertices of a rectangle, determine whether a point is inside it.

    Parameters
        vertices: a list of complex values representing rectangle vertices in clockwise order
        point: a complex value representing the point to check

    Returns
        True if the point is inside the rectangle. False otherwise.
    """
    v0, v1, v2, v3 = vertices
    return is_directed_angle_acute(v0, point, v1) and is_directed_angle_acute(v2, point, v3)

if __name__ == "__main__":
   rect_vertices = [670273 + 4879507j, 677241 + 4859302j, 670388 + 4856938j, 663420 + 4877144j]
   test_point = 676455.270 + 4861545.437j

   print(is_inside_rectangle(rect_vertices, test_point))
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.