diff options
| author | Maze Winther <mazewinther@gmail.com> | 2026-04-15 15:45:50 +0200 |
|---|---|---|
| committer | Maze Winther <mazewinther@gmail.com> | 2026-04-15 15:45:50 +0200 |
| commit | c46d28891fee62ca2d3132da876af6411a41d9da (patch) | |
| tree | f856f71d04c69628f26b904088d675dd298a5080 /apps/web/src/lib/masks/geometry.ts | |
| parent | 66e4367bc4727f1d0bcf8f869529c7518812277e (diff) | |
| download | opencut-c46d28891fee62ca2d3132da876af6411a41d9da.tar.gz | |
feat: text / custom masks, fix preview pan, refactor selection
Diffstat (limited to 'apps/web/src/lib/masks/geometry.ts')
| -rw-r--r-- | apps/web/src/lib/masks/geometry.ts | 233 |
1 files changed, 121 insertions, 112 deletions
diff --git a/apps/web/src/lib/masks/geometry.ts b/apps/web/src/lib/masks/geometry.ts index 0a631735..a5b05030 100644 --- a/apps/web/src/lib/masks/geometry.ts +++ b/apps/web/src/lib/masks/geometry.ts @@ -1,112 +1,121 @@ -import type { ElementBounds } from "@/lib/preview/element-bounds";
-import type { SnapLine } from "@/lib/preview/preview-snap";
-import { MIN_MASK_DIMENSION } from "@/lib/masks/dimensions";
-import type { ParamValues } from "@/lib/params";
-import type { RectangleMaskParams } from "@/lib/masks/types";
-
-export function hasCenterParams(params: ParamValues): params is ParamValues & {
- centerX: number;
- centerY: number;
-} {
- return (
- typeof params.centerX === "number" && typeof params.centerY === "number"
- );
-}
-
-export function isRectangleMaskParams(
- params: ParamValues,
-): params is RectangleMaskParams {
- return (
- hasCenterParams(params) &&
- typeof params.width === "number" &&
- typeof params.height === "number" &&
- typeof params.rotation === "number" &&
- typeof params.scale === "number"
- );
-}
-
-export function getMaskLocalCenter({
- params,
- bounds,
-}: {
- params: ParamValues;
- bounds: ElementBounds;
-}): { x: number; y: number } | null {
- if (!hasCenterParams(params)) return null;
-
- return {
- x: params.centerX * bounds.width,
- y: params.centerY * bounds.height,
- };
-}
-
-export function setMaskLocalCenter({
- center,
- bounds,
-}: {
- center: { x: number; y: number };
- bounds: ElementBounds;
-}): Pick<ParamValues, "centerX" | "centerY"> {
- return {
- centerX: bounds.width === 0 ? 0 : center.x / bounds.width,
- centerY: bounds.height === 0 ? 0 : center.y / bounds.height,
- };
-}
-
-export function getMaskSnapGeometry({
- params,
- bounds,
-}: {
- params: ParamValues;
- bounds: ElementBounds;
-}): {
- position: { x: number; y: number };
- size: { width: number; height: number };
- rotation: number;
-} | null {
- const position = getMaskLocalCenter({ params, bounds });
- if (!position) return null;
-
- if (isRectangleMaskParams(params)) {
- return {
- position,
- size: {
- width: Math.max(params.width, MIN_MASK_DIMENSION) * bounds.width,
- height: Math.max(params.height, MIN_MASK_DIMENSION) * bounds.height,
- },
- rotation: params.rotation,
- };
- }
-
- return {
- position,
- size: { width: 0, height: 0 },
- rotation: typeof params.rotation === "number" ? params.rotation : 0,
- };
-}
-
-export function toGlobalMaskSnapLines({
- lines,
- bounds,
- canvasSize,
-}: {
- lines: SnapLine[];
- bounds: ElementBounds;
- canvasSize: { width: number; height: number };
-}): SnapLine[] {
- const centerX = bounds.cx - canvasSize.width / 2;
- const centerY = bounds.cy - canvasSize.height / 2;
-
- return lines.map((line) =>
- line.type === "vertical"
- ? {
- type: "vertical" as const,
- position: centerX + line.position,
- }
- : {
- type: "horizontal" as const,
- position: centerY + line.position,
- },
- );
-}
-
+import type { ElementBounds } from "@/lib/preview/element-bounds"; +import type { SnapLine } from "@/lib/preview/preview-snap"; +import { MIN_MASK_DIMENSION } from "@/lib/masks/dimensions"; +import type { RectangleMaskParams } from "@/lib/masks/types"; + +type CenterMaskParams = { + centerX: number; + centerY: number; +}; + +type SnapGeometryParams = CenterMaskParams & { + rotation?: number; + width?: number; + height?: number; + scale?: number; +}; + +export function hasCenterParams( + params: Partial<CenterMaskParams>, +): params is CenterMaskParams { + return ( + typeof params.centerX === "number" && typeof params.centerY === "number" + ); +} + +export function isRectangleMaskParams( + params: SnapGeometryParams, +): params is RectangleMaskParams { + return ( + hasCenterParams(params) && + typeof params.width === "number" && + typeof params.height === "number" && + typeof params.rotation === "number" && + typeof params.scale === "number" + ); +} + +export function getMaskLocalCenter({ + params, + bounds, +}: { + params: CenterMaskParams; + bounds: ElementBounds; +}): { x: number; y: number } | null { + if (!hasCenterParams(params)) return null; + + return { + x: params.centerX * bounds.width, + y: params.centerY * bounds.height, + }; +} + +export function setMaskLocalCenter({ + center, + bounds, +}: { + center: { x: number; y: number }; + bounds: ElementBounds; +}): { centerX: number; centerY: number } { + return { + centerX: bounds.width === 0 ? 0 : center.x / bounds.width, + centerY: bounds.height === 0 ? 0 : center.y / bounds.height, + }; +} + +export function getMaskSnapGeometry({ + params, + bounds, +}: { + params: SnapGeometryParams; + bounds: ElementBounds; +}): { + position: { x: number; y: number }; + size: { width: number; height: number }; + rotation: number; +} | null { + const position = getMaskLocalCenter({ params, bounds }); + if (!position) return null; + + if (isRectangleMaskParams(params)) { + return { + position, + size: { + width: Math.max(params.width, MIN_MASK_DIMENSION) * bounds.width, + height: Math.max(params.height, MIN_MASK_DIMENSION) * bounds.height, + }, + rotation: params.rotation, + }; + } + + return { + position, + size: { width: 0, height: 0 }, + rotation: typeof params.rotation === "number" ? params.rotation : 0, + }; +} + +export function toGlobalMaskSnapLines({ + lines, + bounds, + canvasSize, +}: { + lines: SnapLine[]; + bounds: ElementBounds; + canvasSize: { width: number; height: number }; +}): SnapLine[] { + const centerX = bounds.cx - canvasSize.width / 2; + const centerY = bounds.cy - canvasSize.height / 2; + + return lines.map((line) => + line.type === "vertical" + ? { + type: "vertical" as const, + position: centerX + line.position, + } + : { + type: "horizontal" as const, + position: centerY + line.position, + }, + ); +} |
