Phase 11b: fill свободных camera-cells остальными drawable камерами
User: "смущает чёрная ячейка в сетке". Причина: asymmetric templates имели widget cells (placeholder тёмно-серый Y=40) + при недостатке active camera cells оставались BlankCell (чёрный). Два изменения: 1. templates.json — оставили только 16:9 layouts (tpl_1/tpl_4/tpl_9/tpl_16). Все camera-cells, никаких widget-областей. Cells full 16:9 (cs==rs микроячейки), полностью покрывают output 1920×1080 без чёрных полос. Asymmetric layouts (main + satellites) удалены — вернуть в Phase 12 когда widget'ы будут реальными (HA-chat, temperature graph). 2. composer::maybe_relayout — заполнить свободные camera-cells остальными drawable камерами из pool (по priority), если template имеет больше cells чем motion-active. Условие: cap > active.size(). Производство при 4 источниках в pool: - 1 motion → tpl_1 (1 cell full screen) - 2 motion → tpl_4 (2 motion + 2 not-active drawable = 4 cells заняты) - 4 motion → tpl_4 (все 4 motion) - При добавлении новых камер (до 16) — tpl_9 при 5..9, tpl_16 при 10..16 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
+31
-58
@@ -2,7 +2,7 @@
|
||||
"version": 1,
|
||||
"grid_cols": 8,
|
||||
"grid_rows": 8,
|
||||
"_doc": "Layout-templates для cfc-grid auto-layout. Координаты в микроячейках 8×8 (output 1920×1080 → каждая микроячейка 240×135 px, 16:9). Квадраты N×N микроячеек тоже 16:9. role=camera — заполняется из активных камер по priority. role=widget — placeholder.",
|
||||
"_doc": "Phase 11b — только пропорциональные 16:9 layouts (cs == rs микроячеек). Полностью заполняют output 1920×1080 без widget-областей. При недостатке active-камер по motion свободные camera-cells заполняются остальными drawable-камерами из pool (по priority). См. cfc::Composer::maybe_relayout().",
|
||||
|
||||
"templates": [
|
||||
{
|
||||
@@ -12,20 +12,9 @@
|
||||
{"col": 0, "row": 0, "cs": 8, "rs": 8, "role": "camera", "order": 0}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "tpl_3",
|
||||
"_desc": "Главная 1440×810 слева + 2 превью 480×270 справа стопкой, остаток — виджеты.",
|
||||
"cells": [
|
||||
{"col": 0, "row": 0, "cs": 6, "rs": 6, "role": "camera", "order": 0},
|
||||
{"col": 6, "row": 0, "cs": 2, "rs": 2, "role": "camera", "order": 1},
|
||||
{"col": 6, "row": 2, "cs": 2, "rs": 2, "role": "camera", "order": 2},
|
||||
{"col": 6, "row": 4, "cs": 2, "rs": 4, "role": "widget", "widget": "temp_chart"},
|
||||
{"col": 0, "row": 6, "cs": 6, "rs": 2, "role": "widget", "widget": "ha_chat"}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "tpl_4",
|
||||
"_desc": "Quad 2×2: 4 камеры 960×540. order=0 — top-left (главная).",
|
||||
"_desc": "Quad 2×2 — 4 камеры 960×540 (16:9). order=0 — top-left (главная).",
|
||||
"cells": [
|
||||
{"col": 0, "row": 0, "cs": 4, "rs": 4, "role": "camera", "order": 0},
|
||||
{"col": 4, "row": 0, "cs": 4, "rs": 4, "role": "camera", "order": 1},
|
||||
@@ -34,56 +23,40 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "tpl_5",
|
||||
"_desc": "1 главная + 4 превью справа стопкой, нижняя полоса — виджет.",
|
||||
"name": "tpl_9",
|
||||
"_desc": "3×3 grid — 9 камер... только cells 2×2 микроячейки по 6×6 области. ВНИМАНИЕ: 8×8 не делится на 3 без остатка; используем 6×6 камер + bottom/right остаток как background. Если потом будут asymmetric с widget'ами — выделим Phase 12.",
|
||||
"cells": [
|
||||
{"col": 0, "row": 0, "cs": 6, "rs": 6, "role": "camera", "order": 0},
|
||||
{"col": 6, "row": 0, "cs": 2, "rs": 2, "role": "camera", "order": 1},
|
||||
{"col": 6, "row": 2, "cs": 2, "rs": 2, "role": "camera", "order": 2},
|
||||
{"col": 6, "row": 4, "cs": 2, "rs": 2, "role": "camera", "order": 3},
|
||||
{"col": 6, "row": 6, "cs": 2, "rs": 2, "role": "camera", "order": 4},
|
||||
{"col": 0, "row": 6, "cs": 6, "rs": 2, "role": "widget", "widget": "ha_chat"}
|
||||
{"col": 0, "row": 0, "cs": 2, "rs": 2, "role": "camera", "order": 0},
|
||||
{"col": 2, "row": 0, "cs": 2, "rs": 2, "role": "camera", "order": 1},
|
||||
{"col": 4, "row": 0, "cs": 2, "rs": 2, "role": "camera", "order": 2},
|
||||
{"col": 0, "row": 2, "cs": 2, "rs": 2, "role": "camera", "order": 3},
|
||||
{"col": 2, "row": 2, "cs": 2, "rs": 2, "role": "camera", "order": 4},
|
||||
{"col": 4, "row": 2, "cs": 2, "rs": 2, "role": "camera", "order": 5},
|
||||
{"col": 0, "row": 4, "cs": 2, "rs": 2, "role": "camera", "order": 6},
|
||||
{"col": 2, "row": 4, "cs": 2, "rs": 2, "role": "camera", "order": 7},
|
||||
{"col": 4, "row": 4, "cs": 2, "rs": 2, "role": "camera", "order": 8}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "tpl_6",
|
||||
"_desc": "1 главная + 3 правые + 2 нижние, остаток — виджет.",
|
||||
"name": "tpl_16",
|
||||
"_desc": "4×4 grid — 16 камер по 480×270 (16:9). Полностью покрывает 8×8 без остатка.",
|
||||
"cells": [
|
||||
{"col": 0, "row": 0, "cs": 6, "rs": 6, "role": "camera", "order": 0},
|
||||
{"col": 6, "row": 0, "cs": 2, "rs": 2, "role": "camera", "order": 1},
|
||||
{"col": 6, "row": 2, "cs": 2, "rs": 2, "role": "camera", "order": 2},
|
||||
{"col": 6, "row": 4, "cs": 2, "rs": 2, "role": "camera", "order": 3},
|
||||
{"col": 0, "row": 6, "cs": 2, "rs": 2, "role": "camera", "order": 4},
|
||||
{"col": 2, "row": 6, "cs": 2, "rs": 2, "role": "camera", "order": 5},
|
||||
{"col": 4, "row": 6, "cs": 4, "rs": 2, "role": "widget", "widget": "info"}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "tpl_7",
|
||||
"_desc": "1 главная + 3 правые + 3 нижние, угол — виджет.",
|
||||
"cells": [
|
||||
{"col": 0, "row": 0, "cs": 6, "rs": 6, "role": "camera", "order": 0},
|
||||
{"col": 6, "row": 0, "cs": 2, "rs": 2, "role": "camera", "order": 1},
|
||||
{"col": 6, "row": 2, "cs": 2, "rs": 2, "role": "camera", "order": 2},
|
||||
{"col": 6, "row": 4, "cs": 2, "rs": 2, "role": "camera", "order": 3},
|
||||
{"col": 0, "row": 6, "cs": 2, "rs": 2, "role": "camera", "order": 4},
|
||||
{"col": 2, "row": 6, "cs": 2, "rs": 2, "role": "camera", "order": 5},
|
||||
{"col": 4, "row": 6, "cs": 2, "rs": 2, "role": "camera", "order": 6},
|
||||
{"col": 6, "row": 6, "cs": 2, "rs": 2, "role": "widget", "widget": "ha_chat"}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "tpl_8",
|
||||
"_desc": "1+3+4 — главная + 3 правые + полная нижняя строка.",
|
||||
"cells": [
|
||||
{"col": 0, "row": 0, "cs": 6, "rs": 6, "role": "camera", "order": 0},
|
||||
{"col": 6, "row": 0, "cs": 2, "rs": 2, "role": "camera", "order": 1},
|
||||
{"col": 6, "row": 2, "cs": 2, "rs": 2, "role": "camera", "order": 2},
|
||||
{"col": 6, "row": 4, "cs": 2, "rs": 2, "role": "camera", "order": 3},
|
||||
{"col": 0, "row": 6, "cs": 2, "rs": 2, "role": "camera", "order": 4},
|
||||
{"col": 2, "row": 6, "cs": 2, "rs": 2, "role": "camera", "order": 5},
|
||||
{"col": 4, "row": 6, "cs": 2, "rs": 2, "role": "camera", "order": 6},
|
||||
{"col": 6, "row": 6, "cs": 2, "rs": 2, "role": "camera", "order": 7}
|
||||
{"col": 0, "row": 0, "cs": 2, "rs": 2, "role": "camera", "order": 0},
|
||||
{"col": 2, "row": 0, "cs": 2, "rs": 2, "role": "camera", "order": 1},
|
||||
{"col": 4, "row": 0, "cs": 2, "rs": 2, "role": "camera", "order": 2},
|
||||
{"col": 6, "row": 0, "cs": 2, "rs": 2, "role": "camera", "order": 3},
|
||||
{"col": 0, "row": 2, "cs": 2, "rs": 2, "role": "camera", "order": 4},
|
||||
{"col": 2, "row": 2, "cs": 2, "rs": 2, "role": "camera", "order": 5},
|
||||
{"col": 4, "row": 2, "cs": 2, "rs": 2, "role": "camera", "order": 6},
|
||||
{"col": 6, "row": 2, "cs": 2, "rs": 2, "role": "camera", "order": 7},
|
||||
{"col": 0, "row": 4, "cs": 2, "rs": 2, "role": "camera", "order": 8},
|
||||
{"col": 2, "row": 4, "cs": 2, "rs": 2, "role": "camera", "order": 9},
|
||||
{"col": 4, "row": 4, "cs": 2, "rs": 2, "role": "camera", "order": 10},
|
||||
{"col": 6, "row": 4, "cs": 2, "rs": 2, "role": "camera", "order": 11},
|
||||
{"col": 0, "row": 6, "cs": 2, "rs": 2, "role": "camera", "order": 12},
|
||||
{"col": 2, "row": 6, "cs": 2, "rs": 2, "role": "camera", "order": 13},
|
||||
{"col": 4, "row": 6, "cs": 2, "rs": 2, "role": "camera", "order": 14},
|
||||
{"col": 6, "row": 6, "cs": 2, "rs": 2, "role": "camera", "order": 15}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
@@ -215,6 +215,26 @@ void Composer::maybe_relayout()
|
||||
int cap = tpl->nb_camera_cells();
|
||||
if (static_cast<int>(active.size()) > cap) active.resize(cap);
|
||||
|
||||
/* Если template имеет больше camera-cells чем активных по motion —
|
||||
* заполнить оставшиеся drawable камерами из pool (по priority),
|
||||
* которые ещё не вошли в active. Это убирает "чёрные ячейки"
|
||||
* в asymmetric layouts (tpl_3/5/6/7 + tpl_4 при active<4). */
|
||||
if (static_cast<int>(active.size()) < cap) {
|
||||
std::vector<PoolEntry*> already(active.begin(), active.end());
|
||||
std::vector<PoolEntry*> extras;
|
||||
const_cast<SourcePool&>(pool_).for_each([&](PoolEntry& e) {
|
||||
if (!e.drawable()) return;
|
||||
for (auto* a : already) if (a == &e) return;
|
||||
extras.push_back(&e);
|
||||
});
|
||||
std::sort(extras.begin(), extras.end(),
|
||||
[](PoolEntry* a, PoolEntry* b) { return a->priority > b->priority; });
|
||||
for (auto* e : extras) {
|
||||
if (static_cast<int>(active.size()) >= cap) break;
|
||||
active.push_back(e);
|
||||
}
|
||||
}
|
||||
|
||||
std::string sig = build_signature(tpl->name, active);
|
||||
|
||||
if (sig == committed_signature_) {
|
||||
|
||||
Reference in New Issue
Block a user