0

Let me start by stating that while my question is of a programming nature, the part where I get stuck is a bit mathematical. So I'm not sure if this is the correct place to post about it, but I wasn't sure where else.

I'm trying to define some boolean function that returns true if a point (x,y) is inside a certain shape and false if outside. To clarify that, the following code would work for defining an annulus (a ring) of inner radius r1 and outer radius r2:

def ring(pos):
    (x, y) = pos
    rsq = x ** 2 + y ** 2
    return (r1 ** 2 < rsq < r2 ** 2)

My question would be if someone could help me come up with a clever way to define a function like this for a hexagonal region. Specifically, I'd like to define a hexagonal region with side length s (which is half of the diameter), centered around the origin. Ideally it would also be oriented such that the top and bottom are sides, paralel with the x-axis.

user129412
  • 765
  • 1
  • 6
  • 19
  • 1
    If you type "point inside hexagon" into a search engine you get plenty of examples. (one popular approach is to use the same algorithm for detecting if a point is inside any convex polygon) – UnholySheep Mar 20 '17 at 12:31
  • You are completely right, for some reason I phased out a bit and didn't come up with a proper search term. I'll look into those – user129412 Mar 20 '17 at 12:32
  • @UnholySheep This is a quite special case, though, possibly allowing a simpler solution. – Stefan Pochmann Mar 20 '17 at 12:35
  • The inscribed and prescribed circles might be a starting point but there is a zone of uncertainty they leave uncovered. – Ma0 Mar 20 '17 at 12:36
  • @Ev.Kounis I don't think the `ring` code is an attempt to solve the hexagon problem. – Stefan Pochmann Mar 20 '17 at 12:38
  • @Ev.Kounis Indeed, as Stefan points out, the ring code is there to give an elegant example of what I am trying to do, but for the wrong geometry – user129412 Mar 20 '17 at 12:39
  • use 2 bounding boxes. The first one is the upright one. Then rotate the problem 45° and do the same thing again. Don't forget to rotate the point you are looking for too. – Ma0 Mar 20 '17 at 12:50
  • @Ev.Kounis Hexagons don't really have much to do with 45° angles :-P – Stefan Pochmann Mar 20 '17 at 13:00
  • @StefanPochmann. xD, Ok then rotate twice by 60° – Ma0 Mar 20 '17 at 13:04
  • Related, for an entire grid of hexagons: [Faster way to calculate hexagon grid coordinates](https://stackoverflow.com/questions/26691097/faster-way-to-calculate-hexagon-grid-coordinates) – smci Apr 30 '20 at 04:23

4 Answers4

4

Using symmetry to get into the first quadrant and then simple math (Pythagoras is enough) to check whether the point is both "below the diagonal" (which has y = sqrt(3) ⋅ (s - x)) and "below the top edge" (which has y = sqrt(3)/2 ⋅ s).

>>> def hexagon(pos):
        x, y = map(abs, pos)
        return y < 3**0.5 * min(s - x, s / 2)

Demo:

>>> s = 13
>>> for y in range(-s, s+1):
        print(' '.join('.X'[hexagon((x, y))] for x in range(-s, s+1)))

. . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . X X X X X X X X X X X X X . . . . . . .
. . . . . . X X X X X X X X X X X X X X X . . . . . .
. . . . . . X X X X X X X X X X X X X X X . . . . . .
. . . . . X X X X X X X X X X X X X X X X X . . . . .
. . . . . X X X X X X X X X X X X X X X X X . . . . .
. . . . X X X X X X X X X X X X X X X X X X X . . . .
. . . X X X X X X X X X X X X X X X X X X X X X . . .
. . . X X X X X X X X X X X X X X X X X X X X X . . .
. . X X X X X X X X X X X X X X X X X X X X X X X . .
. . X X X X X X X X X X X X X X X X X X X X X X X . .
. X X X X X X X X X X X X X X X X X X X X X X X X X .
. X X X X X X X X X X X X X X X X X X X X X X X X X .
. X X X X X X X X X X X X X X X X X X X X X X X X X .
. . X X X X X X X X X X X X X X X X X X X X X X X . .
. . X X X X X X X X X X X X X X X X X X X X X X X . .
. . . X X X X X X X X X X X X X X X X X X X X X . . .
. . . X X X X X X X X X X X X X X X X X X X X X . . .
. . . . X X X X X X X X X X X X X X X X X X X . . . .
. . . . . X X X X X X X X X X X X X X X X X . . . . .
. . . . . X X X X X X X X X X X X X X X X X . . . . .
. . . . . . X X X X X X X X X X X X X X X . . . . . .
. . . . . . X X X X X X X X X X X X X X X . . . . . .
. . . . . . . X X X X X X X X X X X X X . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . .
Stefan Pochmann
  • 27,593
  • 8
  • 44
  • 107
  • @EricDuminil Took me a few attempts to make the picture look that nice :-). I wish we had a "true" monospace font (equal width and height)... – Stefan Pochmann Mar 20 '17 at 13:26
  • Very cool solution indeed. Perhaps you could elaborate on the below the diagonal and top edge part a little bit, but on the other hand the reader should be able to figure it out him/herself. – user129412 Mar 20 '17 at 13:32
  • 1
    @user129412 Ok I added a bit, showing the two equations separately. – Stefan Pochmann Mar 20 '17 at 13:47
  • Coming back to this, I suppose it's not so simple to displace the hexagon in either the x or the y direction due to the usage of the absolute values. I'd probably have to break it up into multiple pieces, right? – user129412 Apr 06 '17 at 14:02
  • @user129412 I don't see why that wouldn't be trivial. Can't you just subtract the displacement as the first thing (before doing the `abs`)? – Stefan Pochmann Apr 06 '17 at 14:48
  • Yes you can. Momentary lapse of ability to think? – user129412 Apr 07 '17 at 08:42
1

Your solution is basically based on polar coordinates. Since rings or circles are centered on the origin, you don't care about θ though : you just need to check if r is inside [0,r_max] for a disk or [r_min,r_max] for a ring.

You could use a polar definition of an hexagon and check if r is inside [0,r_hexagon(θ)].

Here's an example with numpy and matplotlib :

import numpy as np
import matplotlib.pyplot as plt
import math


theta = np.arange(0, 2 * math.pi, 0.01)

sixty_d = math.pi / 3


def hexagon(theta):
    return math.sqrt(3) / (2 * math.sin(theta + sixty_d -
                                        sixty_d * math.floor(theta / sixty_d)))

hexagon = np.vectorize(hexagon)

ax = plt.subplot(111, projection='polar')
ax.plot(theta, hexagon(theta))
ax.set_rmax(1.5)
ax.set_rticks([0.5, 1, 1.5])
ax.grid(True)

ax.set_title("Hexagon in polar coordinates", va='bottom')
plt.show()

It displays :enter image description here

You can use the above hexagon(theta) to get the maximum radius. theta can be calculated with math.atan2(y, x).

Eric Duminil
  • 52,989
  • 9
  • 71
  • 124
1

My two cents:

def inside(pos, R):
    import numpy as np
    r = R * np.sqrt(3) / 2
    try:  # thanks to @stefan-pochmann
        phi = np.arctan(pos[1] / pos[0])
    except ZeroDivisionError:
        phi = 0.0
    length = np.sqrt(pos[0] ** 2 + pos[1] ** 2)
    for i in range(3):
        rot = 2 * np.pi / 3.0 * i
        new_phi = phi + rot
        new_pos = (length * np.sin(new_phi), length * np.cos(new_phi))
        if abs(new_pos[0]) <= np.sqrt(R ** 2 - r ** 2) and abs(new_pos[1]) <= r:
            return True
    return False

It assumes that the hexagon is centered around (0, 0) and R is the prescribed circle radius and r the inscribed one.

It is not an implementation of the ring algorithm. It uses bounding boxes

Ma0
  • 15,057
  • 4
  • 35
  • 65
  • @StefanPochmann Indeed; the indexes were also inverse. I hope it works fine now. – Ma0 Mar 20 '17 at 13:35
  • @StefanPochmann Forgot to convert the degrees to radians before rotating. Interesting shape though. I am missing 2 corners now for some reason (http://ideone.com/cptiEK). Hmmm – Ma0 Mar 20 '17 at 13:55
  • I think that's because `phi` should be +/- infinity there, not 0 (because you're treating division by zero). – Stefan Pochmann Mar 20 '17 at 14:32
0

create a bounding box around the hexagon, the height is distance between the center corners lets say a, and the width is the distance between the left and right facets lets say b.
if you have a predetermined size for the hexagon you can easily get the area of the 4 triangles that surround it.
therefore you can test if your point is in one of these triangles, if not then it is in the hexagon.

 ________
|   /\   |
|* /  \ *|  This is an illustration of the above suggestion
| /    \ |  The * are points outside the hexagon.
|/      \|
|        |
|        |
|\      /|
| \    / |
| *\  / *|
|___\/___|
Elie Nassif
  • 464
  • 4
  • 11
  • 1
    then the problem becomes how to test if it inside the triangles though.. And if triangles are easier you can "disassemble" the hexagon into 6 of those and do it there – Ma0 Mar 20 '17 at 12:35
  • Triangles are easier and yes you can do that also, depends on how many tests you want to run, 4 is better than 6 in my opinion. – Elie Nassif Mar 20 '17 at 12:40
  • wouldn't there be 5 tests, though? one to check if inside the square at all, and then one per triangle (still better than 6 though) – Brian H. Mar 20 '17 at 12:53