feat(effects): add Darkness action effect like freeze
Adds a fullscreen darkness vignette on click and a permanent shadow spot with a black radial gradient (40% edge, 100% center). Includes control panel UI, i18n, eraser support, and tests. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -34,6 +34,33 @@ void test('pickEraseTargetId: вода по штриху как туман', ()
|
||||
assert.equal(id, 'w1');
|
||||
});
|
||||
|
||||
void test('pickEraseTargetId: тьма как заморозка — точка удара', () => {
|
||||
const darkness: EffectInstance = {
|
||||
...base,
|
||||
id: 'd1',
|
||||
type: 'darkness',
|
||||
at: { x: 0.4, y: 0.55 },
|
||||
intensity: 1,
|
||||
lifetimeMs: 820,
|
||||
};
|
||||
const id = pickEraseTargetId([darkness], { x: 0.41, y: 0.55 }, 0.05);
|
||||
assert.equal(id, 'd1');
|
||||
});
|
||||
|
||||
void test('pickEraseTargetId: shadow с учётом inst.radiusN', () => {
|
||||
const shadow: EffectInstance = {
|
||||
...base,
|
||||
id: 's1',
|
||||
type: 'shadow',
|
||||
at: { x: 0.5, y: 0.5 },
|
||||
radiusN: 0.08,
|
||||
opacity: 1,
|
||||
lifetimeMs: null,
|
||||
};
|
||||
const id = pickEraseTargetId([shadow], { x: 0.59, y: 0.5 }, 0.02);
|
||||
assert.equal(id, 's1');
|
||||
});
|
||||
|
||||
void test('minDistSqEffectToPoint: молния — расстояние до отрезка', () => {
|
||||
const bolt: EffectInstance = {
|
||||
...base,
|
||||
|
||||
@@ -43,13 +43,15 @@ export function minDistSqEffectToPoint(inst: EffectInstance, p: { x: number; y:
|
||||
case 'lightning':
|
||||
case 'sunbeam':
|
||||
return distSqPointToSegment(p.x, p.y, inst.start.x, inst.start.y, inst.end.x, inst.end.y);
|
||||
case 'freeze': {
|
||||
case 'freeze':
|
||||
case 'darkness': {
|
||||
const dx = inst.at.x - p.x;
|
||||
const dy = inst.at.y - p.y;
|
||||
return dx * dx + dy * dy;
|
||||
}
|
||||
case 'scorch':
|
||||
case 'ice':
|
||||
case 'shadow':
|
||||
case 'poisonCloud': {
|
||||
const dx = inst.at.x - p.x;
|
||||
const dy = inst.at.y - p.y;
|
||||
@@ -61,7 +63,7 @@ export function minDistSqEffectToPoint(inst: EffectInstance, p: { x: number; y:
|
||||
}
|
||||
|
||||
function eraseHitThresholdSq(inst: EffectInstance, toolRadiusN: number): number {
|
||||
if (inst.type === 'scorch' || inst.type === 'ice' || inst.type === 'poisonCloud') {
|
||||
if (inst.type === 'scorch' || inst.type === 'ice' || inst.type === 'shadow' || inst.type === 'poisonCloud') {
|
||||
const r = toolRadiusN + inst.radiusN;
|
||||
return r * r;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ export type EffectToolType =
|
||||
| 'fire'
|
||||
| 'rain'
|
||||
| 'water'
|
||||
| 'darkness'
|
||||
| 'lightning'
|
||||
| 'sunbeam'
|
||||
| 'poisonCloud'
|
||||
@@ -14,12 +15,14 @@ export type EffectInstanceType =
|
||||
| 'fire'
|
||||
| 'rain'
|
||||
| 'water'
|
||||
| 'darkness'
|
||||
| 'lightning'
|
||||
| 'sunbeam'
|
||||
| 'poisonCloud'
|
||||
| 'freeze'
|
||||
| 'scorch'
|
||||
| 'ice';
|
||||
| 'ice'
|
||||
| 'shadow';
|
||||
|
||||
/** Нормализованные координаты (0..1) относительно области предпросмотра/презентации. */
|
||||
export type NPoint = { x: number; y: number; tMs: number; pressure?: number };
|
||||
@@ -65,6 +68,13 @@ export type WaterInstance = EffectInstanceBase & {
|
||||
lifetimeMs: number | null;
|
||||
};
|
||||
|
||||
export type DarknessInstance = EffectInstanceBase & {
|
||||
type: 'darkness';
|
||||
at: { x: number; y: number };
|
||||
intensity: number;
|
||||
lifetimeMs: number;
|
||||
};
|
||||
|
||||
export type LightningInstance = EffectInstanceBase & {
|
||||
type: 'lightning';
|
||||
start: { x: number; y: number };
|
||||
@@ -119,17 +129,28 @@ export type IceInstance = EffectInstanceBase & {
|
||||
lifetimeMs: number | null;
|
||||
};
|
||||
|
||||
export type ShadowInstance = EffectInstanceBase & {
|
||||
/** Внутренний инстанс: тёмное пятно после эффекта «Тьма». */
|
||||
type: 'shadow';
|
||||
at: { x: number; y: number };
|
||||
radiusN: number;
|
||||
opacity: number;
|
||||
lifetimeMs: number | null;
|
||||
};
|
||||
|
||||
export type EffectInstance =
|
||||
| FogInstance
|
||||
| FireInstance
|
||||
| RainInstance
|
||||
| WaterInstance
|
||||
| DarknessInstance
|
||||
| LightningInstance
|
||||
| SunbeamInstance
|
||||
| PoisonCloudInstance
|
||||
| FreezeInstance
|
||||
| ScorchInstance
|
||||
| IceInstance;
|
||||
| IceInstance
|
||||
| ShadowInstance;
|
||||
|
||||
export type EffectToolState = {
|
||||
tool: EffectToolType;
|
||||
|
||||
Reference in New Issue
Block a user