dsh-pf-stream 0.0.0-stage → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. package/LICENSE +201 -0
  2. package/README.en.md +110 -0
  3. package/README.md +109 -2
  4. package/cordis.patch.yml +2 -0
  5. package/lib/entry/adapter.d.ts +21 -0
  6. package/lib/entry/adapter.js +112 -0
  7. package/lib/entry/adapter.js.map +1 -0
  8. package/lib/entry/legacyNormalizer.d.ts +16 -0
  9. package/lib/entry/legacyNormalizer.js +86 -0
  10. package/lib/entry/legacyNormalizer.js.map +1 -0
  11. package/lib/errors.d.ts +7 -0
  12. package/lib/errors.js +12 -0
  13. package/lib/errors.js.map +1 -0
  14. package/lib/index.d.ts +108 -0
  15. package/lib/index.js +109 -0
  16. package/lib/index.js.map +1 -0
  17. package/lib/session/state.d.ts +25 -0
  18. package/lib/session/state.js +255 -0
  19. package/lib/session/state.js.map +1 -0
  20. package/lib/stage0/decoder.d.ts +57 -0
  21. package/lib/stage0/decoder.js +229 -0
  22. package/lib/stage0/decoder.js.map +1 -0
  23. package/lib/stage0/ffmpeg.d.ts +34 -0
  24. package/lib/stage0/ffmpeg.js +90 -0
  25. package/lib/stage0/ffmpeg.js.map +1 -0
  26. package/lib/stage0/probe.d.ts +47 -0
  27. package/lib/stage0/probe.js +150 -0
  28. package/lib/stage0/probe.js.map +1 -0
  29. package/lib/stage1/gradient.d.ts +27 -0
  30. package/lib/stage1/gradient.js +85 -0
  31. package/lib/stage1/gradient.js.map +1 -0
  32. package/lib/stage1/perceive.d.ts +66 -0
  33. package/lib/stage1/perceive.js +233 -0
  34. package/lib/stage1/perceive.js.map +1 -0
  35. package/lib/stage2/stateMachine.d.ts +70 -0
  36. package/lib/stage2/stateMachine.js +180 -0
  37. package/lib/stage2/stateMachine.js.map +1 -0
  38. package/lib/stage2/switchDetector.d.ts +18 -0
  39. package/lib/stage2/switchDetector.js +34 -0
  40. package/lib/stage2/switchDetector.js.map +1 -0
  41. package/lib/stage3/deterministicRouter.d.ts +44 -0
  42. package/lib/stage3/deterministicRouter.js +203 -0
  43. package/lib/stage3/deterministicRouter.js.map +1 -0
  44. package/lib/stage3/streamSampler.d.ts +74 -0
  45. package/lib/stage3/streamSampler.js +497 -0
  46. package/lib/stage3/streamSampler.js.map +1 -0
  47. package/lib/stage4/canvasPacker.d.ts +60 -0
  48. package/lib/stage4/canvasPacker.js +161 -0
  49. package/lib/stage4/canvasPacker.js.map +1 -0
  50. package/lib/stage4/markers.d.ts +19 -0
  51. package/lib/stage4/markers.js +98 -0
  52. package/lib/stage4/markers.js.map +1 -0
  53. package/lib/stage4/viewport.d.ts +61 -0
  54. package/lib/stage4/viewport.js +151 -0
  55. package/lib/stage4/viewport.js.map +1 -0
  56. package/lib/stage5/controller.d.ts +61 -0
  57. package/lib/stage5/controller.js +260 -0
  58. package/lib/stage5/controller.js.map +1 -0
  59. package/lib/stage5/intent.d.ts +27 -0
  60. package/lib/stage5/intent.js +87 -0
  61. package/lib/stage5/intent.js.map +1 -0
  62. package/lib/stage6/history.d.ts +52 -0
  63. package/lib/stage6/history.js +155 -0
  64. package/lib/stage6/history.js.map +1 -0
  65. package/lib/stage6/precheck.d.ts +56 -0
  66. package/lib/stage6/precheck.js +86 -0
  67. package/lib/stage6/precheck.js.map +1 -0
  68. package/lib/tool/handler.d.ts +35 -0
  69. package/lib/tool/handler.js +56 -0
  70. package/lib/tool/handler.js.map +1 -0
  71. package/lib/tool/schema.d.ts +29 -0
  72. package/lib/tool/schema.js +59 -0
  73. package/lib/tool/schema.js.map +1 -0
  74. package/lib/tool/streamCall.d.ts +41 -0
  75. package/lib/tool/streamCall.js +105 -0
  76. package/lib/tool/streamCall.js.map +1 -0
  77. package/lib/types.d.ts +355 -0
  78. package/lib/types.js +11 -0
  79. package/lib/types.js.map +1 -0
  80. package/lib/utils/ffmpegBin.d.ts +20 -0
  81. package/lib/utils/ffmpegBin.js +62 -0
  82. package/lib/utils/ffmpegBin.js.map +1 -0
  83. package/lib/utils/hash.d.ts +13 -0
  84. package/lib/utils/hash.js +24 -0
  85. package/lib/utils/hash.js.map +1 -0
  86. package/lib/utils/image.d.ts +32 -0
  87. package/lib/utils/image.js +135 -0
  88. package/lib/utils/image.js.map +1 -0
  89. package/lib/utils/pts.d.ts +15 -0
  90. package/lib/utils/pts.js +50 -0
  91. package/lib/utils/pts.js.map +1 -0
  92. package/package.json +55 -4
@@ -0,0 +1,161 @@
1
+ /**
2
+ * Stage 4 —— 画布打包(FORM_1..4 拼合 + PNG 字节锁定)。
3
+ *
4
+ * 排版规则(《二、双尺度视觉单元与画布形态规范》§3):
5
+ * - FORM_1 1344×672:左半区 2×2 排列 4P(P1 左上/P2 右上/P3 左下/P4 右下),右半区 1F;
6
+ * - FORM_2 672×672:2×2 排列 4P;
7
+ * - FORM_3 672×672:1F 充满画布;
8
+ * - FORM_4 1344×672:1~3P 按象限顺序填入,空象限中性灰,右半区 1F。
9
+ *
10
+ * 单元视觉标记绘制在单元图像自身(含标记的 F 像素在双载体间逐位一致)。
11
+ */
12
+ import { createHash } from 'node:crypto';
13
+ import { composeCanvas, encodePng } from '../utils/image.js';
14
+ import { contentHashOfRgb } from '../utils/hash.js';
15
+ import { drawIdBadge, drawSeparators } from './markers.js';
16
+ /** 画布尺寸(宽, 高)。 */
17
+ export function canvasSizeOf(form) {
18
+ return form === 'FORM_1_STANDARD_STRIP' || form === 'FORM_4_PARTIAL_STRIP' ? [1344, 672] : [672, 672];
19
+ }
20
+ /** 计费序列长度:r×(c+1)+2(画布尺寸为 42 倍数,整除精确)。 */
21
+ export function billTokens(width, height) {
22
+ const r = height / 42;
23
+ const c = width / 42;
24
+ return r * (c + 1) + 2;
25
+ }
26
+ function descriptor(unit, x, y) {
27
+ const w = unit.image.width;
28
+ const h = unit.image.height;
29
+ return {
30
+ globalId: unit.globalId,
31
+ timestampMs: unit.ptsMs,
32
+ scale: unit.scale,
33
+ eventType: unit.eventType,
34
+ tokenGridBbox: [y / 42, x / 42, (y + h) / 42, (x + w) / 42],
35
+ contentPixelBbox: [x, y, x + w, y + h],
36
+ sourceCropBbox: unit.sourceCropBbox,
37
+ };
38
+ }
39
+ function sha256(bytes) {
40
+ return createHash('sha256').update(bytes).digest('hex');
41
+ }
42
+ /**
43
+ * 打包一个画布包。调用方保证单元数量与形态匹配:
44
+ * FORM_1=4P+1F,FORM_2=4P,FORM_3=1F,FORM_4=1~3P+1F。
45
+ */
46
+ export function packCanvas(form, units, options) {
47
+ const [canvasW, canvasH] = canvasSizeOf(form);
48
+ const placements = [];
49
+ const descriptors = [];
50
+ let anchorBytes = null;
51
+ let anchorFId = null;
52
+ let anchorSha = null;
53
+ let anchorContentHash = null;
54
+ const pUnits = units.filter((u) => u.scale === '1x1_P');
55
+ const fUnits = units.filter((u) => u.scale === '2x2_F');
56
+ const fUnit = fUnits[0] ?? null;
57
+ // 单元标记:隔离线 + 反色角标(绘制在单元图像副本上)
58
+ const marked = units.map((u) => {
59
+ const image = { width: u.image.width, height: u.image.height, data: Buffer.from(u.image.data) };
60
+ drawSeparators(image, [{ x: 0, y: 0, w: image.width, h: image.height }]);
61
+ if (options.renderVisualId)
62
+ drawIdBadge(image, 0, 0, u.globalId);
63
+ return { ...u, image };
64
+ });
65
+ if (form === 'FORM_1_STANDARD_STRIP') {
66
+ if (pUnits.length !== 4 || !fUnit)
67
+ throw new Error('FORM_1 需要 4P+1F');
68
+ for (let i = 0; i < 4; i++) {
69
+ const u = marked[i];
70
+ const { x, y } = { x: (i % 2) * 336, y: Math.floor(i / 2) * 336 };
71
+ placements.push({ x, y, image: u.image });
72
+ descriptors.push(descriptor(u, x, y));
73
+ }
74
+ const f = marked[4];
75
+ placements.push({ x: 672, y: 0, image: f.image });
76
+ descriptors.push(descriptor(f, 672, 0));
77
+ }
78
+ else if (form === 'FORM_2_COMPACT_4P') {
79
+ if (pUnits.length !== 4)
80
+ throw new Error('FORM_2 需要 4P');
81
+ for (let i = 0; i < 4; i++) {
82
+ const u = marked[i];
83
+ const x = (i % 2) * 336;
84
+ const y = Math.floor(i / 2) * 336;
85
+ placements.push({ x, y, image: u.image });
86
+ descriptors.push(descriptor(u, x, y));
87
+ }
88
+ }
89
+ else if (form === 'FORM_3_COMPACT_1F') {
90
+ if (!fUnit)
91
+ throw new Error('FORM_3 需要 1F');
92
+ const f = marked[0];
93
+ placements.push({ x: 0, y: 0, image: f.image });
94
+ descriptors.push(descriptor(f, 0, 0));
95
+ }
96
+ else {
97
+ // FORM_4_PARTIAL_STRIP:1~3P + 1F
98
+ if (pUnits.length < 1 || pUnits.length > 3 || !fUnit)
99
+ throw new Error('FORM_4 需要 1~3P+1F');
100
+ for (let i = 0; i < pUnits.length; i++) {
101
+ const u = marked[i];
102
+ const x = (i % 2) * 336;
103
+ const y = Math.floor(i / 2) * 336;
104
+ placements.push({ x, y, image: u.image });
105
+ descriptors.push(descriptor(u, x, y));
106
+ }
107
+ const f = marked[marked.length - 1];
108
+ placements.push({ x: 672, y: 0, image: f.image });
109
+ descriptors.push(descriptor(f, 672, 0));
110
+ }
111
+ const canvas = composeCanvas(canvasW, canvasH, placements);
112
+ const imageBytes = encodePng(canvas);
113
+ if (fUnit) {
114
+ anchorFId = fUnit.globalId;
115
+ const fMarked = marked.find((u) => u.scale === '2x2_F');
116
+ anchorBytes = encodePng(fMarked.image);
117
+ anchorSha = sha256(anchorBytes);
118
+ anchorContentHash = contentHashOfRgb(fMarked.image.width, fMarked.image.height, fMarked.image.data);
119
+ }
120
+ const stripSha = form === 'FORM_3_COMPACT_1F' ? null : sha256(imageBytes);
121
+ const packet = {
122
+ packetId: options.packetId,
123
+ canvasSize: [canvasW, canvasH],
124
+ layoutForm: form,
125
+ triggerPath: options.triggerPath,
126
+ historyMode: 'initial',
127
+ anchorOnlyRefPacketId: null,
128
+ spatialRemap: null,
129
+ anchorFId,
130
+ anchorFSha256: anchorSha,
131
+ stripSha256: stripSha,
132
+ appliedIntentId: options.appliedIntentId,
133
+ appliedCtrlId: options.appliedCtrlId ?? [],
134
+ units: descriptors,
135
+ };
136
+ return { packet, imageBytes, anchorBytes, anchorFContentHash: anchorContentHash };
137
+ }
138
+ /** 时序提示行位置标签。 */
139
+ export function slotLabel(form, index, isF) {
140
+ if (isF)
141
+ return form === 'FORM_3_COMPACT_1F' ? 'Full-Canvas' : 'Right-Half';
142
+ const labels = ['Left-Top', 'Right-Top', 'Left-Bottom', 'Right-Bottom'];
143
+ return labels[index] ?? 'Left-Top';
144
+ }
145
+ /** 生成结构化时序映射提示行(首次发包 / 确定性格式)。 */
146
+ export function timelineHintLine(packet, roi) {
147
+ const parts = packet.units.map((u, i) => {
148
+ const t = `${(u.timestampMs / 1000).toFixed(1)}s`;
149
+ const isF = u.scale === '2x2_F';
150
+ const slot = slotLabel(packet.layoutForm, i, isF);
151
+ const roiPart = isF && packet.triggerPath === 'DETERMINISTIC' && roi
152
+ ? `, ROI:[${roi.x},${roi.y},${roi.w},${roi.h}]`
153
+ : '';
154
+ return `#${u.globalId}(${isF ? 'F' : 'P'}, ${t}${roiPart}, ${slot})`;
155
+ });
156
+ const body = parts.join(' -> ');
157
+ const anchor = packet.anchorFId != null ? `Anchor: #${packet.anchorFId} | ` : '';
158
+ const mode = packet.triggerPath === 'DETERMINISTIC' ? 'Mode: DETERMINISTIC | ' : '';
159
+ return `[Packet #${packet.packetId} | ${mode}${anchor}${body}]`;
160
+ }
161
+ //# sourceMappingURL=canvasPacker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"canvasPacker.js","sourceRoot":"","sources":["../../src/stage4/canvasPacker.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * 消费端视觉标记(《六、发包控制…》§4):
3
+ * - 双色高对比内缩隔离线:单元边缘向内侧 2px(外层 1px 深色 RGB(32,32,32) + 内层 1px 亮色 RGB(224,224,224));
4
+ * - 左上角超像素微型反色角标:每单元首个 42×42 超像素内反色胶囊渲染 #global_id(renderVisualId 开关)。
5
+ */
6
+ import type { RgbImage } from '../utils/image.js';
7
+ export interface UnitBbox {
8
+ x: number;
9
+ y: number;
10
+ w: number;
11
+ h: number;
12
+ }
13
+ /** 在画布上为指定单元区域绘制双色内缩隔离线。 */
14
+ export declare function drawSeparators(canvas: RgbImage, boxes: UnitBbox[]): void;
15
+ /**
16
+ * 反色胶囊角标:在 (originX, originY) 起始的 42×42 超像素内,
17
+ * 对胶囊区域逐像素反色,字型位保持原色(双重反色,高对比)。
18
+ */
19
+ export declare function drawIdBadge(canvas: RgbImage, originX: number, originY: number, globalId: number): void;
@@ -0,0 +1,98 @@
1
+ /**
2
+ * 消费端视觉标记(《六、发包控制…》§4):
3
+ * - 双色高对比内缩隔离线:单元边缘向内侧 2px(外层 1px 深色 RGB(32,32,32) + 内层 1px 亮色 RGB(224,224,224));
4
+ * - 左上角超像素微型反色角标:每单元首个 42×42 超像素内反色胶囊渲染 #global_id(renderVisualId 开关)。
5
+ */
6
+ const SEPARATOR_DARK = 32;
7
+ const SEPARATOR_LIGHT = 224;
8
+ /** 在画布上为指定单元区域绘制双色内缩隔离线。 */
9
+ export function drawSeparators(canvas, boxes) {
10
+ const { width: cw, height: ch, data } = canvas;
11
+ for (const b of boxes) {
12
+ // 外层 1px 深色(单元边缘内侧)
13
+ fillRect(data, cw, ch, b.x, b.y, b.w, 1, SEPARATOR_DARK);
14
+ fillRect(data, cw, ch, b.x, b.y + b.h - 1, b.w, 1, SEPARATOR_DARK);
15
+ fillRect(data, cw, ch, b.x, b.y, 1, b.h, SEPARATOR_DARK);
16
+ fillRect(data, cw, ch, b.x + b.w - 1, b.y, 1, b.h, SEPARATOR_DARK);
17
+ // 内层 1px 亮色
18
+ fillRect(data, cw, ch, b.x + 1, b.y + 1, b.w - 2, 1, SEPARATOR_LIGHT);
19
+ fillRect(data, cw, ch, b.x + 1, b.y + b.h - 2, b.w - 2, 1, SEPARATOR_LIGHT);
20
+ fillRect(data, cw, ch, b.x + 1, b.y + 1, 1, b.h - 2, SEPARATOR_LIGHT);
21
+ fillRect(data, cw, ch, b.x + b.w - 2, b.y + 1, 1, b.h - 2, SEPARATOR_LIGHT);
22
+ }
23
+ }
24
+ function fillRect(data, cw, ch, x, y, w, h, value) {
25
+ for (let dy = 0; dy < h; dy++) {
26
+ const py = y + dy;
27
+ if (py < 0 || py >= ch)
28
+ continue;
29
+ for (let dx = 0; dx < w; dx++) {
30
+ const px = x + dx;
31
+ if (px < 0 || px >= cw)
32
+ continue;
33
+ const i = (py * cw + px) * 3;
34
+ data[i] = value;
35
+ data[i + 1] = value;
36
+ data[i + 2] = value;
37
+ }
38
+ }
39
+ }
40
+ /** 3×5 位图字型(# 与数字)。 */
41
+ const GLYPHS = {
42
+ '#': ['101', '111', '101', '111', '101'],
43
+ '0': ['111', '101', '101', '101', '111'],
44
+ '1': ['010', '110', '010', '010', '111'],
45
+ '2': ['111', '001', '111', '100', '111'],
46
+ '3': ['111', '001', '111', '001', '111'],
47
+ '4': ['101', '101', '111', '001', '001'],
48
+ '5': ['111', '100', '111', '001', '111'],
49
+ '6': ['111', '100', '111', '101', '111'],
50
+ '7': ['111', '001', '001', '001', '001'],
51
+ '8': ['111', '101', '111', '101', '111'],
52
+ '9': ['111', '101', '111', '001', '111'],
53
+ };
54
+ /**
55
+ * 反色胶囊角标:在 (originX, originY) 起始的 42×42 超像素内,
56
+ * 对胶囊区域逐像素反色,字型位保持原色(双重反色,高对比)。
57
+ */
58
+ export function drawIdBadge(canvas, originX, originY, globalId) {
59
+ const text = `#${globalId}`;
60
+ const { width: cw, height: ch, data } = canvas;
61
+ const boxX = originX + 4;
62
+ const boxY = originY + 4;
63
+ const boxW = 34;
64
+ const boxH = 34;
65
+ const glyphW = 3;
66
+ const glyphH = 5;
67
+ const totalW = text.length * glyphW + (text.length - 1);
68
+ const startX = boxX + Math.floor((boxW - totalW) / 2);
69
+ const startY = boxY + Math.floor((boxH - glyphH) / 2);
70
+ for (let dy = 0; dy < boxH; dy++) {
71
+ for (let dx = 0; dx < boxW; dx++) {
72
+ const px = boxX + dx;
73
+ const py = boxY + dy;
74
+ if (px < 0 || px >= cw || py < 0 || py >= ch)
75
+ continue;
76
+ // 角落切角(近似圆角胶囊)
77
+ const corner = Math.min(dx, dy, boxW - 1 - dx, boxH - 1 - dy);
78
+ if (corner < 3 && (dx < 3 || dx > boxW - 4) && (dy < 3 || dy > boxH - 4))
79
+ continue;
80
+ const gx = dx - (startX - boxX);
81
+ const gy = dy - (startY - boxY);
82
+ const cell = gx % (glyphW + 1);
83
+ const charIdx = Math.floor(gx / (glyphW + 1));
84
+ const glyph = charIdx >= 0 && charIdx < text.length ? GLYPHS[text[charIdx]] : undefined;
85
+ const glyphOn = glyph !== undefined && gy >= 0 && gy < glyphH && cell < glyphW && glyph[gy][cell] === '1';
86
+ const i = (py * cw + px) * 3;
87
+ if (glyphOn)
88
+ continue; // 字型位保持原色
89
+ const r = data[i];
90
+ const g = data[i + 1];
91
+ const b = data[i + 2];
92
+ data[i] = 255 - r;
93
+ data[i + 1] = 255 - g;
94
+ data[i + 2] = 255 - b;
95
+ }
96
+ }
97
+ }
98
+ //# sourceMappingURL=markers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"markers.js","sourceRoot":"","sources":["../../src/stage4/markers.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,61 @@
1
+ /**
2
+ * 契约 1/2/4 —— 视窗归一化(显示坐标系)。
3
+ *
4
+ * 规则(《四、采样逻辑…》§3 与契约 4 四步归一化):
5
+ * - roi_hint 或运动质心 → 1:1 正方形视窗(边长 = min(源短边, 包络目标区) 向下取整到 42 倍数,硬下限 84),
6
+ * 缩放后 100% 填满槽位,零灰边;
7
+ * - 全局/ full_frame → 等比内接 + 内容宽高量化到 42 倍数,居中灰补边(偏置对齐 42 网格);
8
+ * - 短边 ≥84 为硬约束(面积预算为软约束);
9
+ * - 源域外扩优先;外扩受物理边界限制仍不足 → 内容级中性灰补边并置 pad_overflow。
10
+ */
11
+ import type { RoiHint } from '../types.js';
12
+ import { type RgbImage } from '../utils/image.js';
13
+ /** 超像素模数。 */
14
+ export declare const SUPERPIXEL = 42;
15
+ /** 有效短边硬下限(2 个超像素步长)。 */
16
+ export declare const MIN_SHORT_SIDE = 84;
17
+ export type SlotKind = 'P' | 'F';
18
+ export interface ViewportPlan {
19
+ /** 源视频显示坐标系采样区域 [x, y, w, h](w/h 亦为 42 倍数)。 */
20
+ sourceCropBbox: [number, number, number, number];
21
+ /** 目标内容宽高(42 倍数;RoI 路径等于槽位边长)。 */
22
+ targetW: number;
23
+ targetH: number;
24
+ /** 画布槽位内内容偏移(42 倍数)。 */
25
+ offsetX: number;
26
+ offsetY: number;
27
+ /** 源域不足时内容级补边像素数(上/下/左/右)。 */
28
+ pad: {
29
+ top: number;
30
+ bottom: number;
31
+ left: number;
32
+ right: number;
33
+ };
34
+ /** 内容区出现源域补边时为 true。 */
35
+ padOverflow: boolean;
36
+ }
37
+ export declare function quant42Floor(v: number): number;
38
+ export declare function quant42Round(v: number): number;
39
+ export declare function slotSize(slot: SlotKind): number;
40
+ export interface ViewportInput {
41
+ displayWidth: number;
42
+ displayHeight: number;
43
+ roi?: RoiHint | undefined;
44
+ fullFrame: boolean;
45
+ slot: SlotKind;
46
+ }
47
+ /**
48
+ * 生成视窗计划。调用方保证 displayWidth/Height > 0。
49
+ */
50
+ export declare function planViewport(input: ViewportInput): ViewportPlan;
51
+ /** 规格化 roi 到显示像素矩形并钳制到 [0,1]。 */
52
+ export declare function roiToPixels(roi: RoiHint, displayWidth: number, displayHeight: number): {
53
+ x: number;
54
+ y: number;
55
+ w: number;
56
+ h: number;
57
+ };
58
+ /**
59
+ * 按视窗计划把源帧变换为槽位内容图(槽位尺寸,含内容级补边与居中灰)。
60
+ */
61
+ export declare function applyViewport(src: RgbImage, plan: ViewportPlan, slot: SlotKind): RgbImage;
@@ -0,0 +1,151 @@
1
+ /**
2
+ * 契约 1/2/4 —— 视窗归一化(显示坐标系)。
3
+ *
4
+ * 规则(《四、采样逻辑…》§3 与契约 4 四步归一化):
5
+ * - roi_hint 或运动质心 → 1:1 正方形视窗(边长 = min(源短边, 包络目标区) 向下取整到 42 倍数,硬下限 84),
6
+ * 缩放后 100% 填满槽位,零灰边;
7
+ * - 全局/ full_frame → 等比内接 + 内容宽高量化到 42 倍数,居中灰补边(偏置对齐 42 网格);
8
+ * - 短边 ≥84 为硬约束(面积预算为软约束);
9
+ * - 源域外扩优先;外扩受物理边界限制仍不足 → 内容级中性灰补边并置 pad_overflow。
10
+ */
11
+ import { PAD_GRAY, bilinearResize, cropWithGrayFallback } from '../utils/image.js';
12
+ /** 超像素模数。 */
13
+ export const SUPERPIXEL = 42;
14
+ /** 有效短边硬下限(2 个超像素步长)。 */
15
+ export const MIN_SHORT_SIDE = 84;
16
+ export function quant42Floor(v) {
17
+ return Math.floor(v / SUPERPIXEL) * SUPERPIXEL;
18
+ }
19
+ export function quant42Round(v) {
20
+ return Math.floor(v / SUPERPIXEL + 0.5) * SUPERPIXEL;
21
+ }
22
+ export function slotSize(slot) {
23
+ return slot === 'P' ? 336 : 672;
24
+ }
25
+ /**
26
+ * 生成视窗计划。调用方保证 displayWidth/Height > 0。
27
+ */
28
+ export function planViewport(input) {
29
+ const slot = slotSize(input.slot);
30
+ const shortSide = Math.min(input.displayWidth, input.displayHeight);
31
+ if (!input.fullFrame && input.roi) {
32
+ return planSquareViewport(input, slot, shortSide);
33
+ }
34
+ return planInscribeViewport(input, slot);
35
+ }
36
+ function clamp01(v) {
37
+ return Math.min(Math.max(v, 0), 1);
38
+ }
39
+ /** 规格化 roi 到显示像素矩形并钳制到 [0,1]。 */
40
+ export function roiToPixels(roi, displayWidth, displayHeight) {
41
+ const x0 = clamp01(Math.min(roi.x, roi.x + roi.w));
42
+ const y0 = clamp01(Math.min(roi.y, roi.y + roi.h));
43
+ const x1 = clamp01(Math.max(roi.x, roi.x + roi.w));
44
+ const y1 = clamp01(Math.max(roi.y, roi.y + roi.h));
45
+ return {
46
+ x: Math.round(x0 * displayWidth),
47
+ y: Math.round(y0 * displayHeight),
48
+ w: Math.max(1, Math.round((x1 - x0) * displayWidth)),
49
+ h: Math.max(1, Math.round((y1 - y0) * displayHeight)),
50
+ };
51
+ }
52
+ function planSquareViewport(input, slot, shortSide) {
53
+ const roi = roiToPixels(input.roi, input.displayWidth, input.displayHeight);
54
+ const envelope = Math.max(roi.w, roi.h);
55
+ // 边长:min(源短边, 包络目标区),向下取整 42 倍数;硬下限 84(源域外扩优先)。
56
+ let side = quant42Floor(Math.min(shortSide, envelope));
57
+ let padOverflow = false;
58
+ let pad = { top: 0, bottom: 0, left: 0, right: 0 };
59
+ if (side < MIN_SHORT_SIDE) {
60
+ side = MIN_SHORT_SIDE;
61
+ if (side > quant42Floor(shortSide)) {
62
+ // 源短边物理上不足 84:内容级补边(源域外扩到物理边界仍不足)
63
+ padOverflow = true;
64
+ }
65
+ }
66
+ // 视窗中心 = RoI 中心,钳制到源内;起始坐标对齐 42 网格
67
+ const cx = roi.x + roi.w / 2;
68
+ const cy = roi.y + roi.h / 2;
69
+ let x = quant42Floor(Math.round(cx - side / 2));
70
+ let y = quant42Floor(Math.round(cy - side / 2));
71
+ x = Math.min(Math.max(x, 0), Math.max(0, input.displayWidth - side));
72
+ y = Math.min(Math.max(y, 0), Math.max(0, input.displayHeight - side));
73
+ x = quant42Floor(x);
74
+ y = quant42Floor(y);
75
+ // 越界侧记录内容级补边(源域不足的方向)
76
+ const overRight = x + side - input.displayWidth;
77
+ const overBottom = y + side - input.displayHeight;
78
+ if (overRight > 0) {
79
+ pad = { ...pad, right: overRight };
80
+ padOverflow = true;
81
+ }
82
+ if (overBottom > 0) {
83
+ pad = { ...pad, bottom: overBottom };
84
+ padOverflow = true;
85
+ }
86
+ return {
87
+ sourceCropBbox: [x, y, side, side],
88
+ targetW: slot,
89
+ targetH: slot,
90
+ offsetX: 0,
91
+ offsetY: 0,
92
+ pad,
93
+ padOverflow,
94
+ };
95
+ }
96
+ function planInscribeViewport(input, slot) {
97
+ const { displayWidth: w, displayHeight: h } = input;
98
+ const budget = slot * slot;
99
+ const k = Math.min(slot / w, slot / h, Math.sqrt(budget / (w * h)));
100
+ let targetW = Math.min(quant42Round(w * k), slot);
101
+ let targetH = Math.min(quant42Round(h * k), slot);
102
+ // 短边硬约束(软面积预算让位)
103
+ if (targetW < MIN_SHORT_SIDE)
104
+ targetW = Math.min(MIN_SHORT_SIDE, quant42Floor(slot));
105
+ if (targetH < MIN_SHORT_SIDE)
106
+ targetH = Math.min(MIN_SHORT_SIDE, quant42Floor(slot));
107
+ // 内接居中,偏置对齐 42 网格
108
+ const offsetX = quant42Floor((slot - targetW) / 2);
109
+ const offsetY = quant42Floor((slot - targetH) / 2);
110
+ return {
111
+ sourceCropBbox: [0, 0, w, h],
112
+ targetW,
113
+ targetH,
114
+ offsetX,
115
+ offsetY,
116
+ pad: {
117
+ top: offsetY,
118
+ bottom: slot - targetH - offsetY,
119
+ left: offsetX,
120
+ right: slot - targetW - offsetX,
121
+ },
122
+ padOverflow: false,
123
+ };
124
+ }
125
+ /**
126
+ * 按视窗计划把源帧变换为槽位内容图(槽位尺寸,含内容级补边与居中灰)。
127
+ */
128
+ export function applyViewport(src, plan, slot) {
129
+ const slotSide = slotSize(slot);
130
+ const [cx, cy, cw, ch] = plan.sourceCropBbox;
131
+ const cropped = cropWithGrayFallback(src, cx, cy, cw, ch);
132
+ const scaled = bilinearResize(cropped, plan.targetW, plan.targetH);
133
+ if (plan.targetW === slotSide && plan.targetH === slotSide)
134
+ return scaled;
135
+ // 非满槽(全画幅内接):居中放置,四周中性灰
136
+ const canvas = { width: slotSide, height: slotSide, data: Buffer.alloc(slotSide * slotSide * 3, PAD_GRAY) };
137
+ for (let dy = 0; dy < plan.targetH; dy++) {
138
+ for (let dx = 0; dx < plan.targetW; dx++) {
139
+ const si = (dy * plan.targetW + dx) * 3;
140
+ const di = ((plan.offsetY + dy) * slotSide + plan.offsetX + dx) * 3;
141
+ const r = scaled.data[si];
142
+ const g = scaled.data[si + 1];
143
+ const b = scaled.data[si + 2];
144
+ canvas.data[di] = r;
145
+ canvas.data[di + 1] = g;
146
+ canvas.data[di + 2] = b;
147
+ }
148
+ }
149
+ return canvas;
150
+ }
151
+ //# sourceMappingURL=viewport.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"viewport.js","sourceRoot":"","sources":["../../src/stage4/viewport.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Stage 5(内容自闭环层)—— R1-R4 规则与 500ms PTS 节拍平滑(规格《五》§2)。
3
+ *
4
+ * 优先级 R3 > R1 > R2 > R4:R3 频闪抑制激活期间冻结 R1/R2 的新触发;
5
+ * R1/R2/R3 以 ΔPTS ≥ 500ms 为更新节拍,执行一阶平滑 θ_new = clamp(0.6·target + 0.4·curr);
6
+ * R4 在 SWITCH 触发时刻零延迟直写 T_settle_eff = 80ms,恢复走双路径
7
+ * (① 首个 MOTION;② STATIC 稳定满 T_settle_base),独立于节拍。
8
+ *
9
+ * 生效值写入 live(StreamBaselines 可变引用),streamSampler 每步直接读取;
10
+ * 基准值 base 保持不变,供遥测与恢复使用。
11
+ */
12
+ import { type StreamBaselines } from './intent.js';
13
+ import type { ControllerSnapshot, CtrlTickLogEntry } from '../types.js';
14
+ export declare const CTRL_TICK_MS = 500;
15
+ export declare const R3_FLIP_TRIGGER_FRAMES = 5;
16
+ export declare const R3_FLIP_RECOVER_FRAMES = 10;
17
+ export declare const R4_SETTLE_MS = 80;
18
+ export declare class StreamController {
19
+ /** 当前生效参数(原地变更,采样器直接读取)。 */
20
+ readonly live: StreamBaselines;
21
+ private readonly base;
22
+ private r1Active;
23
+ private r2Active;
24
+ private r3Active;
25
+ private r4Active;
26
+ private sTarget;
27
+ private tCoolTarget;
28
+ private hHighTarget;
29
+ private readonly ctrlId;
30
+ private readonly seq;
31
+ private lastTickPtsMs;
32
+ private readonly dHist;
33
+ private rFlipRun;
34
+ private rFlipCalmRun;
35
+ private r1CondSincePtsMs;
36
+ private r2CondSincePtsMs;
37
+ private lastState;
38
+ private stateSincePtsMs;
39
+ /** 每 500ms 节拍的生效值轨迹(遥测断言用)。 */
40
+ readonly tickLog: CtrlTickLogEntry[];
41
+ constructor(baselines: StreamBaselines, initialPtsMs?: number, snapshot?: ControllerSnapshot);
42
+ /** SWITCH 效果:R4 零延迟直写(规格《三》§3 原子步骤 5)。 */
43
+ onSwitch(ptsMs: number): void;
44
+ /**
45
+ * 每个步进帧调用:r_flip 连续计数、状态迁移(R4 恢复路径与 R1/R2 退场)、
46
+ * 500ms 节拍上的 R3→R1→R2 求值与一阶平滑。
47
+ */
48
+ onFrameStepped(ptsMs: number, d: number, rFlip: number, machineState: 'STATIC' | 'MOTION' | 'SETTLE' | 'SWITCH'): void;
49
+ /** R3 激活期间 α 锁定 0.1(规格 R3 调节动作)。 */
50
+ alphaFor(signalAlpha: number): number;
51
+ /** 当前激活规则的 ctrl_id 列表(发包回填 applied_ctrl_id)。 */
52
+ activeCtrlIds(): string[];
53
+ activeSlugs(): string[];
54
+ snapshot(): ControllerSnapshot;
55
+ private restore;
56
+ private isActive;
57
+ private deactivateR1;
58
+ private deactivateR2;
59
+ private recoverR4;
60
+ private nextId;
61
+ }