From 0ec8b775543b5f7e303e21a3fabe746b0a2d7331 Mon Sep 17 00:00:00 2001 From: Roz K Date: Fri, 24 Oct 2025 05:26:42 +0200 Subject: [PATCH] pve --- pve/mesh.py | 61 ----------------- pve/node.py | 100 +++++++++++++++++++++++++++ pve/pixmap.py | 4 +- pve/polygon.py | 107 +++++++++++++++++++++++++++++ pve/shape.py | 152 ++--------------------------------------- pve/vector.py | 29 ++++++++ pygl/libgl/__init__.py | 4 +- pygl/vertexbuffer.py | 6 +- 8 files changed, 250 insertions(+), 213 deletions(-) delete mode 100644 pve/mesh.py create mode 100644 pve/node.py create mode 100644 pve/polygon.py create mode 100644 pve/vector.py diff --git a/pve/mesh.py b/pve/mesh.py deleted file mode 100644 index 3496bb8..0000000 --- a/pve/mesh.py +++ /dev/null @@ -1,61 +0,0 @@ -# People's Video Editor: high quality, GPU accelerated mp4 editor -# Copyright (C) 2025 Roz K -# -# This file is part of People's Video Editor. -# -# People's Video Editor is free software: you can redistribute it and/or modify it under the terms of the -# GNU General Public License as published by the Free Software Foundation, either version 3 of the License, -# or (at your option) any later version. -# -# People's Video Editor is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -# See the GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License along with People's Video Editor. -# If not, see . - -#TODO: split channels - -from .shape import Vertex, Shape - -from array import array - -class Mesh: - __slots__ = '_array', '_view', '_size' - - def __init__(self, shape): - #TODO: cpp - if shape.cutouts: - raise NotImplementedError - if shape.size > 4: - raise NotImplementedError - - self._array = array(Vertex.Type, 2 * 3 * Vertex.Channels) - self._view = memoryview(self._array) - self._size = self.view.nbytes - tl = shape[0] - tr = shape[1] - br = shape[2] - bl = shape[3] - self.set(0, 0, tl) - self.set(0, 1, tr) - self.set(0, 2, br) - self.set(1, 0, br) - self.set(1, 1, bl) - self.set(1, 2, tl) - - def __del__(self): - del self._view - del self._array - - def set(self, triangle, index, vertex): - index = (triangle * 3 + index) * Vertex.Channels - self._view[index:(index + Vertex.Channels)] = vertex - - @property - def size(self): - return self._size - - @property - def view(self): - return self._view diff --git a/pve/node.py b/pve/node.py new file mode 100644 index 0000000..904ee8d --- /dev/null +++ b/pve/node.py @@ -0,0 +1,100 @@ +# People's Video Editor: high quality, GPU accelerated mp4 editor +# Copyright (C) 2025 Roz K +# +# This file is part of People's Video Editor. +# +# People's Video Editor is free software: you can redistribute it and/or modify it under the terms of the +# GNU General Public License as published by the Free Software Foundation, either version 3 of the License, +# or (at your option) any later version. +# +# People's Video Editor is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with People's Video Editor. +# If not, see . + +from weakref import ref + +class Node: + __slots__ = 'parent', 'children', 'state', '__weakref__' + + STATE_NEW = 0 + STATE_MODIFIED = 1 + STATE_BUILT = 2 + + def __init__(self, parent = None, children = None): + self.parent = None + self.children = None + self.state = Node.STATE_NEW + if children is not None: + self.add_children(children) + if parent is not None: + parent.add_child(self) + + def __del__(self): + if self.parent is not None: + self.parent().remove_child(self) + + def add_child(self, child: Node): + assert child.parent is None + child.parent = ref(self) + if self.children is None: + self.children = [child] + else: + self.children.append(child) + child.state = Node.STATE_NEW + self.set_modified() + + def add_children(self, children): + for child in children: + self.add_child(child) + + def remove_child(self, child: Node): + assert child.parent is self + child.parent = None + self.children.remove(child) + self.set_modified() + + def remove_children(self, children): + for child in children: + self.remove_child(child) + + def is_new(self): + return self.state == Node.STATE_NEW + + def needs_rebuilding(self): + return self.state != Node.STATE_BUILT + + def set_modified(self): + if self.needs_rebuilding(): + return + self.state = Node.STATE_MODIFIED + if self.parent is not None: + self.set_parent_modified(self.parent()) + + def set_parent_modified(self, parent): + parent.set_modified() + + def rebuild(self): + if not self.needs_rebuilding(): + return True + if self.children is not None: + if not self.rebuild_children(self.children): + return False + if not self.build(): + return False + self.state = Node.STATE_BUILT + return True + + def rebuild_children(self, children): + for child in children: + if not self.rebuild_child(child): + return False + return True + + def rebuild_child(self, child): + return child.rebuild() + + def build(self): + return True diff --git a/pve/pixmap.py b/pve/pixmap.py index 82000eb..858ea1a 100644 --- a/pve/pixmap.py +++ b/pve/pixmap.py @@ -60,10 +60,10 @@ class Format: def __init__(self, channels, depth): self._channels = channels self._depth = depth - if depth is 8: + if depth == 8: self._type = 'B' self._size = channels - elif depth is 16: + elif depth == 16: self._type = 'H' self._size = channels * 2 else: diff --git a/pve/polygon.py b/pve/polygon.py new file mode 100644 index 0000000..11c4359 --- /dev/null +++ b/pve/polygon.py @@ -0,0 +1,107 @@ +# People's Video Editor: high quality, GPU accelerated mp4 editor +# Copyright (C) 2025 Roz K +# +# This file is part of People's Video Editor. +# +# People's Video Editor is free software: you can redistribute it and/or modify it under the terms of the +# GNU General Public License as published by the Free Software Foundation, either version 3 of the License, +# or (at your option) any later version. +# +# People's Video Editor is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with People's Video Editor. +# If not, see . + +from array import array + +from .node import Node +from .vector import Vector2 + +class Point: + __slots__ = 'position', 'mapping' + + def __init__(self, xy = Vector2(), uv = Vector2()): + assert isinstance(xy, Vector2) and isinstance(uv, Vector2) + self.position = xy + self.mapping = uv + +class Polygon(Node): + __slots__ = '_positions', '_mappings', '_len' + + @classmethod + def rectangle(cls, geometry): + tl = Vector2(0.0, 0.0) + tr = Vector2(geometry._width, 0.0) + br = Vector2(geometry._width, geometry._height) + bl = Vector2(0.0, geometry._height) + return cls(points = ( + Point(tl, tl), + Point(tr, tr), + Point(br, br), + Point(bl, bl))) + + def __init__(self, parent = None, children = None, size = 0, points = None): + Node.__init__(self, parent, children) + self.clear_points() + self.add_points(size, points) + + def __len__(self): + return self._len + + def __getitem__(self, index): + begin = index * 2 + end = begin + 2 + return Point( + Vector2(*self._positions[begin:end]), + Vector2(*self._mappings[begin:end])) + + def __setitem__(self, index, point): + begin = index * 2 + end = begin + 2 + self._positions[begin:end] = array(Vector2.TYPE, point.position) + self._mappings[begin:end] = array(Vector2.TYPE, point.mapping) + + def add_child(self, child): + assert isinstance(child, Polygon) + Node.add_child(self, child) + + def add_points(self, count = 0, points = None): + assert (count > 0) ^ len(points) + if count: + initializer = bytes(count * Vector2.SIZE) + self._positions.frombytes(initializer) + self._mappings.frombytes(initializer) + self._len += count + else: + for point in points: + self._positions.extend(point.position) + self._mappings.extend(point.mapping) + self._len += len(points) + + def insert_points(self, index, count = 0, points = None): + assert (count == 0) ^ (points is None) + index *= 2 + if count: + initializer = array(Vector2.TYPE, bytes(count * Vector2.SIZE)) + self._positions[index:index] = initializer + self._mappings[index:index] = initializer + self._len += count + else: + polygon = Polygon(points = points) + self._positions[index:index] = polygon._positions + self._mappings[index:index] = polygon._mappings + self._len += polygon._len + + def remove_points(self, index, count = 1): + begin = index * 2 + end = begin + count * 2 + del self._positions[begin:end] + del self._mappings[begin:end] + self._len -= count + + def clear_points(self): + self._positions = array(Vector2.TYPE) + self._mappings = array(Vector2.TYPE) + self._len = 0 diff --git a/pve/shape.py b/pve/shape.py index fddf41e..05d41d0 100644 --- a/pve/shape.py +++ b/pve/shape.py @@ -14,151 +14,13 @@ # You should have received a copy of the GNU General Public License along with People's Video Editor. # If not, see . -#TODO: split channels +from .node import Node -import itertools -from array import array +class Shape(Node): -_X, _Y, _U, _V, _Channels = itertools.count(0) -_Type = 'f' -_Size = array(_Type).itemsize * _Channels + def __init__(self, parent = None, children = None): + Node.__init__(self, parent, children) -class Vertex: - __slots__ = 'view' - - Type = _Type - Channels = _Channels - Size = _Size - - def __init__(self, view): - assert isinstance(view, memoryview) - self.view = view - - def __del__(self): - del self.view - - @property - def xy(self): - return self.view[_X], self.view[_Y] - - @xy.setter - def xy(self, value): - self.view[_X], self.view[_Y] = value - - @property - def x(self): - return self.view[_X] - - @x.setter - def x(self, value): - self.view[_X] = value - - @property - def y(self): - return self.view[_Y] - - @y.setter - def y(self, value): - self.view[_Y] = value - - @property - def uv(self): - return self.view[_U], self.view[_V] - - @uv.setter - def uv(self, value): - self.view[_U], self.view[_V] = value - - @property - def u(self): - return self.view[_U] - - @u.setter - def u(self, value): - self.view[_U] = value - - @property - def v(self): - return self.view[_V] - - @v.setter - def v(self, value): - self.view[_V] = value - -class Shape: - __slots__ = '_array', '_view', '_size', '_cutouts' - - @classmethod - def rectangle(cls, geometry): - shape = cls(size = 4) - tl = Vertex(shape[0]) - tl.xy = tl.uv = (0.0, 0.0) - tr = Vertex(shape[1]) - tr.xy = tr.uv = (geometry._width, 0.0) - br = Vertex(shape[2]) - br.xy = br.uv = (geometry._width, geometry._height) - bl = Vertex(shape[3]) - bl.xy = bl.uv = (0.0, geometry._height) - return shape - - def __init__(self, size = 0, cutouts = None): - self._array = array(_Type, bytes(size * _Size)) - self._view = memoryview(self._array) - self._size = size - self._cutouts = list(cutouts) if cutouts else None - - def __del__(self): - del self._view - del self._array - del self._cutouts - - def __len__(self): - return self._size - - def __getitem__(self, index): - index *= _Channels - return self._view[index:(index + _Channels)] - - @property - def size(self): - return self._size - - @property - def view(self): - return self._view - - @property - def cutouts(self): - return list(self._cutouts) if self._cutouts else [] - - @cutouts.setter - def cutouts(self, value): - self._cutouts = list(value) if value else None - - def append(self, count = 1): - index = self._size - self._view.release() - self._array.frombytes(bytes(count * _Size)) - self._view = memoryview(self._array) - self._size += count - return index - - def insert(self, index, count = 1): - index *= _Channels - self._view.release() - self._array[index:index] = array(_Type, bytes(count * _Size)) - self._view = memoryview(self._array) - self._size += count - - def remove(self, index, count = 1): - index *= _Channels - self._view.release() - del self._array[index:(index + count * _Channels)] - self._view = memoryview(self._array) - self._size -= count - - def clear(self): - self._view.release() - self._array = array(_Type) - self._view = memoryview(self._array) - self._size = 0 + def rebuild(self): + #TODO + raise NotImplementedError diff --git a/pve/vector.py b/pve/vector.py new file mode 100644 index 0000000..df77847 --- /dev/null +++ b/pve/vector.py @@ -0,0 +1,29 @@ +# People's Video Editor: high quality, GPU accelerated mp4 editor +# Copyright (C) 2025 Roz K +# +# This file is part of People's Video Editor. +# +# People's Video Editor is free software: you can redistribute it and/or modify it under the terms of the +# GNU General Public License as published by the Free Software Foundation, either version 3 of the License, +# or (at your option) any later version. +# +# People's Video Editor is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along with People's Video Editor. +# If not, see . + +class Vector2: + __slots__ = 'x', 'y' + + TYPE = 'f' + SIZE = 8 + + def __init__(self, x = 0.0, y = 0.0): + assert isinstance(x, float) and isinstance(y, float) + self.x = x + self.y = y + + def __iter__(self): + return iter((self.x, self.y)) diff --git a/pygl/libgl/__init__.py b/pygl/libgl/__init__.py index c4e7b50..be7ea4e 100644 --- a/pygl/libgl/__init__.py +++ b/pygl/libgl/__init__.py @@ -31,11 +31,11 @@ def upload_texture(texture, view, line_size): def dowload_texture(texture, view, line_size): pass -def create_vertex_buffer(size): +def create_vertex_buffer(size, channels, elems): return None def delete_vertex_buffer(buffer): pass -def upload_vertex_buffer(buffer, view, size): +def upload_vertex_buffer(buffer, views, size): pass diff --git a/pygl/vertexbuffer.py b/pygl/vertexbuffer.py index 64dcf41..f6b7b2b 100644 --- a/pygl/vertexbuffer.py +++ b/pygl/vertexbuffer.py @@ -19,8 +19,8 @@ from . import libgl class VertexBuffer: __slots__ = '_buffer', '_size' - def __init__(self, size): - self._buffer = libgl.create_vertex_buffer(size) + def __init__(self, size, channels, elems): + self._buffer = libgl.create_vertex_buffer(size, channels, elems) self._size = size def __del__(self): @@ -32,4 +32,4 @@ class VertexBuffer: def upload(self, mesh): assert (self._size >= mesh.size) - return libgl.upload_vertex_buffer(self._buffer, mesh.view, mesh.size) + return libgl.upload_vertex_buffer(self._buffer, mesh.views, mesh.size)