summaryrefslogtreecommitdiff
path: root/apps/web/src/lib/commands/timeline/element/masks
diff options
context:
space:
mode:
authorMaze Winther <mazewinther@gmail.com>2026-04-20 11:31:17 +0200
committerMaze Winther <mazewinther@gmail.com>2026-04-20 11:31:17 +0200
commit3e89d2998588f8e35c524e523ba2ee853f0f6475 (patch)
tree57221f96c1259b3b07e3ddf8c1adefc502b9d8eb /apps/web/src/lib/commands/timeline/element/masks
parent729d10592f4e802f91c555c309ff3ba4b119f843 (diff)
downloadopencut-3e89d2998588f8e35c524e523ba2ee853f0f6475.tar.gz
refactor: restructure files to their domains, new preview overlay system, and dep graph
Diffstat (limited to 'apps/web/src/lib/commands/timeline/element/masks')
-rw-r--r--apps/web/src/lib/commands/timeline/element/masks/delete-custom-mask-points.ts131
-rw-r--r--apps/web/src/lib/commands/timeline/element/masks/index.ts4
-rw-r--r--apps/web/src/lib/commands/timeline/element/masks/insert-custom-mask-point.ts169
-rw-r--r--apps/web/src/lib/commands/timeline/element/masks/remove-mask.ts65
-rw-r--r--apps/web/src/lib/commands/timeline/element/masks/toggle-mask-inverted.ts76
5 files changed, 0 insertions, 445 deletions
diff --git a/apps/web/src/lib/commands/timeline/element/masks/delete-custom-mask-points.ts b/apps/web/src/lib/commands/timeline/element/masks/delete-custom-mask-points.ts
deleted file mode 100644
index 346eb8d9..00000000
--- a/apps/web/src/lib/commands/timeline/element/masks/delete-custom-mask-points.ts
+++ /dev/null
@@ -1,131 +0,0 @@
-import { EditorCore } from "@/core";
-import { Command, type CommandResult } from "@/lib/commands/base-command";
-import {
- getCustomMaskClosedStateAfterPointRemoval,
- removeCustomMaskPoints,
-} from "@/lib/masks/custom-path";
-import type { CustomMask } from "@/lib/masks/types";
-import { isMaskableElement, updateElementInSceneTracks } from "@/lib/timeline";
-import type { MaskableElement, SceneTracks } from "@/lib/timeline";
-
-function deletePointsFromCustomMask({
- mask,
- pointIds,
-}: {
- mask: CustomMask;
- pointIds: string[];
-}): CustomMask {
- const points = mask.params.path;
- const nextPoints = removeCustomMaskPoints({ points, pointIds });
- if (nextPoints.length === points.length) {
- return mask;
- }
-
- return {
- ...mask,
- params: {
- ...mask.params,
- path: nextPoints,
- closed: getCustomMaskClosedStateAfterPointRemoval({
- wasClosed: mask.params.closed,
- remainingPointCount: nextPoints.length,
- }),
- },
- };
-}
-
-function deletePointsFromElementMask({
- element,
- maskId,
- pointIds,
-}: {
- element: MaskableElement;
- maskId: string;
- pointIds: string[];
-}): { element: MaskableElement; didDeletePoints: boolean } {
- const currentMasks = element.masks ?? [];
- let didDeletePoints = false;
- const nextMasks = currentMasks.map((mask) => {
- if (mask.id !== maskId || mask.type !== "custom") {
- return mask;
- }
-
- const nextMask = deletePointsFromCustomMask({
- mask,
- pointIds,
- });
- didDeletePoints ||= nextMask !== mask;
- return nextMask;
- });
-
- return {
- element: didDeletePoints ? { ...element, masks: nextMasks } : element,
- didDeletePoints,
- };
-}
-
-export class DeleteCustomMaskPointsCommand extends Command {
- private savedState: SceneTracks | null = null;
- private readonly trackId: string;
- private readonly elementId: string;
- private readonly maskId: string;
- private readonly pointIds: string[];
-
- constructor({
- trackId,
- elementId,
- maskId,
- pointIds,
- }: {
- trackId: string;
- elementId: string;
- maskId: string;
- pointIds: string[];
- }) {
- super();
- this.trackId = trackId;
- this.elementId = elementId;
- this.maskId = maskId;
- this.pointIds = pointIds;
- }
-
- execute(): CommandResult | undefined {
- const editor = EditorCore.getInstance();
- this.savedState = editor.scenes.getActiveScene().tracks;
-
- let didDeletePoints = false;
- const updatedTracks = updateElementInSceneTracks({
- tracks: this.savedState,
- trackId: this.trackId,
- elementId: this.elementId,
- elementPredicate: isMaskableElement,
- update: (element) => {
- const result = deletePointsFromElementMask({
- element: element as MaskableElement,
- maskId: this.maskId,
- pointIds: this.pointIds,
- });
- didDeletePoints ||= result.didDeletePoints;
- return result.element;
- },
- });
-
- if (didDeletePoints) {
- editor.timeline.updateTracks(updatedTracks);
- return {
- selection: {
- selectedMaskPoints: null,
- },
- };
- }
-
- return undefined;
- }
-
- undo(): void {
- if (this.savedState) {
- const editor = EditorCore.getInstance();
- editor.timeline.updateTracks(this.savedState);
- }
- }
-}
diff --git a/apps/web/src/lib/commands/timeline/element/masks/index.ts b/apps/web/src/lib/commands/timeline/element/masks/index.ts
deleted file mode 100644
index dd6373b1..00000000
--- a/apps/web/src/lib/commands/timeline/element/masks/index.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-export { DeleteCustomMaskPointsCommand } from "./delete-custom-mask-points";
-export { InsertCustomMaskPointCommand } from "./insert-custom-mask-point";
-export { RemoveMaskCommand } from "./remove-mask";
-export { ToggleMaskInvertedCommand } from "./toggle-mask-inverted";
diff --git a/apps/web/src/lib/commands/timeline/element/masks/insert-custom-mask-point.ts b/apps/web/src/lib/commands/timeline/element/masks/insert-custom-mask-point.ts
deleted file mode 100644
index 9f837836..00000000
--- a/apps/web/src/lib/commands/timeline/element/masks/insert-custom-mask-point.ts
+++ /dev/null
@@ -1,169 +0,0 @@
-import { EditorCore } from "@/core";
-import { Command, type CommandResult } from "@/lib/commands/base-command";
-import { insertPointOnCustomMaskSegment } from "@/lib/masks/definitions/custom";
-import type { ElementBounds } from "@/lib/preview/element-bounds";
-import type { CustomMask } from "@/lib/masks/types";
-import { isMaskableElement, updateElementInSceneTracks } from "@/lib/timeline";
-import type { MaskableElement, SceneTracks } from "@/lib/timeline";
-
-function insertPointIntoCustomMask({
- mask,
- segmentIndex,
- canvasPoint,
- bounds,
-}: {
- mask: CustomMask;
- segmentIndex: number;
- canvasPoint: { x: number; y: number };
- bounds: ElementBounds;
-}): { mask: CustomMask; insertedPointId: string | null } {
- const result = insertPointOnCustomMaskSegment({
- params: mask.params,
- segmentIndex,
- canvasPoint,
- bounds,
- });
- if (!result) {
- return {
- mask,
- insertedPointId: null,
- };
- }
-
- return {
- mask: {
- ...mask,
- params: result.params,
- },
- insertedPointId: result.pointId,
- };
-}
-
-function insertPointIntoElementMask({
- element,
- maskId,
- segmentIndex,
- canvasPoint,
- bounds,
-}: {
- element: MaskableElement;
- maskId: string;
- segmentIndex: number;
- canvasPoint: { x: number; y: number };
- bounds: ElementBounds;
-}): {
- element: MaskableElement;
- didInsertPoint: boolean;
- insertedPointId: string | null;
-} {
- const currentMasks = element.masks ?? [];
- let insertedPointId: string | null = null;
- let didInsertPoint = false;
-
- const nextMasks = currentMasks.map((mask) => {
- if (mask.id !== maskId || mask.type !== "custom") {
- return mask;
- }
-
- const result = insertPointIntoCustomMask({
- mask,
- segmentIndex,
- canvasPoint,
- bounds,
- });
- if (result.insertedPointId) {
- insertedPointId = result.insertedPointId;
- didInsertPoint = true;
- }
- return result.mask;
- });
-
- return {
- element: didInsertPoint ? { ...element, masks: nextMasks } : element,
- didInsertPoint,
- insertedPointId,
- };
-}
-
-export class InsertCustomMaskPointCommand extends Command {
- private savedState: SceneTracks | null = null;
- private readonly trackId: string;
- private readonly elementId: string;
- private readonly maskId: string;
- private readonly segmentIndex: number;
- private readonly canvasPoint: { x: number; y: number };
- private readonly bounds: ElementBounds;
-
- constructor({
- trackId,
- elementId,
- maskId,
- segmentIndex,
- canvasPoint,
- bounds,
- }: {
- trackId: string;
- elementId: string;
- maskId: string;
- segmentIndex: number;
- canvasPoint: { x: number; y: number };
- bounds: ElementBounds;
- }) {
- super();
- this.trackId = trackId;
- this.elementId = elementId;
- this.maskId = maskId;
- this.segmentIndex = segmentIndex;
- this.canvasPoint = canvasPoint;
- this.bounds = bounds;
- }
-
- execute(): CommandResult | undefined {
- const editor = EditorCore.getInstance();
- this.savedState = editor.scenes.getActiveScene().tracks;
-
- let didInsertPoint = false;
- let insertedPointId: string | null = null;
- const updatedTracks = updateElementInSceneTracks({
- tracks: this.savedState,
- trackId: this.trackId,
- elementId: this.elementId,
- elementPredicate: isMaskableElement,
- update: (element) => {
- const result = insertPointIntoElementMask({
- element: element as MaskableElement,
- maskId: this.maskId,
- segmentIndex: this.segmentIndex,
- canvasPoint: this.canvasPoint,
- bounds: this.bounds,
- });
- didInsertPoint ||= result.didInsertPoint;
- insertedPointId ??= result.insertedPointId;
- return result.element;
- },
- });
-
- if (!didInsertPoint || !insertedPointId) {
- return undefined;
- }
-
- editor.timeline.updateTracks(updatedTracks);
- return {
- selection: {
- selectedMaskPoints: {
- trackId: this.trackId,
- elementId: this.elementId,
- maskId: this.maskId,
- pointIds: [insertedPointId],
- },
- },
- };
- }
-
- undo(): void {
- if (this.savedState) {
- const editor = EditorCore.getInstance();
- editor.timeline.updateTracks(this.savedState);
- }
- }
-}
diff --git a/apps/web/src/lib/commands/timeline/element/masks/remove-mask.ts b/apps/web/src/lib/commands/timeline/element/masks/remove-mask.ts
deleted file mode 100644
index 9f6c5f77..00000000
--- a/apps/web/src/lib/commands/timeline/element/masks/remove-mask.ts
+++ /dev/null
@@ -1,65 +0,0 @@
-import { EditorCore } from "@/core";
-import { Command, type CommandResult } from "@/lib/commands/base-command";
-import { isMaskableElement, updateElementInSceneTracks } from "@/lib/timeline";
-import type { SceneTracks, MaskableElement } from "@/lib/timeline";
-
-function removeMaskFromElement({
- element,
- maskId,
-}: {
- element: MaskableElement;
- maskId: string;
-}): MaskableElement {
- const currentMasks = element.masks ?? [];
- const filteredMasks = currentMasks.filter((mask) => mask.id !== maskId);
- return { ...element, masks: filteredMasks };
-}
-
-export class RemoveMaskCommand extends Command {
- private savedState: SceneTracks | null = null;
- private readonly trackId: string;
- private readonly elementId: string;
- private readonly maskId: string;
-
- constructor({
- trackId,
- elementId,
- maskId,
- }: {
- trackId: string;
- elementId: string;
- maskId: string;
- }) {
- super();
- this.trackId = trackId;
- this.elementId = elementId;
- this.maskId = maskId;
- }
-
- execute(): CommandResult | undefined {
- const editor = EditorCore.getInstance();
- this.savedState = editor.scenes.getActiveScene().tracks;
-
- const updatedTracks = updateElementInSceneTracks({
- tracks: this.savedState,
- trackId: this.trackId,
- elementId: this.elementId,
- elementPredicate: isMaskableElement,
- update: (element) =>
- removeMaskFromElement({
- element: element as MaskableElement,
- maskId: this.maskId,
- }),
- });
-
- editor.timeline.updateTracks(updatedTracks);
- return undefined;
- }
-
- undo(): void {
- if (this.savedState) {
- const editor = EditorCore.getInstance();
- editor.timeline.updateTracks(this.savedState);
- }
- }
-}
diff --git a/apps/web/src/lib/commands/timeline/element/masks/toggle-mask-inverted.ts b/apps/web/src/lib/commands/timeline/element/masks/toggle-mask-inverted.ts
deleted file mode 100644
index 2b5c1694..00000000
--- a/apps/web/src/lib/commands/timeline/element/masks/toggle-mask-inverted.ts
+++ /dev/null
@@ -1,76 +0,0 @@
-import { EditorCore } from "@/core";
-import { Command, type CommandResult } from "@/lib/commands/base-command";
-import { isMaskableElement, updateElementInSceneTracks } from "@/lib/timeline";
-import type { Mask } from "@/lib/masks/types";
-import type { SceneTracks, MaskableElement } from "@/lib/timeline";
-
-export function toggleMaskInvertedOnElement({
- element,
- maskId,
-}: {
- element: MaskableElement;
- maskId: string;
-}): MaskableElement {
- const currentMasks = element.masks ?? [];
- const toggleMask = <TMask extends Mask>(mask: TMask): TMask => ({
- ...mask,
- params: {
- ...mask.params,
- inverted: !mask.params.inverted,
- },
- });
- const updatedMasks = currentMasks.map((mask) =>
- mask.id !== maskId ? mask : toggleMask(mask),
- );
-
- return { ...element, masks: updatedMasks };
-}
-
-export class ToggleMaskInvertedCommand extends Command {
- private savedState: SceneTracks | null = null;
- private readonly trackId: string;
- private readonly elementId: string;
- private readonly maskId: string;
-
- constructor({
- trackId,
- elementId,
- maskId,
- }: {
- trackId: string;
- elementId: string;
- maskId: string;
- }) {
- super();
- this.trackId = trackId;
- this.elementId = elementId;
- this.maskId = maskId;
- }
-
- execute(): CommandResult | undefined {
- const editor = EditorCore.getInstance();
- this.savedState = editor.scenes.getActiveScene().tracks;
-
- const updatedTracks = updateElementInSceneTracks({
- tracks: this.savedState,
- trackId: this.trackId,
- elementId: this.elementId,
- elementPredicate: isMaskableElement,
- update: (element) =>
- toggleMaskInvertedOnElement({
- element: element as MaskableElement,
- maskId: this.maskId,
- }),
- });
-
- editor.timeline.updateTracks(updatedTracks);
- return undefined;
- }
-
- undo(): void {
- if (this.savedState) {
- const editor = EditorCore.getInstance();
- editor.timeline.updateTracks(this.savedState);
- }
- }
-}