Optimizing game camera I'm moving some octagons around and I found a serious bottleneck:

@win.event
def on_mouse_drag(x, y, dx, dy, buttons, modifiers):
    for o in octagons:
        o.vertices = tuple(chain.from_iterable( (o.vertices[i]+dx, o.vertices[i+1]+dy) for i in range(0, 18, 2)))


The program starts lagging when about 1000 points (111 octagons) are moved at once. I must find a way to optimize it but OpenGL is the last straw I want to draw because I don't want to use an API I don't understand. Could someone suggest a way to make it faster? Any internal solution or should I use NumPy? Here's the whole code (mere 30 lines) if you want to run it on your machine:

import pyglet
from circle import octagon
from random import randrange
from itertools import chain

SIZE = 800
RADIUS = 50
COUNT = 50
WIN = SIZE, SIZE, 'Camera', True, 'tool' # x, y, caption, resizable, style

def rnd_c():
    return (randrange(256), randrange(256), randrange(256)) * 9

def rnd_pos():
    return randrange(SIZE)

win = pyglet.window.Window(*WIN, vsync=False) # vsync off to unlimit FPS
batch = pyglet.graphics.Batch()
octagons = [octagon(rnd_pos(), rnd_pos(), RADIUS, rnd_c(), batch, None) for _ in range(COUNT)]
fps = pyglet.clock.ClockDisplay()

@win.event
def on_draw():
    win.clear()
    batch.draw()
    fps.draw()

@win.event
def on_mouse_drag(x, y, dx, dy, buttons, modifiers):
    for o in octagons:
        o.vertices = tuple(chain.from_iterable( (o.vertices[i]+dx, o.vertices[i+1]+dy) for i in range(0, 18, 2)))

def on_step(dt):
    pass
pyglet.clock.schedule(on_step)

pyglet.app.run()


Graphics are fine (I think). I can render 1000 steady octagons @ 300 FPS.
NumPy is just plain awesome! Now I can move 3X more octagons without any lag at all! And it greatly shortens the length of lines so I can use more descriptive names, too! Here's the NumPy version:

import pyglet
import numpy as np
from circle import octagon
from random import randrange

R = 50
SIZE = 800
COUNT = 333 # NO LAG! RUNS @ &gt;60 FPS!
WIN = SIZE, SIZE, 'Camera', True, 'tool' # x, y, caption, resizable, style

def clr():
    return (randrange(256), randrange(256), randrange(256)) * 9

def coord():
    return randrange(SIZE)

win = pyglet.window.Window(*WIN, vsync=False) # to unlimit FPS
batch = pyglet.graphics.Batch()
shapes = [octagon(coord(), coord(), R, clr(), batch, None) for _ in range(COUNT)]
coords = np.array( [shape.vertices for shape in shapes], dtype=int)
fps = pyglet.clock.ClockDisplay()

@win.event
def on_draw():
    win.clear()
    batch.draw()
    fps.draw()

@win.event
def on_mouse_drag(x, y, dx, dy, buttons, modifiers):
    global coords
    for i, shape in enumerate(shapes):
        coords[i][::2] += dx
        coords[i][1::2] += dy
        shape.vertices = tuple(coords[i])

def on_step(dt):
    pass
pyglet.clock.schedule(on_step)

pyglet.app.run()