支持布尔操作按帧范围执行
- 区域合并和重叠区域去除新增“按帧范围选择”,复用底部时间轴范围选择并在执行前二次确认 - 布尔操作范围只处理所选帧内存在对应传播链的区域,范围外传播 mask 保持不变 - 自动传播范围选择时在顶栏显示传播权重,以及相对参考帧的向前/向后传播帧数 - Canvas 将传播链布尔操作委托给工作区统一处理范围选择,同时保留当前帧/所有传播帧快捷操作 - 增加 CanvasArea、VideoWorkspace 回归测试,覆盖布尔操作范围选择、范围执行和自动传播方向摘要 - 更新 AGENTS 与前端审计、需求冻结、设计冻结、测试计划文档
This commit is contained in:
@@ -993,6 +993,68 @@ describe('CanvasArea', () => {
|
||||
}));
|
||||
});
|
||||
|
||||
it('can hand propagated boolean operations to the workspace frame range selector', () => {
|
||||
const onRequestBooleanFrameRange = vi.fn();
|
||||
useStore.setState({
|
||||
masks: [
|
||||
{
|
||||
id: 'annotation-1',
|
||||
annotationId: '1',
|
||||
frameId: 'frame-1',
|
||||
pathData: 'M 10 10 L 60 10 L 60 60 L 10 60 Z',
|
||||
label: 'A',
|
||||
color: '#06b6d4',
|
||||
segmentation: [[10, 10, 60, 10, 60, 60, 10, 60]],
|
||||
},
|
||||
{
|
||||
id: 'annotation-2',
|
||||
annotationId: '2',
|
||||
frameId: 'frame-1',
|
||||
pathData: 'M 50 50 L 100 50 L 100 100 L 50 100 Z',
|
||||
label: 'A',
|
||||
color: '#06b6d4',
|
||||
segmentation: [[50, 50, 100, 50, 100, 100, 50, 100]],
|
||||
},
|
||||
{
|
||||
id: 'annotation-10',
|
||||
annotationId: '10',
|
||||
frameId: 'frame-2',
|
||||
pathData: 'M 12 12 L 62 12 L 62 62 L 12 62 Z',
|
||||
label: 'A',
|
||||
color: '#06b6d4',
|
||||
segmentation: [[12, 12, 62, 12, 62, 62, 12, 62]],
|
||||
metadata: { source: 'sam2.1_hiera_tiny_propagation', source_annotation_id: 1, propagation_seed_key: 'annotation:1' },
|
||||
},
|
||||
{
|
||||
id: 'annotation-20',
|
||||
annotationId: '20',
|
||||
frameId: 'frame-2',
|
||||
pathData: 'M 52 52 L 102 52 L 102 102 L 52 102 Z',
|
||||
label: 'A',
|
||||
color: '#06b6d4',
|
||||
segmentation: [[52, 52, 102, 52, 102, 102, 52, 102]],
|
||||
metadata: { source: 'sam2.1_hiera_tiny_propagation', source_annotation_id: 2, propagation_seed_key: 'annotation:2' },
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
render(<CanvasArea activeTool="area_merge" frame={frame} onRequestBooleanFrameRange={onRequestBooleanFrameRange} />);
|
||||
const paths = screen.getAllByTestId('konva-path');
|
||||
fireEvent.click(paths[0]);
|
||||
fireEvent.click(paths[1]);
|
||||
fireEvent.click(screen.getByRole('button', { name: '合并选中' }));
|
||||
fireEvent.click(screen.getByRole('button', { name: '按帧范围选择' }));
|
||||
|
||||
expect(onRequestBooleanFrameRange).toHaveBeenCalledWith(expect.objectContaining({
|
||||
operation: 'area_merge',
|
||||
currentFrameId: 'frame-1',
|
||||
candidateFrameIds: expect.arrayContaining(['frame-1', 'frame-2']),
|
||||
selectedMaskIds: ['annotation-1', 'annotation-2'],
|
||||
execute: expect.any(Function),
|
||||
}));
|
||||
expect(useStore.getState().masks.map((mask) => mask.id).sort()).toEqual(['annotation-1', 'annotation-10', 'annotation-2', 'annotation-20']);
|
||||
});
|
||||
|
||||
it('removes overlap from the primary selected mask with polygon difference', () => {
|
||||
useStore.setState({
|
||||
masks: [
|
||||
|
||||
Reference in New Issue
Block a user