Created
March 4, 2010 14:24
-
-
Save tito/321738 to your computer and use it in GitHub Desktop.
This file contains hidden or 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
def get_filled_path(points): | |
from OpenGL.GLU import gluNewTess, gluTessNormal, gluTessProperty,\ | |
gluTessBeginPolygon, gluTessBeginContour, gluTessEndContour,\ | |
gluTessEndPolygon, gluTessCallback, gluErrorString, gluTessVertex,\ | |
GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_NONZERO, GLU_TESS_VERTEX,\ | |
GLU_TESS_BEGIN, GLU_TESS_END, GLU_TESS_ERROR, GLU_TESS_COMBINE | |
tess = gluNewTess() | |
gluTessNormal(tess, 0, 0, 1) | |
gluTessProperty(tess, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_NONZERO) | |
tess_list = [] | |
tess_style = None | |
tess_shape = None | |
def tess_vertex(vertex): | |
global tess_shape | |
tess_shape += list(vertex[0:2]) | |
def tess_begin(which): | |
global tess_style, tess_shape | |
tess_style = which | |
tess_shape = [] | |
def tess_end(): | |
global tess_style, tess_shape | |
tess_list.append((tess_style, tess_shape)) | |
def tess_error(code): | |
err = gluErrorString(code) | |
pymt.pymt_logger.warning('BezierPath: GLU Tesselation Error: %s' % str(err)) | |
gluTessCallback(tess, GLU_TESS_VERTEX, tess_vertex) | |
gluTessCallback(tess, GLU_TESS_BEGIN, tess_begin) | |
gluTessCallback(tess, GLU_TESS_END, tess_end) | |
gluTessCallback(tess, GLU_TESS_ERROR, tess_error) | |
gluTessBeginPolygon(tess, None) | |
gluTessBeginContour(tess) | |
for x, y in zip(points[::2], points[1::2]): | |
v_data = (x, y, 0) | |
gluTessVertex(tess, v_data, v_data) | |
gluTessEndContour(tess) | |
gluTessEndPolygon(tess) | |
return tess_list | |
def draw_filled_path(points): | |
from OpenGL.GL import glVertex2f | |
for style, points in points: | |
with gx_begin(style): | |
for x, y in zip(points[::2], points[1::2]): | |
glVertex2f(x, y) | |
def point_inside_polygon(x, y, poly): | |
n = len(poly) | |
if n < 2: | |
return False | |
inside = False | |
p1x, p1y = poly[0] | |
for i in range(n+1): | |
p2x,p2y = poly[i % n] | |
if y > min(p1y,p2y): | |
if y <= max(p1y,p2y): | |
if x <= max(p1x,p2x): | |
if p1y != p2y: | |
xinters = (y-p1y)*(p2x-p1x)/(p2y-p1y)+p1x | |
if p1x == p2x or x <= xinters: | |
inside = not inside | |
p1x,p1y = p2x,p2y | |
return inside | |
class Curve(object): | |
def __init__(self, points=[], tension=0., precision=1., is_closed=False): | |
self._points = points | |
self._tension = tension | |
self._is_closed = is_closed | |
self._curve = [] | |
self._filledpath = None | |
self._precision = precision | |
self.update() | |
def update(self): | |
p = self._points | |
self._curve = [] | |
self._filledpath = None | |
if len(self._points) < 2: | |
return | |
if len(self._points) == 2: | |
p0, p1 = p | |
self._curve = [p0[0], p0[1], p1[0], p1[1]] | |
return | |
n = len(p) - 1 | |
if self._is_closed: | |
for i in xrange(n): | |
if i == 0: | |
o = self._curve_seg(p[n], p[0], p[1], p[2]) | |
elif i == (n - 1): | |
o = self._curve_seg(p[n-2], p[n-1], p[n], p[0]) | |
else: | |
o = self._curve_seg(p[i-1], p[i], p[i+1], p[i+2]) | |
self._curve += o | |
self._curve += self._curve_seg(p[n-1], p[n], p[0], p[1]) | |
else: | |
for i in xrange(n): | |
if i == 0: | |
o = self._curve_seg(p[0], p[0], p[1], p[2]) | |
elif i == (n - 1): | |
o = self._curve_seg(p[n-2], p[n-1], p[n], p[n]) | |
else: | |
o = self._curve_seg(p[i-1], p[i], p[i+1], p[i+2]) | |
self._curve += o | |
def _curve_seg(self, p0, p1, p2, p3): | |
curve = [] | |
p = self._precision | |
tension = self._tension | |
x = 0. | |
y = 0. | |
xl = p1[0] - 1. | |
yl = p1[1] - 1. | |
t = 0. | |
f = 1. | |
k = 1.1 | |
m1x = (1 - tension) * (p2[0] - p0[0]) / 2. | |
m2x = (1 - tension) * (p3[0] - p1[0]) / 2. | |
m1y = (1 - tension) * (p2[1] - p0[1]) / 2. | |
m2y = (1 - tension) * (p3[1] - p1[1]) / 2. | |
while t <= 1: | |
x = (2 * t * t * t - 3 * t * t + 1) * p1[0] + \ | |
(t * t * t - 2 * t * t + t) * m1x + \ | |
(-2 * t * t * t + 3 * t * t) * p2[0] + \ | |
(t * t * t - t * t) * m2x | |
y = (2 * t * t * t - 3 * t * t + 1) * p1[1] + \ | |
(t * t * t - 2 * t * t + t) * m1y + \ | |
(-2 * t * t * t + 3 * t * t) * p2[1] + \ | |
(t * t * t - t * t) * m2y | |
#x = round(x) | |
#y = round(y) | |
if x != xl or y != yl: | |
if x - xl > p or y - yl > p or xl - x > p or yl - y > p: | |
t -= f | |
f = f / k | |
else: | |
curve += [x, y] | |
xl = x | |
yl = y | |
if t + f > 1.: | |
t = 1 - f | |
else: | |
f = f * k | |
t += f | |
return curve | |
@property | |
def curve(self): | |
return self._curve | |
@property | |
def filledpath(self): | |
if self._filledpath is None: | |
self._filledpath = get_filled_path(self.curve) | |
return self._filledpath | |
def _set_tension(self, value): | |
if self._tension == value: | |
return | |
self._tension = value | |
self.update() | |
def _get_tension(self): | |
return self._tension | |
tension = property(_get_tension, _set_tension) | |
def _set_points(self, value): | |
if self._points == value: | |
return | |
self._points = value | |
self.update() | |
def _get_points(self): | |
return self._points | |
points = property(_get_points, _set_points) | |
def _set_precision(self, value): | |
if self._precision == value: | |
return | |
self._precision = value | |
self.update() | |
def _get_precision(self): | |
return self._precision | |
precision = property(_get_precision, _set_precision) | |
def _set_is_closed(self, value): | |
if self._is_closed == value: | |
return | |
self._is_closed = value | |
self.update() | |
def _get_is_closed(self): | |
return self._is_closed | |
is_closed = property(_get_is_closed, _set_is_closed) | |
if __name__ == '__main__': | |
from pymt import * | |
w = getWindow() | |
class Shape(MTWidget): | |
def __init__(self, **kwargs): | |
super(Shape, self).__init__(**kwargs) | |
self.curve = Curve(precision=10) | |
def r(): | |
import random | |
if random.random() > .5: | |
return .5 | |
return .3 | |
self.color = map(lambda x: r(), xrange(3)) + [.5] | |
self.linecolor = self.color[:3] + [.7] | |
self.circlecolor = map(lambda x: x + .1, self.color[:3]) | |
def collide_point(self, x, y): | |
return point_inside_polygon(x, y, self.curve.points) | |
def draw(self): | |
c = self.curve | |
if c.is_closed: | |
set_color(*self.color) | |
draw_filled_path(c.filledpath) | |
set_color(*self.linecolor) | |
drawLine(c.curve, width=5.) | |
set_color(*self.circlecolor) | |
for p in c.points: | |
drawCircle(pos=p, radius=3) | |
def on_touch_down(self, touch): | |
if not self.collide_point(*touch.pos): | |
return | |
touch.grab(self) | |
touch.userdata['controlpoints'] = [] | |
for idx in xrange(len(self.curve.points)): | |
x, y = self.curve.points[idx] | |
if Vector(x, y).distance(touch.pos) < 150: | |
touch.userdata['controlpoints'].append(idx) | |
return True | |
def on_touch_move(self, touch): | |
if touch.grab_current != self: | |
return | |
if touch.dpos == (0, 0): | |
return | |
p = self.curve.points | |
for idx in touch.userdata['controlpoints']: | |
d = Vector(touch.pos) - Vector(touch.dpos) | |
p[idx] = Vector(p[idx]) + d | |
self.curve.update() | |
return True | |
class Canvas(MTWidget): | |
def __init__(self, **kwargs): | |
super(Canvas, self).__init__(**kwargs) | |
def on_touch_down(self, touch): | |
if super(Canvas, self).on_touch_down(touch): | |
return True | |
shape = Shape() | |
self.add_widget(shape) | |
touch.userdata['shape'] = shape | |
shape.curve.points = shape.curve.points + [touch.pos] | |
shape.curve.points = shape.curve.points + [touch.pos] | |
return True | |
def on_touch_move(self, touch): | |
if 'shape' in touch.userdata: | |
shape = touch.userdata['shape'] | |
last = shape.curve.points[-2] | |
if Vector(touch.pos).distance(Vector(last)) > 80: | |
shape.curve.points = shape.curve.points + [touch.pos] | |
else: | |
shape.curve.points = shape.curve.points[:-1] + [touch.pos] | |
return True | |
return super(Canvas, self).on_touch_move(touch) | |
def on_touch_up(self, touch): | |
if 'shape' in touch.userdata: | |
shape = touch.userdata['shape'] | |
if len(shape.curve.curve) < 3: | |
self.remove_widget(shape) | |
return | |
shape.curve.is_closed = True | |
shape.curve.update() | |
return True | |
return super(Canvas, self).on_touch_up(touch) | |
runTouchApp(Canvas()) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment