更新换行算法和APP-PUSH
This commit is contained in:
78
client/src/features/textWrap/breakpoints/tcCandidates.ts
Normal file
78
client/src/features/textWrap/breakpoints/tcCandidates.ts
Normal file
@@ -0,0 +1,78 @@
|
||||
import type { Breakpoint } from './types';
|
||||
import type { Token } from '../core/types';
|
||||
|
||||
type TcCandidateConfig = {
|
||||
tcPunctuations: string[];
|
||||
balanceRange: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* 计算 BALANCE 的 idealPos 列表(确定性简化版)。
|
||||
*
|
||||
* - N=tokens.length
|
||||
* - targetLines=min(maxLines,N)
|
||||
* - lineIndex in 1..targetLines-1:
|
||||
* idealPos=round(N*lineIndex/targetLines)
|
||||
*/
|
||||
export function computeTcIdealPositions(tokens: Token[], maxLines: number): number[] {
|
||||
const n = tokens.length;
|
||||
const targetLines = Math.max(1, Math.min(maxLines | 0, n));
|
||||
const ideals: number[] = [];
|
||||
|
||||
for (let lineIndex = 1; lineIndex <= targetLines - 1; lineIndex++) {
|
||||
const idealPos = Math.round((n * lineIndex) / targetLines);
|
||||
ideals.push(idealPos);
|
||||
}
|
||||
|
||||
// 去重并排序(稳定)
|
||||
ideals.sort((a, b) => a - b);
|
||||
return ideals.filter((v, idx) => idx === 0 || v !== ideals[idx - 1]);
|
||||
}
|
||||
|
||||
/**
|
||||
* TC 候选断点生成:
|
||||
* - 标点后:kind=PUNCT,priority=30
|
||||
* - 空格后:kind=SPACE,priority=20
|
||||
* - BALANCE:kind=BALANCE,priority=5(围绕 idealPos ± balanceRange)
|
||||
*
|
||||
* 注意:BALANCE 断点允许生成在短语 span 内,是否可用交给评分阶段强惩罚淘汰。
|
||||
*/
|
||||
export function generateTcCandidates(tokens: Token[], maxLines: number, config: TcCandidateConfig): { candidates: Breakpoint[]; idealPositions: number[] } {
|
||||
const n = tokens.length;
|
||||
if (n <= 1) return { candidates: [], idealPositions: [] };
|
||||
|
||||
const punctSet = new Set(config.tcPunctuations);
|
||||
const candidates: Breakpoint[] = [];
|
||||
|
||||
// PUNCT / SPACE(扫描 token)
|
||||
for (let i = 0; i < n; i++) {
|
||||
const t = tokens[i]?.text ?? '';
|
||||
const pos = i + 1;
|
||||
|
||||
// 只允许行内断点
|
||||
if (pos < 1 || pos > n - 1) continue;
|
||||
|
||||
if (punctSet.has(t)) {
|
||||
candidates.push({ pos, kind: 'PUNCT', priority: 30 });
|
||||
}
|
||||
|
||||
if (t === ' ') {
|
||||
candidates.push({ pos, kind: 'SPACE', priority: 20 });
|
||||
}
|
||||
}
|
||||
|
||||
// BALANCE
|
||||
const idealPositions = computeTcIdealPositions(tokens, maxLines);
|
||||
const range = Math.max(0, config.balanceRange | 0);
|
||||
if (range > 0 && idealPositions.length > 0) {
|
||||
for (const ideal of idealPositions) {
|
||||
for (let pos = ideal - range; pos <= ideal + range; pos++) {
|
||||
if (pos < 1 || pos > n - 1) continue;
|
||||
candidates.push({ pos, kind: 'BALANCE', priority: 5 });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return { candidates, idealPositions };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user