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