• Nikitos's avatar
    Обрезка (scissor): $.gfx.clip и .clip() у узла
    · f714982d
    Nikitos создал
    §4.2 назвал дыру прямо: «grep scissor/clipRect = 0 — нет ни scissor, ни clip».
    Из-за этого не работали прокрутка списка, портрет в рамке и миникарта. В SDL3 GPU
    оказался SDL_SetGPUScissor (с 3.2.0), так что stencil для этого не понадобился —
    взял scissor, он закрывает ровно названную дыру.
    
    ЧТО СДЕЛАНО:
    * C: таблица обрезок кадра (R2D_MAX_CLIPS 256), r2d_render_set_clip/clear_clip/
      get_clip/clip_count; каждая команда помнит СВОЮ обрезку (R2DDrawCmd.clip), а
      проход рвёт участок по клипу и ставит scissor перед рисованием. Повтор той же
      обрезки не занимает новый слот — иначе скролл переполнил бы таблицу за секунду;
    * engine.setClip / clearClip / getClip / clipCount;
    * $.gfx.clip(x, y, w, h) | ({rect}) | (узел), clipOff(), clipRect(), clipCount();
    * .clip(true | {x,y,w,h} | false) у узла — обрезка по своей коробке или по
      прямоугольнику.
    
    КЛЮЧЕВОЕ: обрезка действует на КОМАНДУ, а не на кадр. Батч кадра один, но каждый
    спрайт помнит свой прямоугольник, и отправка рвётся по клипу. Поэтому РАЗНЫЕ УЗЛЫ
    ОДНОГО КАДРА обрезаются по-разному — это и проверено тестом (два узла, два клипа,
    3600 пикселей каждый).
    
    ЕЩЁ ОДНА ТОНКОСТЬ, найденная по дороге: сброс обрезок стоял в начале _render, а
    игра ставит клип в $.render, который идёт РАНЬШЕ — клип стирался перед самой
    отрисовкой. Сброс перенесён в начало кадра отрисовки (setRender).
    
    ПРОВЕРКА (tests/agent/highlevel_clip_test.py, по пикселям): без обрезки узел
    виден весь; клип 600,500,100,80 даёт ровно 600..699 x 500..579; clipOff возвращает
    целый узел; обрезка узла НЕ трогает соседа (не «растекается»); два узла одного
    кадра обрезаются каждый своим; нулевой прямоугольник снимает обрезку.
    
    ДОКИ: render.md §5.1 (как работает, где вызывать, таблица вызовов, ограничения —
    обрезка узла не наследуется детьми), API.md (engine.setClip и родня), §1.6 TASKS
    переписан из «нет scissor/clip» в «сделано».
    
    Полный qjs, агентские тесты, doc_coverage, doc_claims, duplicate_keys, быстрый
    набор — зелёные.
    f714982d