feat(effects): add Closing brush as inverse of Opening brush
Allow covering revealed darkness again during darkened scene sessions. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -197,6 +197,7 @@ export function ControlApp() {
|
||||
| 'explosion'
|
||||
| 'freeze'
|
||||
| 'exploreBrush'
|
||||
| 'closeBrush'
|
||||
| 'eraser';
|
||||
startN?: { x: number; y: number };
|
||||
points?: { x: number; y: number; tMs: number }[];
|
||||
@@ -1055,10 +1056,17 @@ export function ControlApp() {
|
||||
draftPaintRafRef.current = 0;
|
||||
pushDraftToPixi();
|
||||
const b = brushRef.current;
|
||||
if (b?.tool === 'exploreBrush' && b.points) {
|
||||
if (
|
||||
(b?.tool === 'exploreBrush' || b?.tool === 'closeBrush') &&
|
||||
b.points
|
||||
) {
|
||||
void sd.dispatch({
|
||||
kind: 'draft.set',
|
||||
draft: { points: b.points, radiusN: toolRef.current.radiusN },
|
||||
draft: {
|
||||
points: b.points,
|
||||
radiusN: toolRef.current.radiusN,
|
||||
mode: b.tool === 'closeBrush' ? 'cover' : 'reveal',
|
||||
},
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -1207,7 +1215,11 @@ export function ControlApp() {
|
||||
},
|
||||
});
|
||||
}
|
||||
if (b.tool === 'exploreBrush' && b.points && b.points.length > 0) {
|
||||
if (
|
||||
(b.tool === 'exploreBrush' || b.tool === 'closeBrush') &&
|
||||
b.points &&
|
||||
b.points.length > 0
|
||||
) {
|
||||
await sd.dispatch({
|
||||
kind: 'stroke.add',
|
||||
stroke: {
|
||||
@@ -1216,6 +1228,7 @@ export function ControlApp() {
|
||||
createdAtMs,
|
||||
points: b.points,
|
||||
radiusN: tool.radiusN,
|
||||
mode: b.tool === 'closeBrush' ? 'cover' : 'reveal',
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -1619,6 +1632,15 @@ export function ControlApp() {
|
||||
>
|
||||
<span className={styles.iconGlyph}>🔦</span>
|
||||
</Button>
|
||||
<Button
|
||||
variant={tool.tool === 'closeBrush' ? 'primary' : 'ghost'}
|
||||
iconOnly
|
||||
title={t('control.closerBrush')}
|
||||
ariaLabel={t('control.closerBrush')}
|
||||
onClick={() => selectEffectTool('closeBrush')}
|
||||
>
|
||||
<span className={styles.iconGlyph}>⬛</span>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
@@ -1850,12 +1872,13 @@ export function ControlApp() {
|
||||
if (tool.tool === 'water' || tool.tool === 'rain') {
|
||||
extinguishFireAlong([startPt]);
|
||||
}
|
||||
if (tool.tool === 'exploreBrush') {
|
||||
if (tool.tool === 'exploreBrush' || tool.tool === 'closeBrush') {
|
||||
void sd.dispatch({
|
||||
kind: 'draft.set',
|
||||
draft: {
|
||||
points: [startPt],
|
||||
radiusN: tool.radiusN,
|
||||
mode: tool.tool === 'closeBrush' ? 'cover' : 'reveal',
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -1932,7 +1955,10 @@ export function ControlApp() {
|
||||
scenePanRef.current = null;
|
||||
return;
|
||||
}
|
||||
if (brushRef.current?.tool === 'exploreBrush') {
|
||||
if (
|
||||
brushRef.current?.tool === 'exploreBrush' ||
|
||||
brushRef.current?.tool === 'closeBrush'
|
||||
) {
|
||||
void sd.dispatch({ kind: 'draft.set', draft: null });
|
||||
}
|
||||
brushRef.current = null;
|
||||
|
||||
@@ -87,6 +87,9 @@ void test('ControlApp: эффекты в пульте, иконки с тулт
|
||||
assert.ok(src.includes("t('control.actionEffects')"));
|
||||
assert.ok(src.includes("t('control.darknessControl')"));
|
||||
assert.ok(src.includes("t('control.explorerBrush')"));
|
||||
assert.ok(src.includes("t('control.closerBrush')"));
|
||||
assert.ok(src.includes("tool: 'closeBrush'") || src.includes("selectEffectTool('closeBrush')"));
|
||||
assert.ok(src.includes("mode: b.tool === 'closeBrush' ? 'cover' : 'reveal'") || src.includes("'cover'"));
|
||||
assert.ok(src.includes('SceneDarknessOverlay'));
|
||||
assert.ok(src.includes('useSceneDarknessState'));
|
||||
assert.ok(src.includes("t('control.sunbeam')"));
|
||||
|
||||
Reference in New Issue
Block a user