feat(canvas): separate mixer resize and crop semantics
This commit is contained in:
@@ -23,6 +23,10 @@ export type MixerPreviewState = {
|
||||
overlayY: number;
|
||||
overlayWidth: number;
|
||||
overlayHeight: number;
|
||||
cropLeft: number;
|
||||
cropTop: number;
|
||||
cropRight: number;
|
||||
cropBottom: number;
|
||||
error?: MixerPreviewError;
|
||||
};
|
||||
|
||||
@@ -41,6 +45,10 @@ const DEFAULT_OVERLAY_X = 0;
|
||||
const DEFAULT_OVERLAY_Y = 0;
|
||||
const DEFAULT_OVERLAY_WIDTH = 1;
|
||||
const DEFAULT_OVERLAY_HEIGHT = 1;
|
||||
const DEFAULT_CROP_LEFT = 0;
|
||||
const DEFAULT_CROP_TOP = 0;
|
||||
const DEFAULT_CROP_RIGHT = 0;
|
||||
const DEFAULT_CROP_BOTTOM = 0;
|
||||
const MIN_OVERLAY_POSITION = 0;
|
||||
const MAX_OVERLAY_POSITION = 1;
|
||||
const MIN_OVERLAY_SIZE = 0.1;
|
||||
@@ -81,6 +89,37 @@ function normalizeOverlayNumber(value: unknown, fallback: number): number {
|
||||
return parsed;
|
||||
}
|
||||
|
||||
function normalizeUnitRect(args: {
|
||||
x: unknown;
|
||||
y: unknown;
|
||||
width: unknown;
|
||||
height: unknown;
|
||||
defaults: { x: number; y: number; width: number; height: number };
|
||||
}): { x: number; y: number; width: number; height: number } {
|
||||
const x = clamp(
|
||||
normalizeOverlayNumber(args.x, args.defaults.x),
|
||||
MIN_OVERLAY_POSITION,
|
||||
MAX_OVERLAY_POSITION - MIN_OVERLAY_SIZE,
|
||||
);
|
||||
const y = clamp(
|
||||
normalizeOverlayNumber(args.y, args.defaults.y),
|
||||
MIN_OVERLAY_POSITION,
|
||||
MAX_OVERLAY_POSITION - MIN_OVERLAY_SIZE,
|
||||
);
|
||||
const width = clamp(
|
||||
normalizeOverlayNumber(args.width, args.defaults.width),
|
||||
MIN_OVERLAY_SIZE,
|
||||
Math.min(MAX_OVERLAY_SIZE, MAX_OVERLAY_POSITION - x),
|
||||
);
|
||||
const height = clamp(
|
||||
normalizeOverlayNumber(args.height, args.defaults.height),
|
||||
MIN_OVERLAY_SIZE,
|
||||
Math.min(MAX_OVERLAY_SIZE, MAX_OVERLAY_POSITION - y),
|
||||
);
|
||||
|
||||
return { x, y, width, height };
|
||||
}
|
||||
|
||||
function normalizeOverlayRect(record: Record<string, unknown>): Pick<
|
||||
MixerPreviewState,
|
||||
"overlayX" | "overlayY" | "overlayWidth" | "overlayHeight"
|
||||
@@ -101,38 +140,105 @@ function normalizeOverlayRect(record: Record<string, unknown>): Pick<
|
||||
};
|
||||
}
|
||||
|
||||
const overlayX = clamp(
|
||||
normalizeOverlayNumber(record.overlayX, DEFAULT_OVERLAY_X),
|
||||
MIN_OVERLAY_POSITION,
|
||||
MAX_OVERLAY_POSITION - MIN_OVERLAY_SIZE,
|
||||
const normalized = normalizeUnitRect({
|
||||
x: record.overlayX,
|
||||
y: record.overlayY,
|
||||
width: record.overlayWidth,
|
||||
height: record.overlayHeight,
|
||||
defaults: {
|
||||
x: DEFAULT_OVERLAY_X,
|
||||
y: DEFAULT_OVERLAY_Y,
|
||||
width: DEFAULT_OVERLAY_WIDTH,
|
||||
height: DEFAULT_OVERLAY_HEIGHT,
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
overlayX: normalized.x,
|
||||
overlayY: normalized.y,
|
||||
overlayWidth: normalized.width,
|
||||
overlayHeight: normalized.height,
|
||||
};
|
||||
}
|
||||
|
||||
function normalizeCropEdges(record: Record<string, unknown>): Pick<
|
||||
MixerPreviewState,
|
||||
"cropLeft" | "cropTop" | "cropRight" | "cropBottom"
|
||||
> {
|
||||
const hasCropField =
|
||||
record.cropLeft !== undefined ||
|
||||
record.cropTop !== undefined ||
|
||||
record.cropRight !== undefined ||
|
||||
record.cropBottom !== undefined;
|
||||
const hasLegacyContentRectField =
|
||||
record.contentX !== undefined ||
|
||||
record.contentY !== undefined ||
|
||||
record.contentWidth !== undefined ||
|
||||
record.contentHeight !== undefined;
|
||||
|
||||
if (!hasCropField && hasLegacyContentRectField) {
|
||||
const legacyRect = normalizeUnitRect({
|
||||
x: record.contentX,
|
||||
y: record.contentY,
|
||||
width: record.contentWidth,
|
||||
height: record.contentHeight,
|
||||
defaults: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 1,
|
||||
height: 1,
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
cropLeft: legacyRect.x,
|
||||
cropTop: legacyRect.y,
|
||||
cropRight: 1 - (legacyRect.x + legacyRect.width),
|
||||
cropBottom: 1 - (legacyRect.y + legacyRect.height),
|
||||
};
|
||||
}
|
||||
|
||||
const cropLeft = clamp(
|
||||
normalizeOverlayNumber(record.cropLeft, DEFAULT_CROP_LEFT),
|
||||
0,
|
||||
1 - MIN_OVERLAY_SIZE,
|
||||
);
|
||||
const overlayY = clamp(
|
||||
normalizeOverlayNumber(record.overlayY, DEFAULT_OVERLAY_Y),
|
||||
MIN_OVERLAY_POSITION,
|
||||
MAX_OVERLAY_POSITION - MIN_OVERLAY_SIZE,
|
||||
const cropTop = clamp(
|
||||
normalizeOverlayNumber(record.cropTop, DEFAULT_CROP_TOP),
|
||||
0,
|
||||
1 - MIN_OVERLAY_SIZE,
|
||||
);
|
||||
const overlayWidth = clamp(
|
||||
normalizeOverlayNumber(record.overlayWidth, DEFAULT_OVERLAY_WIDTH),
|
||||
MIN_OVERLAY_SIZE,
|
||||
Math.min(MAX_OVERLAY_SIZE, MAX_OVERLAY_POSITION - overlayX),
|
||||
const cropRight = clamp(
|
||||
normalizeOverlayNumber(record.cropRight, DEFAULT_CROP_RIGHT),
|
||||
0,
|
||||
1 - cropLeft - MIN_OVERLAY_SIZE,
|
||||
);
|
||||
const overlayHeight = clamp(
|
||||
normalizeOverlayNumber(record.overlayHeight, DEFAULT_OVERLAY_HEIGHT),
|
||||
MIN_OVERLAY_SIZE,
|
||||
Math.min(MAX_OVERLAY_SIZE, MAX_OVERLAY_POSITION - overlayY),
|
||||
const cropBottom = clamp(
|
||||
normalizeOverlayNumber(record.cropBottom, DEFAULT_CROP_BOTTOM),
|
||||
0,
|
||||
1 - cropTop - MIN_OVERLAY_SIZE,
|
||||
);
|
||||
|
||||
return {
|
||||
overlayX,
|
||||
overlayY,
|
||||
overlayWidth,
|
||||
overlayHeight,
|
||||
cropLeft,
|
||||
cropTop,
|
||||
cropRight,
|
||||
cropBottom,
|
||||
};
|
||||
}
|
||||
|
||||
export function normalizeMixerPreviewData(data: unknown): Pick<
|
||||
MixerPreviewState,
|
||||
"blendMode" | "opacity" | "overlayX" | "overlayY" | "overlayWidth" | "overlayHeight"
|
||||
| "blendMode"
|
||||
| "opacity"
|
||||
| "overlayX"
|
||||
| "overlayY"
|
||||
| "overlayWidth"
|
||||
| "overlayHeight"
|
||||
| "cropLeft"
|
||||
| "cropTop"
|
||||
| "cropRight"
|
||||
| "cropBottom"
|
||||
> {
|
||||
const record = (data ?? {}) as Record<string, unknown>;
|
||||
const blendMode = MIXER_BLEND_MODES.has(record.blendMode as MixerBlendMode)
|
||||
@@ -143,6 +249,7 @@ export function normalizeMixerPreviewData(data: unknown): Pick<
|
||||
blendMode,
|
||||
opacity: normalizeOpacity(record.opacity),
|
||||
...normalizeOverlayRect(record),
|
||||
...normalizeCropEdges(record),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -174,6 +281,17 @@ function resolveSourceUrlFromNode(args: {
|
||||
}
|
||||
|
||||
if (args.sourceNode.type === "render") {
|
||||
const preview = resolveRenderPreviewInputFromGraph({
|
||||
nodeId: args.sourceNode.id,
|
||||
graph: args.graph,
|
||||
});
|
||||
if (preview.sourceComposition) {
|
||||
return undefined;
|
||||
}
|
||||
if (preview.sourceUrl) {
|
||||
return preview.sourceUrl;
|
||||
}
|
||||
|
||||
const renderData = (args.sourceNode.data ?? {}) as Record<string, unknown>;
|
||||
const renderOutputUrl =
|
||||
typeof renderData.lastUploadUrl === "string" && renderData.lastUploadUrl.length > 0
|
||||
@@ -188,11 +306,7 @@ function resolveSourceUrlFromNode(args: {
|
||||
return directRenderUrl;
|
||||
}
|
||||
|
||||
const preview = resolveRenderPreviewInputFromGraph({
|
||||
nodeId: args.sourceNode.id,
|
||||
graph: args.graph,
|
||||
});
|
||||
return preview.sourceUrl ?? undefined;
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return resolveNodeImageUrl(args.sourceNode.data) ?? undefined;
|
||||
|
||||
Reference in New Issue
Block a user