Source code for simvx.graphics.draw2d_batch

"""Scissor clipping and draw batch management for Draw2D.

Groups per-frame geometry into batches separated by clip-rect changes,
then converts raw tuple lists to structured numpy arrays for GPU upload.
"""

from __future__ import annotations

import numpy as np

from .draw2d_vertex import UI_VERTEX_DTYPE


[docs] class Draw2DBatchMixin: """Mixin providing scissor clipping and batch management for Draw2D.""" # Scissor clipping _clip_stack: list[tuple[int, int, int, int]] = [] # Batches: list of (clip, fill_verts, fill_indices, line_verts, text_verts, text_indices, tex_quads) _batches: list[tuple] = [] _current_clip: tuple[int, int, int, int] | None = None
[docs] @classmethod def push_clip(cls, x: int, y: int, w: int, h: int): """Push a scissor clip rect. Content outside is not drawn. Nested clips are intersected with the current clip. """ # Flush current geometry into a batch before changing clip cls._flush_batch() new_clip = (int(x), int(y), int(w), int(h)) if cls._current_clip: # Intersect with current clip cx, cy, cw, ch = cls._current_clip nx = max(cx, new_clip[0]) ny = max(cy, new_clip[1]) nx2 = min(cx + cw, new_clip[0] + new_clip[2]) ny2 = min(cy + ch, new_clip[1] + new_clip[3]) new_clip = (nx, ny, max(0, nx2 - nx), max(0, ny2 - ny)) cls._clip_stack.append(cls._current_clip) cls._current_clip = new_clip
[docs] @classmethod def pop_clip(cls): """Pop the last clip rect, restoring the previous one.""" cls._flush_batch() if cls._clip_stack: cls._current_clip = cls._clip_stack.pop() else: cls._current_clip = None
[docs] @classmethod def new_layer(cls): """Force a batch break so subsequent draws render on top of all prior geometry.""" cls._flush_batch()
[docs] @classmethod def reset_clip(cls): """Clear the clip stack and current clip, restoring full-screen drawing.""" cls._flush_batch() cls._clip_stack.clear() cls._current_clip = None
@classmethod def _flush_batch(cls): """Save current geometry as a batch with the current clip rect.""" if cls._fill_verts or cls._line_verts or cls._text_verts or cls._textured_quads: cls._batches.append( ( cls._current_clip, list(cls._fill_verts), list(cls._fill_indices), list(cls._line_verts), list(cls._text_verts), list(cls._text_indices), list(cls._textured_quads), ) ) cls._fill_verts.clear() cls._fill_indices.clear() cls._line_verts.clear() cls._text_verts.clear() cls._text_indices.clear() cls._textured_quads.clear() @classmethod def _verts_from_tuples(cls, vert_list): """Convert list of 8-float tuples to structured vertex array (vectorized).""" arr = np.array(vert_list, dtype=np.float32) # (N, 8) verts = np.zeros(len(vert_list), dtype=UI_VERTEX_DTYPE) verts["position"] = arr[:, :2] verts["uv"] = arr[:, 2:4] verts["colour"] = arr[:, 4:8] return verts @classmethod def _get_batches(cls): """Get all batches for rendering. Each batch: (clip, fill_data, line_data, text_data, tex_draws) where tex_draws is a list of (texture_id, verts_array, indices_array). """ cls._flush_batch() result = [] for batch in cls._batches: clip = batch[0] fill_v, fill_i = batch[1], batch[2] line_v = batch[3] text_v, text_i = batch[4], batch[5] tex_quads = batch[6] if len(batch) > 6 else [] fill_data = None if fill_v: verts = cls._verts_from_tuples(fill_v) indices = np.array(fill_i, dtype=np.uint32) fill_data = (verts, indices) line_data = None if line_v: line_data = cls._verts_from_tuples(line_v) text_data = None if text_v: verts = cls._verts_from_tuples(text_v) indices = np.array(text_i, dtype=np.uint32) text_data = (verts, indices) # Convert textured quads: group by texture_id, build arrays tex_draws = [] if tex_quads: by_tex: dict[int, tuple[list[tuple], list[int]]] = {} for tex_id, vlist, ilist in tex_quads: if tex_id not in by_tex: by_tex[tex_id] = ([], []) base = len(by_tex[tex_id][0]) by_tex[tex_id][0].extend(vlist) by_tex[tex_id][1].extend(i + base for i in ilist) for tex_id, (vlist, ilist) in by_tex.items(): verts = cls._verts_from_tuples(vlist) indices = np.array(ilist, dtype=np.uint32) tex_draws.append((tex_id, verts, indices)) result.append((clip, fill_data, line_data, text_data, tex_draws)) return result