Skip to content

Commit e2de268

Browse files
author
Frederik De Bleser
committed
Merge pull request nodebox#3 from jsundram/master
performance enhancements to drawing primitives
2 parents b07dcfd + 87e66f5 commit e2de268

File tree

1 file changed

+69
-17
lines changed

1 file changed

+69
-17
lines changed

nodebox/graphics/context.py

+69-17
Original file line numberDiff line numberDiff line change
@@ -572,8 +572,9 @@ def line(x0, y0, x1, y1, **kwargs):
572572
if stroke is not None and strokewidth > 0:
573573
glColor4f(stroke[0], stroke[1], stroke[2], stroke[3] * _alpha)
574574
glLineWidth(strokewidth)
575-
glLineDash(strokestyle)
576-
glBegin(GL_LINE_LOOP)
575+
if strokestyle != _strokestyle:
576+
glLineDash(strokestyle)
577+
glBegin(GL_LINES)
577578
glVertex2f(x0, y0)
578579
glVertex2f(x1, y1)
579580
glEnd()
@@ -598,7 +599,7 @@ def rect(x, y, width, height, **kwargs):
598599
glVertex2f(x+width, y+height)
599600
glVertex2f(x, y+height)
600601
glEnd()
601-
602+
602603
def triangle(x1, y1, x2, y2, x3, y3, **kwargs):
603604
""" Draws the triangle created by connecting the three given points.
604605
The current stroke, strokewidth and fill color are applied.
@@ -608,10 +609,11 @@ def triangle(x1, y1, x2, y2, x3, y3, **kwargs):
608609
if clr is not None and (i==0 or strokewidth > 0):
609610
if i == 1:
610611
glLineWidth(strokewidth)
611-
glLineDash(strokestyle)
612+
if strokestyle != _strokestyle:
613+
glLineDash(strokestyle)
612614
glColor4f(clr[0], clr[1], clr[2], clr[3] * _alpha)
613615
# Note: this performs equally well as when using precompile().
614-
glBegin((GL_POLYGON, GL_LINE_LOOP)[i])
616+
glBegin((GL_TRIANGLES, GL_LINE_LOOP)[i])
615617
glVertex2f(x1, y1)
616618
glVertex2f(x2, y2)
617619
glVertex2f(x3, y3)
@@ -626,19 +628,25 @@ def ellipse(x, y, width, height, segments=ELLIPSE_SEGMENTS, **kwargs):
626628
if not segments in _ellipses:
627629
# For the given amount of line segments, calculate the ellipse once.
628630
# Then reuse the cached ellipse by scaling it to the desired size.
629-
_ellipses[segments] = []
630-
for mode in (GL_POLYGON, GL_LINE_LOOP):
631-
_ellipses[segments].append(precompile(lambda:(
632-
glBegin(mode),
633-
[glVertex2f(cos(t)/2, sin(t)/2) for t in [2*pi*i/segments for i in range(segments)]],
634-
glEnd()
635-
)))
631+
commands = []
632+
f = 2 * pi / segments
633+
v = [(cos(t)/2, sin(t)/2) for t in [i*f for i in range(segments)+[0]]]
634+
for mode in (GL_TRIANGLE_FAN, GL_LINE_LOOP):
635+
commands.append(precompile(lambda:(
636+
glBegin(mode),
637+
[glVertex2f(x, y) for (x, y) in v],
638+
glEnd()
639+
)))
640+
641+
_ellipses[segments] = commands
642+
636643
fill, stroke, strokewidth, strokestyle = color_mixin(**kwargs)
637644
for i, clr in enumerate((fill, stroke)):
638645
if clr is not None and (i==0 or strokewidth > 0):
639646
if i == 1:
640647
glLineWidth(strokewidth)
641-
glLineDash(strokestyle)
648+
if strokestyle != _strokestyle:
649+
glLineDash(strokestyle)
642650
glColor4f(clr[0], clr[1], clr[2], clr[3] * _alpha)
643651
glPushMatrix()
644652
glTranslatef(x, y, 0)
@@ -673,20 +681,63 @@ def arrow(x, y, width, **kwargs):
673681
glVertex2f(x, y)
674682
glEnd()
675683

684+
def gcd(a, b):
685+
return gcd(b, a % b) if b else a
686+
687+
_stars = {} #TODO: LRU?
688+
def fast_star(x, y, points=20, outer=100, inner=50, **kwargs):
689+
""" Draws a star with the given points, outer radius and inner radius.
690+
The current stroke, strokewidth and fill color are applied.
691+
"""
692+
scale = gcd(inner, outer)
693+
iscale = inner / scale
694+
oscale = outer / scale
695+
cached = _stars.get((points, iscale, oscale), [])
696+
if not cached:
697+
radii = [oscale, iscale] * int(points+1); radii.pop() # which radius?
698+
f = pi / points
699+
v = [(r*sin(i*f), r*cos(i*f)) for i, r in enumerate(radii)]
700+
cached.append(precompile(lambda:(
701+
glBegin(GL_TRIANGLE_FAN),
702+
glVertex2f(0, 0),
703+
[glVertex2f(vx, vy) for (vx, vy) in v],
704+
glEnd()
705+
)))
706+
cached.append(precompile(lambda:(
707+
glBegin(GL_LINE_LOOP),
708+
[glVertex2f(vx, vy) for (vx, vy) in v],
709+
glEnd()
710+
)))
711+
_stars[(points, iscale, oscale)] = cached
712+
713+
fill, stroke, strokewidth, strokestyle = color_mixin(**kwargs)
714+
for i, clr in enumerate((fill, stroke)):
715+
if clr is not None and (i == 0 or strokewidth > 0):
716+
if i == 1:
717+
glLineWidth(strokewidth)
718+
if strokestyle != _strokestyle:
719+
glLineDash(strokestyle)
720+
glColor4f(clr[0], clr[1], clr[2], clr[3] * _alpha)
721+
glPushMatrix()
722+
glTranslatef(x, y, 0)
723+
glScalef(scale, scale, 1)
724+
glCallList(cached[i])
725+
glPopMatrix()
726+
676727
def star(x, y, points=20, outer=100, inner=50, **kwargs):
677728
""" Draws a star with the given points, outer radius and inner radius.
678729
The current stroke, strokewidth and fill color are applied.
730+
This is about 20x slower than fast_star; use it only if you need the path returned.
679731
"""
680-
# GL_POLYGON only works with convex polygons,
681-
# so we use a BezierPath (which does tessellation for fill colors).
682732
p = BezierPath(**kwargs)
683733
p.moveto(x, y+outer)
684734
for i in range(0, int(2*points)+1):
685735
r = (outer, inner)[i%2]
686736
a = pi*i/points
687737
p.lineto(x+r*sin(a), y+r*cos(a))
688738
p.closepath()
689-
if kwargs.get("draw", True):
739+
740+
if kwargs.get("draw", True):
690741
p.draw(**kwargs)
691742
return p
692743

@@ -1454,7 +1505,8 @@ def texture(img, data=None):
14541505
return _texture_cache[img]
14551506
# Image file path, load it, cache it, return texture.
14561507
if isinstance(img, basestring):
1457-
try: cache(img, pyglet.image.load(img).get_texture())
1508+
try:
1509+
cache(img, pyglet.image.load(img).get_texture())
14581510
except IOError:
14591511
raise ImageError, "can't load image from %s" % repr(img)
14601512
return _texture_cache[img]

0 commit comments

Comments
 (0)