Skip to content

Instantly share code, notes, and snippets.

@tito
Created March 4, 2010 14:24
Show Gist options
  • Save tito/321738 to your computer and use it in GitHub Desktop.
Save tito/321738 to your computer and use it in GitHub Desktop.
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