Initial version
This commit is contained in:
commit
01e6b0f672
6 changed files with 280 additions and 0 deletions
48
README.md
Normal file
48
README.md
Normal file
|
@ -0,0 +1,48 @@
|
|||
# Pyglet CornerPin
|
||||
|
||||
This is a little utility that adds corner pin transforms to a pyglet window.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
pip install pyglet-cornerpin
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
Create instance for a window, and register the event handlers for dragging the pins.
|
||||
|
||||
|
||||
```python
|
||||
|
||||
pins = PygletCornerPin(window)
|
||||
# event handlers for dragging:
|
||||
window.push_handlers(pins)
|
||||
```
|
||||
|
||||
Then you can draw the pins in your `on_draw()` event.
|
||||
|
||||
```python
|
||||
@window.event
|
||||
def on_draw():
|
||||
window.clear()
|
||||
...
|
||||
# draw corner pins
|
||||
pins.draw()
|
||||
```
|
||||
|
||||
|
||||
Optionally you can provide initial positions for the pins.
|
||||
|
||||
```python
|
||||
corners = [
|
||||
(0, 0), # Bottom left
|
||||
(window.width), # Bottom right
|
||||
(0, window.height), # Top left
|
||||
(window.width, window.height), # Top right
|
||||
]
|
||||
pins = PygletCornerPin(window, corners)
|
||||
|
||||
```
|
||||
|
||||
Run [pattern.py](examples/pattern.py) in the examples folder for a demo.
|
45
examples/pattern.py
Normal file
45
examples/pattern.py
Normal file
|
@ -0,0 +1,45 @@
|
|||
import pyglet
|
||||
from pyglet_corner_pin import PygletCornerPin
|
||||
|
||||
window = pyglet.window.Window()
|
||||
|
||||
|
||||
# Generate a test pattern
|
||||
batch = pyglet.graphics.Batch()
|
||||
pattern = []
|
||||
d = 20
|
||||
y_steps = int(window.height / d)
|
||||
x_steps = int(window.width / d)
|
||||
colors = [
|
||||
(255,255,255,255),
|
||||
(180,180,180,180)
|
||||
]
|
||||
for i in range(y_steps+3):
|
||||
y = int(i * d)
|
||||
for j in range(x_steps+3):
|
||||
x = int(j * d)
|
||||
pattern.append(
|
||||
pyglet.shapes.Rectangle(x,y, d,d, colors[(i+j) % len(colors)], batch=batch)
|
||||
)
|
||||
|
||||
@window.event
|
||||
def on_key_press(symbol, modifiers):
|
||||
if symbol == pyglet.window.key.Q or symbol == pyglet.window.key.ESCAPE:
|
||||
window.close()
|
||||
exit()
|
||||
|
||||
|
||||
pins = PygletCornerPin(window)
|
||||
# event handlers for dragging:
|
||||
window.push_handlers(pins)
|
||||
|
||||
@window.event
|
||||
def on_draw():
|
||||
window.clear()
|
||||
# draw test pattern
|
||||
batch.draw()
|
||||
# draw corner pins
|
||||
pins.draw()
|
||||
|
||||
|
||||
pyglet.app.run()
|
17
poetry.lock
generated
Normal file
17
poetry.lock
generated
Normal file
|
@ -0,0 +1,17 @@
|
|||
# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand.
|
||||
|
||||
[[package]]
|
||||
name = "pyglet"
|
||||
version = "2.0.18"
|
||||
description = "pyglet is a cross-platform games and multimedia package."
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "pyglet-2.0.18-py3-none-any.whl", hash = "sha256:e592952ae0297e456c587b6486ed8c3e5f9d0c3519d517bb92dde5fdf4c26b41"},
|
||||
{file = "pyglet-2.0.18.tar.gz", hash = "sha256:7cf9238d70082a2da282759679f8a011cc979753a32224a8ead8ed80e48f99dc"},
|
||||
]
|
||||
|
||||
[metadata]
|
||||
lock-version = "2.0"
|
||||
python-versions = "^3.10"
|
||||
content-hash = "872c0fb02f6495f3797bcf31fc98f0616221db89bc16b072bcefd0227543cc0a"
|
1
pyglet_cornerpin/__init__.py
Normal file
1
pyglet_cornerpin/__init__.py
Normal file
|
@ -0,0 +1 @@
|
|||
from .cornerpin import PygletCornerPin
|
154
pyglet_cornerpin/cornerpin.py
Normal file
154
pyglet_cornerpin/cornerpin.py
Normal file
|
@ -0,0 +1,154 @@
|
|||
"""
|
||||
This little library provides functions to add corner pin functionality
|
||||
into python in general, and pyglet specifically.
|
||||
|
||||
Long time ago I used a set of javascript functions from Cihad Turhan (see
|
||||
https://stackoverflow.com/a/30646172), which I now ported to python.
|
||||
These computations might be faster with numpy, but for me these run only
|
||||
incidently so won't really hurt performance.
|
||||
"""
|
||||
|
||||
import pyglet
|
||||
from typing import Optional
|
||||
import logging
|
||||
|
||||
def adj(m): # Compute the adjugate of m
|
||||
return [
|
||||
m[4]*m[8]-m[5]*m[7], m[2]*m[7]-m[1]*m[8], m[1]*m[5]-m[2]*m[4],
|
||||
m[5]*m[6]-m[3]*m[8], m[0]*m[8]-m[2]*m[6], m[2]*m[3]-m[0]*m[5],
|
||||
m[3]*m[7]-m[4]*m[6], m[1]*m[6]-m[0]*m[7], m[0]*m[4]-m[1]*m[3]
|
||||
]
|
||||
|
||||
def multmm(a, b) -> list[float]: # multiply two matrices
|
||||
c = [0.]*9
|
||||
for i in range(3):
|
||||
for j in range(3):
|
||||
cij = 0
|
||||
for k in range(3):
|
||||
cij += a[3*i + k]*b[3*k + j]
|
||||
c[3 * i + j] = cij
|
||||
return c
|
||||
|
||||
def multmv(m, v): # multiply matrix and vector
|
||||
return [
|
||||
m[0]*v[0] + m[1]*v[1] + m[2]*v[2],
|
||||
m[3]*v[0] + m[4]*v[1] + m[5]*v[2],
|
||||
m[6]*v[0] + m[7]*v[1] + m[8]*v[2]
|
||||
]
|
||||
|
||||
# def pdbg(m, v) :
|
||||
# r = multmv(m, v)
|
||||
# return r + " (" + r[0]/r[2] + ", " + r[1]/r[2] + ")"
|
||||
|
||||
def basisToPoints(x1, y1, x2, y2, x3, y3, x4, y4) :
|
||||
m = [
|
||||
x1, x2, x3,
|
||||
y1, y2, y3,
|
||||
1, 1, 1
|
||||
]
|
||||
v = multmv(adj(m), [x4, y4, 1])
|
||||
return multmm(m, [
|
||||
v[0], 0, 0,
|
||||
0, v[1], 0,
|
||||
0, 0, v[2]
|
||||
])
|
||||
|
||||
def general2DProjection(
|
||||
x1s, y1s, x1d, y1d,
|
||||
x2s, y2s, x2d, y2d,
|
||||
x3s, y3s, x3d, y3d,
|
||||
x4s, y4s, x4d, y4d
|
||||
) :
|
||||
s = basisToPoints(x1s, y1s, x2s, y2s, x3s, y3s, x4s, y4s)
|
||||
d = basisToPoints(x1d, y1d, x2d, y2d, x3d, y3d, x4d, y4d)
|
||||
return multmm(d, adj(s))
|
||||
|
||||
def transform2d(w, h, x1, y1, x2, y2, x3, y3, x4, y4):
|
||||
t = general2DProjection(0, 0, x1, y1, w, 0, x2, y2, 0, h, x3, y3, w, h, x4, y4)
|
||||
for i in range(9):
|
||||
t[i] = t[i] / t[8]
|
||||
|
||||
t = [t[0], t[3], 0, t[6],
|
||||
t[1], t[4], 0, t[7],
|
||||
0 , 0 , 1, 0 ,
|
||||
t[2], t[5], 0, t[8]]
|
||||
return t
|
||||
|
||||
# Below are all pyglet specific functions
|
||||
|
||||
class PygletCornerPin(pyglet.event.EventDispatcher):
|
||||
def __init__(self, window: pyglet.window.Window, corners: Optional[list[list[float]]] = None) -> None:
|
||||
"""
|
||||
Creates CornerPin utility for a given Pyglet window. If not given, corners default to the
|
||||
bottom left, bottom right, top left, top right corners of the window (i.e. no transform)
|
||||
|
||||
Do not forget to register event handlers by running `window.register_handlers(pins)`.
|
||||
"""
|
||||
self.window = window
|
||||
self.corners = corners if corners else [
|
||||
[0, 0],
|
||||
[self.window.width, 0],
|
||||
[0, self.window.height],
|
||||
[self.window.width, self.window.height],
|
||||
]
|
||||
self.batch = pyglet.graphics.Batch()
|
||||
self.dragging: Optional[int] = None
|
||||
self.handles = [
|
||||
pyglet.shapes.Arc(c[0],c[1],20, thickness=2, color=(0,0,255,255), batch=self.batch) for c in self.corners
|
||||
]
|
||||
|
||||
def update_handles(self):
|
||||
for i, c in enumerate(self.corners):
|
||||
self.handles[i].x = c[0]
|
||||
self.handles[i].y = c[1]
|
||||
|
||||
def draw(self):
|
||||
"""
|
||||
Draw the handles in the on_draw loop
|
||||
"""
|
||||
self.batch.draw()
|
||||
|
||||
def transform2d_matrix(self, x1, y1, x2, y2, x3, y3, x4, y4):
|
||||
"""
|
||||
Calculate the transform matrix
|
||||
"""
|
||||
t = transform2d(
|
||||
self.window.width,
|
||||
self.window.height,
|
||||
x1, y1, x2, y2, x3, y3, x4, y4 )
|
||||
return pyglet.math.Mat4(t)
|
||||
|
||||
# event handlers
|
||||
|
||||
def on_mouse_press(self, x, y, button, modifiers):
|
||||
r = 20
|
||||
currentcorner = None
|
||||
dist = r * r
|
||||
for i in range(4):
|
||||
dx = x - self.corners[i][0]
|
||||
dy = y - self.corners[i][1]
|
||||
if dist > dx**2 + dy ** 2:
|
||||
dist = dx**2 + dy ** 2
|
||||
currentcorner = i
|
||||
|
||||
self.dragging = currentcorner
|
||||
return pyglet.event.EVENT_HANDLED if self.dragging else pyglet.event.EVENT_UNHANDLED
|
||||
|
||||
def on_mouse_release(self, x, y, button, modifiers):
|
||||
if self.dragging is None:
|
||||
return pyglet.event.EVENT_UNHANDLED
|
||||
self.dragging = None
|
||||
logging.debug(f"Corner pins set to {self.corners}")
|
||||
return pyglet.event.EVENT_HANDLED
|
||||
|
||||
def on_mouse_drag(self, x, y, dx, dy, buttons, modifiers):
|
||||
if self.dragging is None:
|
||||
return pyglet.event.EVENT_UNHANDLED
|
||||
|
||||
self.corners[self.dragging][0] = x
|
||||
self.corners[self.dragging][1] = y
|
||||
self.update_view()
|
||||
|
||||
|
||||
def update_view(self):
|
||||
self.window.view = self.transform2d_matrix(*[x for c in self.corners for x in c])
|
15
pyproject.toml
Normal file
15
pyproject.toml
Normal file
|
@ -0,0 +1,15 @@
|
|||
[tool.poetry]
|
||||
name = "pyglet-cornerpin"
|
||||
version = "0.1.0"
|
||||
description = ""
|
||||
authors = ["Ruben van de Ven <git@rubenvandeven.com>"]
|
||||
readme = "README.md"
|
||||
|
||||
[tool.poetry.dependencies]
|
||||
python = "^3.10"
|
||||
pyglet = "^2.0.18"
|
||||
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry-core"]
|
||||
build-backend = "poetry.core.masonry.api"
|
Loading…
Reference in a new issue