parent
401cc35743
commit
6ea5a156b6
|
|
@ -13,7 +13,7 @@ import {
|
||||||
import { getSplitMaskStrokeSegment } from "@/masks/definitions/split";
|
import { getSplitMaskStrokeSegment } from "@/masks/definitions/split";
|
||||||
import { textMaskDefinition } from "@/masks/definitions/text";
|
import { textMaskDefinition } from "@/masks/definitions/text";
|
||||||
import { getMaskSnapGeometry } from "@/masks/geometry";
|
import { getMaskSnapGeometry } from "@/masks/geometry";
|
||||||
import { snapMaskInteraction } from "@/masks/snap";
|
import { snapBoxMaskInteraction, snapSplitMaskInteraction } from "@/masks/snap";
|
||||||
import type { ElementBounds } from "@/preview/element-bounds";
|
import type { ElementBounds } from "@/preview/element-bounds";
|
||||||
import type {
|
import type {
|
||||||
CustomMaskParams,
|
CustomMaskParams,
|
||||||
|
|
@ -230,7 +230,7 @@ describe("mask geometry", () => {
|
||||||
|
|
||||||
describe("mask snapping", () => {
|
describe("mask snapping", () => {
|
||||||
test("snaps split mask movement using the shared position pipeline", () => {
|
test("snaps split mask movement using the shared position pipeline", () => {
|
||||||
const result = snapMaskInteraction({
|
const result = snapSplitMaskInteraction({
|
||||||
handleId: "position",
|
handleId: "position",
|
||||||
startParams: buildSplitParams({
|
startParams: buildSplitParams({
|
||||||
centerX: 0.03,
|
centerX: 0.03,
|
||||||
|
|
@ -254,7 +254,7 @@ describe("mask snapping", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test("snaps box mask movement against element center and edges", () => {
|
test("snaps box mask movement against element center and edges", () => {
|
||||||
const result = snapMaskInteraction({
|
const result = snapBoxMaskInteraction({
|
||||||
handleId: "position",
|
handleId: "position",
|
||||||
startParams: buildRectangleParams(),
|
startParams: buildRectangleParams(),
|
||||||
proposedParams: buildRectangleParams({
|
proposedParams: buildRectangleParams({
|
||||||
|
|
@ -275,7 +275,7 @@ describe("mask snapping", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test("snaps mask rotation through the shared rotation path", () => {
|
test("snaps mask rotation through the shared rotation path", () => {
|
||||||
const result = snapMaskInteraction({
|
const result = snapBoxMaskInteraction({
|
||||||
handleId: "rotation",
|
handleId: "rotation",
|
||||||
startParams: buildRectangleParams(),
|
startParams: buildRectangleParams(),
|
||||||
proposedParams: buildRectangleParams({
|
proposedParams: buildRectangleParams({
|
||||||
|
|
@ -291,7 +291,7 @@ describe("mask snapping", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test("snaps edge resize for box masks", () => {
|
test("snaps edge resize for box masks", () => {
|
||||||
const result = snapMaskInteraction({
|
const result = snapBoxMaskInteraction({
|
||||||
handleId: "right",
|
handleId: "right",
|
||||||
startParams: buildRectangleParams(),
|
startParams: buildRectangleParams(),
|
||||||
proposedParams: buildRectangleParams({
|
proposedParams: buildRectangleParams({
|
||||||
|
|
@ -307,7 +307,7 @@ describe("mask snapping", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test("snaps corner resize for box masks", () => {
|
test("snaps corner resize for box masks", () => {
|
||||||
const result = snapMaskInteraction({
|
const result = snapBoxMaskInteraction({
|
||||||
handleId: "bottom-right",
|
handleId: "bottom-right",
|
||||||
startParams: buildRectangleParams(),
|
startParams: buildRectangleParams(),
|
||||||
proposedParams: buildRectangleParams({
|
proposedParams: buildRectangleParams({
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ import {
|
||||||
getBoxMaskHandlePositions,
|
getBoxMaskHandlePositions,
|
||||||
getBoxMaskOverlays,
|
getBoxMaskOverlays,
|
||||||
} from "@/masks/handle-positions";
|
} from "@/masks/handle-positions";
|
||||||
import { snapMaskInteraction } from "@/masks/snap";
|
import { snapBoxMaskInteraction } from "@/masks/snap";
|
||||||
|
|
||||||
const PERCENTAGE_DISPLAY: Pick<
|
const PERCENTAGE_DISPLAY: Pick<
|
||||||
NumberParamDefinition,
|
NumberParamDefinition,
|
||||||
|
|
@ -198,7 +198,7 @@ export function buildBoxMaskInteraction({
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
snap(args) {
|
snap(args) {
|
||||||
return snapMaskInteraction(args);
|
return snapBoxMaskInteraction(args);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ import {
|
||||||
getLineMaskHandlePositions,
|
getLineMaskHandlePositions,
|
||||||
getLineMaskOverlay,
|
getLineMaskOverlay,
|
||||||
} from "@/masks/handle-positions";
|
} from "@/masks/handle-positions";
|
||||||
import { snapMaskInteraction } from "@/masks/snap";
|
import { snapSplitMaskInteraction } from "@/masks/snap";
|
||||||
|
|
||||||
// cos(π/2) returns ~6e-17 in JS, not 0. Values below this threshold are snapped
|
// cos(π/2) returns ~6e-17 in JS, not 0. Values below this threshold are snapped
|
||||||
// to exactly 0 to prevent opposite-sign float noise on canvas corners that lie
|
// to exactly 0 to prevent opposite-sign float noise on canvas corners that lie
|
||||||
|
|
@ -216,7 +216,7 @@ export const splitMaskDefinition: MaskDefinition<SplitMaskParams> = {
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
snap(args) {
|
snap(args) {
|
||||||
return snapMaskInteraction(args);
|
return snapSplitMaskInteraction(args);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
buildDefault() {
|
buildDefault() {
|
||||||
|
|
|
||||||
|
|
@ -6,23 +6,19 @@ import {
|
||||||
snapScale,
|
snapScale,
|
||||||
snapScaleAxes,
|
snapScaleAxes,
|
||||||
type ScaleEdgePreference,
|
type ScaleEdgePreference,
|
||||||
type SnapLine,
|
|
||||||
} from "@/preview/preview-snap";
|
} from "@/preview/preview-snap";
|
||||||
import type { RectangleMaskParams, SplitMaskParams } from "@/masks/types";
|
import type {
|
||||||
|
MaskSnapArgs,
|
||||||
|
MaskSnapResult,
|
||||||
|
RectangleMaskParams,
|
||||||
|
SplitMaskParams,
|
||||||
|
} from "@/masks/types";
|
||||||
import {
|
import {
|
||||||
isRectangleMaskParams,
|
|
||||||
getMaskSnapGeometry,
|
getMaskSnapGeometry,
|
||||||
setMaskLocalCenter,
|
setMaskLocalCenter,
|
||||||
toGlobalMaskSnapLines,
|
toGlobalMaskSnapLines,
|
||||||
} from "./geometry";
|
} from "./geometry";
|
||||||
|
|
||||||
type SharedMaskParams = SplitMaskParams | RectangleMaskParams;
|
|
||||||
|
|
||||||
type MaskSnapResult<TParams extends SharedMaskParams> = {
|
|
||||||
params: TParams;
|
|
||||||
activeLines: SnapLine[];
|
|
||||||
};
|
|
||||||
|
|
||||||
const CORNER_SIZE_HANDLES = new Set([
|
const CORNER_SIZE_HANDLES = new Set([
|
||||||
"top-left",
|
"top-left",
|
||||||
"top-right",
|
"top-right",
|
||||||
|
|
@ -75,11 +71,33 @@ function snapMaskPosition({
|
||||||
canvasSize,
|
canvasSize,
|
||||||
snapThreshold,
|
snapThreshold,
|
||||||
}: {
|
}: {
|
||||||
proposedParams: SharedMaskParams;
|
proposedParams: RectangleMaskParams;
|
||||||
bounds: ElementBounds;
|
bounds: ElementBounds;
|
||||||
canvasSize: { width: number; height: number };
|
canvasSize: { width: number; height: number };
|
||||||
snapThreshold: { x: number; y: number };
|
snapThreshold: { x: number; y: number };
|
||||||
}): MaskSnapResult<SharedMaskParams> {
|
}): MaskSnapResult<RectangleMaskParams>;
|
||||||
|
function snapMaskPosition({
|
||||||
|
proposedParams,
|
||||||
|
bounds,
|
||||||
|
canvasSize,
|
||||||
|
snapThreshold,
|
||||||
|
}: {
|
||||||
|
proposedParams: SplitMaskParams;
|
||||||
|
bounds: ElementBounds;
|
||||||
|
canvasSize: { width: number; height: number };
|
||||||
|
snapThreshold: { x: number; y: number };
|
||||||
|
}): MaskSnapResult<SplitMaskParams>;
|
||||||
|
function snapMaskPosition({
|
||||||
|
proposedParams,
|
||||||
|
bounds,
|
||||||
|
canvasSize,
|
||||||
|
snapThreshold,
|
||||||
|
}: {
|
||||||
|
proposedParams: RectangleMaskParams | SplitMaskParams;
|
||||||
|
bounds: ElementBounds;
|
||||||
|
canvasSize: { width: number; height: number };
|
||||||
|
snapThreshold: { x: number; y: number };
|
||||||
|
}): MaskSnapResult<RectangleMaskParams | SplitMaskParams> {
|
||||||
const geometry = getMaskSnapGeometry({
|
const geometry = getMaskSnapGeometry({
|
||||||
params: proposedParams,
|
params: proposedParams,
|
||||||
bounds,
|
bounds,
|
||||||
|
|
@ -115,12 +133,18 @@ function snapMaskPosition({
|
||||||
function snapMaskRotation({
|
function snapMaskRotation({
|
||||||
proposedParams,
|
proposedParams,
|
||||||
}: {
|
}: {
|
||||||
proposedParams: SharedMaskParams;
|
proposedParams: RectangleMaskParams;
|
||||||
}): MaskSnapResult<SharedMaskParams> {
|
}): MaskSnapResult<RectangleMaskParams>;
|
||||||
if (typeof proposedParams.rotation !== "number") {
|
function snapMaskRotation({
|
||||||
return { params: proposedParams, activeLines: [] };
|
proposedParams,
|
||||||
}
|
}: {
|
||||||
|
proposedParams: SplitMaskParams;
|
||||||
|
}): MaskSnapResult<SplitMaskParams>;
|
||||||
|
function snapMaskRotation({
|
||||||
|
proposedParams,
|
||||||
|
}: {
|
||||||
|
proposedParams: RectangleMaskParams | SplitMaskParams;
|
||||||
|
}): MaskSnapResult<RectangleMaskParams | SplitMaskParams> {
|
||||||
const { snappedRotation } = snapRotation({
|
const { snappedRotation } = snapRotation({
|
||||||
proposedRotation: proposedParams.rotation,
|
proposedRotation: proposedParams.rotation,
|
||||||
});
|
});
|
||||||
|
|
@ -143,19 +167,12 @@ function snapBoxMaskSize({
|
||||||
snapThreshold,
|
snapThreshold,
|
||||||
}: {
|
}: {
|
||||||
handleId: string;
|
handleId: string;
|
||||||
startParams: SharedMaskParams;
|
startParams: RectangleMaskParams;
|
||||||
proposedParams: SharedMaskParams;
|
proposedParams: RectangleMaskParams;
|
||||||
bounds: ElementBounds;
|
bounds: ElementBounds;
|
||||||
canvasSize: { width: number; height: number };
|
canvasSize: { width: number; height: number };
|
||||||
snapThreshold: { x: number; y: number };
|
snapThreshold: { x: number; y: number };
|
||||||
}): MaskSnapResult<SharedMaskParams> {
|
}): MaskSnapResult<RectangleMaskParams> {
|
||||||
if (
|
|
||||||
!isRectangleMaskParams(startParams) ||
|
|
||||||
!isRectangleMaskParams(proposedParams)
|
|
||||||
) {
|
|
||||||
return { params: proposedParams, activeLines: [] };
|
|
||||||
}
|
|
||||||
|
|
||||||
const geometry = getMaskSnapGeometry({
|
const geometry = getMaskSnapGeometry({
|
||||||
params: proposedParams,
|
params: proposedParams,
|
||||||
bounds,
|
bounds,
|
||||||
|
|
@ -297,7 +314,7 @@ function snapBoxMaskSize({
|
||||||
return { params: proposedParams, activeLines: [] };
|
return { params: proposedParams, activeLines: [] };
|
||||||
}
|
}
|
||||||
|
|
||||||
export function snapMaskInteraction<TParams extends SharedMaskParams>({
|
export function snapBoxMaskInteraction({
|
||||||
handleId,
|
handleId,
|
||||||
startParams,
|
startParams,
|
||||||
proposedParams,
|
proposedParams,
|
||||||
|
|
@ -306,23 +323,23 @@ export function snapMaskInteraction<TParams extends SharedMaskParams>({
|
||||||
snapThreshold,
|
snapThreshold,
|
||||||
}: {
|
}: {
|
||||||
handleId: string;
|
handleId: string;
|
||||||
startParams: TParams;
|
startParams: RectangleMaskParams;
|
||||||
proposedParams: TParams;
|
proposedParams: RectangleMaskParams;
|
||||||
bounds: ElementBounds;
|
bounds: ElementBounds;
|
||||||
canvasSize: { width: number; height: number };
|
canvasSize: { width: number; height: number };
|
||||||
snapThreshold: { x: number; y: number };
|
snapThreshold: { x: number; y: number };
|
||||||
}): MaskSnapResult<TParams> {
|
}): MaskSnapResult<RectangleMaskParams> {
|
||||||
if (handleId === "position") {
|
if (handleId === "position") {
|
||||||
return snapMaskPosition({
|
return snapMaskPosition({
|
||||||
proposedParams,
|
proposedParams,
|
||||||
bounds,
|
bounds,
|
||||||
canvasSize,
|
canvasSize,
|
||||||
snapThreshold,
|
snapThreshold,
|
||||||
}) as MaskSnapResult<TParams>;
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (handleId === "rotation") {
|
if (handleId === "rotation") {
|
||||||
return snapMaskRotation({ proposedParams }) as MaskSnapResult<TParams>;
|
return snapMaskRotation({ proposedParams });
|
||||||
}
|
}
|
||||||
|
|
||||||
return snapBoxMaskSize({
|
return snapBoxMaskSize({
|
||||||
|
|
@ -332,5 +349,28 @@ export function snapMaskInteraction<TParams extends SharedMaskParams>({
|
||||||
bounds,
|
bounds,
|
||||||
canvasSize,
|
canvasSize,
|
||||||
snapThreshold,
|
snapThreshold,
|
||||||
}) as MaskSnapResult<TParams>;
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function snapSplitMaskInteraction({
|
||||||
|
handleId,
|
||||||
|
proposedParams,
|
||||||
|
bounds,
|
||||||
|
canvasSize,
|
||||||
|
snapThreshold,
|
||||||
|
}: MaskSnapArgs<SplitMaskParams>): MaskSnapResult<SplitMaskParams> {
|
||||||
|
if (handleId === "position") {
|
||||||
|
return snapMaskPosition({
|
||||||
|
proposedParams,
|
||||||
|
bounds,
|
||||||
|
canvasSize,
|
||||||
|
snapThreshold,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (handleId === "rotation") {
|
||||||
|
return snapMaskRotation({ proposedParams });
|
||||||
|
}
|
||||||
|
|
||||||
|
return { params: proposedParams, activeLines: [] };
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue