Last active
December 21, 2015 10:28
-
-
Save ZhanruiLiang/6291709 to your computer and use it in GitHub Desktop.
Is vertex in polygon ?
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import pygame as pg | |
import math | |
W, H = 1000, 600 | |
pg.display.init() | |
pg.font.init() | |
font = pg.font.SysFont('serif', 20) | |
X, Y = 0, 1 | |
def cross(a, b): | |
return a[X] * b[Y] - a[Y] * b[X] | |
def dot(a, b): | |
return a[X] * b[X] + a[Y] * b[Y] | |
def minus(a, b): | |
return (a[X] - b[X], a[Y] - b[Y]) | |
def is_CCW(ps): | |
""" | |
Test if polygon ps is in CCW order | |
""" | |
w = 0. | |
for i in xrange(len(ps)): | |
a = minus(ps[i-1], ps[i-2]) | |
b = minus(ps[i], ps[i-1]) | |
t = math.atan2(cross(a, b), dot(a, b)) | |
w += t | |
return w >= 0 | |
def is_vertex_in_polygon(ps, v): | |
""" | |
Test if vertex v in polygon ps | |
""" | |
w = 0. | |
for i in xrange(len(ps)): | |
a = minus(v, ps[i-1]) | |
b = minus(v, ps[i]) | |
t = math.atan2(cross(a, b), dot(a, b)) | |
w += t | |
return abs(w) > math.pi | |
EPS = 1e-8 | |
def sgn(x): | |
return (x > EPS) - (x < -EPS) | |
def is_vertex_in_polygon_raytrace(ps, p): | |
s = (1., 0.) | |
cnt = 0 | |
for i in xrange(len(ps)): | |
v1 = minus(ps[i-1], ps[i]) | |
v2 = minus(ps[i-1], p) | |
f1 = cross(s, v1) | |
if sgn(f1) == 0: | |
if sgn(dot(minus(ps[i], p), v2)) <= 0: | |
return True | |
continue | |
if sgn(f1 * cross(v2, v1)) >= 0: | |
r = cross(s, v2) / f1 | |
if sgn(r) == 0: | |
if sgn(cross(s, v1) * cross(minus(ps[i-1], ps[i-2]), s)) > 0: | |
cnt += 1 | |
elif sgn(r) > 0 and sgn(r - 1) < 0: | |
cnt += 1 | |
return cnt % 2 != 0 | |
test_method = is_vertex_in_polygon_raytrace | |
def screen_to_math(p): | |
x, y = p | |
return (x, H - y) | |
math_to_screen = screen_to_math | |
class App: | |
SELECT_POLY, SELECT_VERTEX, FINISH = 0, 1, 2 | |
FIELD_DISTANCE = 8 | |
def __init__(self): | |
self.screen = pg.display.set_mode((W, H)) | |
self.clear() | |
self.run() | |
def write_at(self, s, pos): | |
self.screen.blit(font.render(s, 1, (0, 0, 0)), pos) | |
def update(self): | |
screen = self.screen | |
screen.fill((0xff, 0xff, 0xff)) | |
if len(self.polygon) > 1: | |
closed = self.state == self.SELECT_VERTEX | |
polygon = map(math_to_screen, self.polygon) | |
pg.draw.lines(screen, self.polyColor, closed, polygon) | |
for i, p in enumerate(polygon): | |
screen.blit(font.render('#%d'%(i), 1, (0x44, 0x44, 0x44)), p) | |
if closed and self.fieldMode: | |
d = self.FIELD_DISTANCE | |
for x in xrange(d, W, d): | |
for y in xrange(d, H, d): | |
isInside = test_method(self.polygon, (x, y)) | |
pg.draw.circle(screen, | |
(0x4f, 0x8f, 0) if isInside else (0xef, 0xef, 0x9f), | |
math_to_screen((x, y)), 2) | |
if self.vertex: | |
pg.draw.circle(screen, | |
(0, 0x4f, 0) if self.isInside else (0x8f, 0xff, 0), | |
math_to_screen(self.vertex), 4) | |
self.write_at('isCCW?: {}'.format(self.isCCW), (10, 10)) | |
self.write_at('isInside?: {}'.format(self.isInside), (10, 40)) | |
def clear(self): | |
self.state = self.SELECT_POLY | |
self.polygon = [] | |
self.vertex = None | |
self.isInside = None | |
self.isCCW = None | |
self.fieldMode = True | |
self.polyColor = (0, 0, 0) | |
def on_left_click(self, pos): | |
if self.state == self.SELECT_POLY: | |
self.polygon.append(pos) | |
self.update() | |
elif self.state == self.SELECT_VERTEX: | |
self.vertex = pos | |
self.isInside = test_method(self.polygon, self.vertex) | |
self.update() | |
def close_polygon(self): | |
self.isCCW = is_CCW(self.polygon) | |
def on_right_click(self, pos): | |
if self.state == self.SELECT_POLY: | |
self.state = self.SELECT_VERTEX | |
self.close_polygon() | |
self.update() | |
elif self.state == self.SELECT_VERTEX: | |
self.clear() | |
self.update() | |
def run(self): | |
timer = pg.time.Clock() | |
self.update() | |
while 1: | |
for e in pg.event.get(): | |
if e.type == pg.QUIT: | |
exit(0) | |
if e.type == pg.MOUSEBUTTONDOWN: | |
pos = screen_to_math(e.pos) | |
if e.button == 1: | |
self.on_left_click(pos) | |
elif e.button == 3: | |
self.on_right_click(pos) | |
if e.type == pg.KEYDOWN: | |
if e.key == pg.K_SPACE: | |
self.fieldMode = not self.fieldMode | |
pg.display.update() | |
timer.tick(30) | |
# help(font.render) | |
App().run() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment