refactor(snaps): filter redundant point snaplines
This commit is contained in:
+256
-60
@@ -55,10 +55,35 @@ type Vector2D = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
type PointPair = [GlobalPoint, GlobalPoint];
|
type PointPair = [GlobalPoint, GlobalPoint];
|
||||||
|
type SnapPointType = "outer" | "center";
|
||||||
|
type SnapPointSourceId = string;
|
||||||
|
|
||||||
|
const SELECTION_SNAP_POINT_SOURCE_ID = "selection";
|
||||||
|
|
||||||
|
// Source ids let us filter redundant snaplines only within one
|
||||||
|
// selection-to-reference relationship, without mixing separate reference groups.
|
||||||
|
// For example, "selection->rectA" may collapse to its outermost snaplines,
|
||||||
|
// while "selection->rectB" still keeps its own independent snaplines.
|
||||||
|
type SnapPoint = {
|
||||||
|
point: GlobalPoint;
|
||||||
|
type: SnapPointType;
|
||||||
|
sourceId: SnapPointSourceId;
|
||||||
|
};
|
||||||
|
|
||||||
|
const outerSnapPoint = (
|
||||||
|
point: GlobalPoint,
|
||||||
|
sourceId = SELECTION_SNAP_POINT_SOURCE_ID,
|
||||||
|
): SnapPoint => ({
|
||||||
|
point,
|
||||||
|
type: "outer",
|
||||||
|
sourceId,
|
||||||
|
});
|
||||||
|
|
||||||
export type PointSnap = {
|
export type PointSnap = {
|
||||||
type: "point";
|
type: "point";
|
||||||
points: PointPair;
|
points: PointPair;
|
||||||
|
pointTypes: [SnapPointType, SnapPointType];
|
||||||
|
sourceIds: [SnapPointSourceId, SnapPointSourceId];
|
||||||
offset: number;
|
offset: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -120,14 +145,14 @@ export type SnapLine = PointSnapLine | GapSnapLine | PointerSnapLine;
|
|||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|
||||||
export class SnapCache {
|
export class SnapCache {
|
||||||
private static referenceSnapPoints: GlobalPoint[] | null = null;
|
private static referenceSnapPoints: SnapPoint[] | null = null;
|
||||||
|
|
||||||
private static visibleGaps: {
|
private static visibleGaps: {
|
||||||
verticalGaps: Gap[];
|
verticalGaps: Gap[];
|
||||||
horizontalGaps: Gap[];
|
horizontalGaps: Gap[];
|
||||||
} | null = null;
|
} | null = null;
|
||||||
|
|
||||||
public static setReferenceSnapPoints = (snapPoints: GlobalPoint[] | null) => {
|
public static setReferenceSnapPoints = (snapPoints: SnapPoint[] | null) => {
|
||||||
SnapCache.referenceSnapPoints = snapPoints;
|
SnapCache.referenceSnapPoints = snapPoints;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -312,6 +337,30 @@ export const getElementsCorners = (
|
|||||||
return result.map((p) => pointFrom(round(p[0]), round(p[1])));
|
return result.map((p) => pointFrom(round(p[0]), round(p[1])));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getElementsSnapPoints = (
|
||||||
|
elements: ExcalidrawElement[],
|
||||||
|
elementsMap: ElementsMap,
|
||||||
|
options: Parameters<typeof getElementsCorners>[2] = {},
|
||||||
|
sourceId = SELECTION_SNAP_POINT_SOURCE_ID,
|
||||||
|
): SnapPoint[] => {
|
||||||
|
const points = getElementsCorners(elements, elementsMap, options);
|
||||||
|
// getElementsCorners() appends the center point last unless omitCenter is set.
|
||||||
|
const hasCenterPoint = !options.omitCenter && points.length > 0;
|
||||||
|
|
||||||
|
return points.map((point, index) => ({
|
||||||
|
point,
|
||||||
|
type: hasCenterPoint && index === points.length - 1 ? "center" : "outer",
|
||||||
|
sourceId,
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
const getSnapPointSourceId = (elements: ExcalidrawElement[]) => {
|
||||||
|
return elements
|
||||||
|
.map((element) => element.id)
|
||||||
|
.sort()
|
||||||
|
.join(",");
|
||||||
|
};
|
||||||
|
|
||||||
const getReferenceElements = (
|
const getReferenceElements = (
|
||||||
elements: readonly NonDeletedExcalidrawElement[],
|
elements: readonly NonDeletedExcalidrawElement[],
|
||||||
selectedElements: NonDeletedExcalidrawElement[],
|
selectedElements: NonDeletedExcalidrawElement[],
|
||||||
@@ -630,12 +679,19 @@ export const getReferenceSnapPoints = (
|
|||||||
(elementsGroup) =>
|
(elementsGroup) =>
|
||||||
!(elementsGroup.length === 1 && isBoundToContainer(elementsGroup[0])),
|
!(elementsGroup.length === 1 && isBoundToContainer(elementsGroup[0])),
|
||||||
)
|
)
|
||||||
.flatMap((elementGroup) => getElementsCorners(elementGroup, elementsMap));
|
.flatMap((elementGroup) =>
|
||||||
|
getElementsSnapPoints(
|
||||||
|
elementGroup,
|
||||||
|
elementsMap,
|
||||||
|
{},
|
||||||
|
getSnapPointSourceId(elementGroup),
|
||||||
|
),
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const getPointSnaps = (
|
const getPointSnaps = (
|
||||||
selectedElements: ExcalidrawElement[],
|
selectedElements: ExcalidrawElement[],
|
||||||
selectionSnapPoints: GlobalPoint[],
|
selectionSnapPoints: SnapPoint[],
|
||||||
app: AppClassProperties,
|
app: AppClassProperties,
|
||||||
event: KeyboardModifiersObject,
|
event: KeyboardModifiersObject,
|
||||||
nearestSnapsX: Snaps,
|
nearestSnapsX: Snaps,
|
||||||
@@ -654,8 +710,8 @@ const getPointSnaps = (
|
|||||||
if (referenceSnapPoints) {
|
if (referenceSnapPoints) {
|
||||||
for (const thisSnapPoint of selectionSnapPoints) {
|
for (const thisSnapPoint of selectionSnapPoints) {
|
||||||
for (const otherSnapPoint of referenceSnapPoints) {
|
for (const otherSnapPoint of referenceSnapPoints) {
|
||||||
const offsetX = otherSnapPoint[0] - thisSnapPoint[0];
|
const offsetX = otherSnapPoint.point[0] - thisSnapPoint.point[0];
|
||||||
const offsetY = otherSnapPoint[1] - thisSnapPoint[1];
|
const offsetY = otherSnapPoint.point[1] - thisSnapPoint.point[1];
|
||||||
|
|
||||||
if (Math.abs(offsetX) <= minOffset.x) {
|
if (Math.abs(offsetX) <= minOffset.x) {
|
||||||
if (Math.abs(offsetX) < minOffset.x) {
|
if (Math.abs(offsetX) < minOffset.x) {
|
||||||
@@ -664,7 +720,9 @@ const getPointSnaps = (
|
|||||||
|
|
||||||
nearestSnapsX.push({
|
nearestSnapsX.push({
|
||||||
type: "point",
|
type: "point",
|
||||||
points: [thisSnapPoint, otherSnapPoint],
|
points: [thisSnapPoint.point, otherSnapPoint.point],
|
||||||
|
pointTypes: [thisSnapPoint.type, otherSnapPoint.type],
|
||||||
|
sourceIds: [thisSnapPoint.sourceId, otherSnapPoint.sourceId],
|
||||||
offset: offsetX,
|
offset: offsetX,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -678,7 +736,9 @@ const getPointSnaps = (
|
|||||||
|
|
||||||
nearestSnapsY.push({
|
nearestSnapsY.push({
|
||||||
type: "point",
|
type: "point",
|
||||||
points: [thisSnapPoint, otherSnapPoint],
|
points: [thisSnapPoint.point, otherSnapPoint.point],
|
||||||
|
pointTypes: [thisSnapPoint.type, otherSnapPoint.type],
|
||||||
|
sourceIds: [thisSnapPoint.sourceId, otherSnapPoint.sourceId],
|
||||||
offset: offsetY,
|
offset: offsetY,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -720,7 +780,7 @@ export const snapDraggedElements = (
|
|||||||
y: snapDistance,
|
y: snapDistance,
|
||||||
};
|
};
|
||||||
|
|
||||||
const selectionPoints = getElementsCorners(selectedElements, elementsMap, {
|
const selectionPoints = getElementsSnapPoints(selectedElements, elementsMap, {
|
||||||
dragOffset,
|
dragOffset,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -770,7 +830,7 @@ export const snapDraggedElements = (
|
|||||||
|
|
||||||
getPointSnaps(
|
getPointSnaps(
|
||||||
selectedElements,
|
selectedElements,
|
||||||
getElementsCorners(selectedElements, elementsMap, {
|
getElementsSnapPoints(selectedElements, elementsMap, {
|
||||||
dragOffset: newDragOffset,
|
dragOffset: newDragOffset,
|
||||||
}),
|
}),
|
||||||
app,
|
app,
|
||||||
@@ -825,12 +885,124 @@ const dedupePoints = (points: GlobalPoint[]): GlobalPoint[] => {
|
|||||||
return Array.from(map.values());
|
return Array.from(map.values());
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type PointSnapLineMatch = {
|
||||||
|
sourceKey: string;
|
||||||
|
pointType: SnapPointType;
|
||||||
|
};
|
||||||
|
|
||||||
|
type PointSnapLineCandidate = PointSnapLine & {
|
||||||
|
matches: PointSnapLineMatch[];
|
||||||
|
};
|
||||||
|
|
||||||
|
type PointSnapLineBucket = {
|
||||||
|
points: GlobalPoint[];
|
||||||
|
matches: PointSnapLineMatch[];
|
||||||
|
};
|
||||||
|
|
||||||
|
const getPointSnapLineMatch = (snap: PointSnap): PointSnapLineMatch => {
|
||||||
|
return {
|
||||||
|
sourceKey: snap.sourceIds.join("->"),
|
||||||
|
pointType: snap.pointTypes.every((pointType) => pointType === "center")
|
||||||
|
? "center"
|
||||||
|
: "outer",
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const getOuterCoordinatesBySourceKey = (
|
||||||
|
snapLines: PointSnapLineCandidate[],
|
||||||
|
getCoordinate: (snapLine: PointSnapLine) => number,
|
||||||
|
) => {
|
||||||
|
const outerCoordinates = new Map<string, number[]>();
|
||||||
|
|
||||||
|
for (const snapLine of snapLines) {
|
||||||
|
const coordinate = getCoordinate(snapLine);
|
||||||
|
|
||||||
|
for (const match of snapLine.matches) {
|
||||||
|
if (match.pointType === "center") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const sourceCoordinates = outerCoordinates.get(match.sourceKey) ?? [];
|
||||||
|
sourceCoordinates.push(coordinate);
|
||||||
|
outerCoordinates.set(match.sourceKey, sourceCoordinates);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return outerCoordinates;
|
||||||
|
};
|
||||||
|
|
||||||
|
// A center snapline is redundant when outer snaplines from the same source pair
|
||||||
|
// exist on both sides, because the outer lines already imply center alignment.
|
||||||
|
const isRedundantCenterSnapLine = (
|
||||||
|
coordinate: number,
|
||||||
|
sourceOuterCoordinates: number[],
|
||||||
|
) => {
|
||||||
|
const hasOuterBefore = sourceOuterCoordinates.some(
|
||||||
|
(outerCoordinate) => outerCoordinate < coordinate,
|
||||||
|
);
|
||||||
|
const hasOuterAfter = sourceOuterCoordinates.some(
|
||||||
|
(outerCoordinate) => outerCoordinate > coordinate,
|
||||||
|
);
|
||||||
|
|
||||||
|
return hasOuterBefore && hasOuterAfter;
|
||||||
|
};
|
||||||
|
|
||||||
|
// When more than two outer snaplines come from the same source pair, the inner
|
||||||
|
// ones are redundant; the two extremes communicate the same shape alignment.
|
||||||
|
const isRedundantOuterSnapLine = (
|
||||||
|
coordinate: number,
|
||||||
|
sourceOuterCoordinates: number[],
|
||||||
|
) => {
|
||||||
|
if (sourceOuterCoordinates.length <= 2) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const minCoordinate = Math.min(...sourceOuterCoordinates);
|
||||||
|
const maxCoordinate = Math.max(...sourceOuterCoordinates);
|
||||||
|
|
||||||
|
return coordinate !== minCoordinate && coordinate !== maxCoordinate;
|
||||||
|
};
|
||||||
|
|
||||||
|
const filterRedundantPointSnapLines = (
|
||||||
|
snapLines: PointSnapLineCandidate[],
|
||||||
|
getCoordinate: (snapLine: PointSnapLine) => number,
|
||||||
|
): PointSnapLine[] => {
|
||||||
|
if (snapLines.length < 3) {
|
||||||
|
return snapLines.map(({ matches, ...snapLine }) => snapLine);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Track outer-line coordinates per source pair. The same visual snapline can
|
||||||
|
// be produced by multiple source pairs, so a line is removed only when every
|
||||||
|
// pair represented by that line considers it redundant.
|
||||||
|
const outerCoordinatesBySourceKey = getOuterCoordinatesBySourceKey(
|
||||||
|
snapLines,
|
||||||
|
getCoordinate,
|
||||||
|
);
|
||||||
|
|
||||||
|
return snapLines
|
||||||
|
.filter((snapLine) => {
|
||||||
|
const coordinate = getCoordinate(snapLine);
|
||||||
|
|
||||||
|
return snapLine.matches.some((match) => {
|
||||||
|
const sourceOuterCoordinates =
|
||||||
|
outerCoordinatesBySourceKey.get(match.sourceKey) ?? [];
|
||||||
|
|
||||||
|
if (match.pointType === "center") {
|
||||||
|
return !isRedundantCenterSnapLine(coordinate, sourceOuterCoordinates);
|
||||||
|
}
|
||||||
|
|
||||||
|
return !isRedundantOuterSnapLine(coordinate, sourceOuterCoordinates);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.map(({ matches, ...snapLine }) => snapLine);
|
||||||
|
};
|
||||||
|
|
||||||
const createPointSnapLines = (
|
const createPointSnapLines = (
|
||||||
nearestSnapsX: Snaps,
|
nearestSnapsX: Snaps,
|
||||||
nearestSnapsY: Snaps,
|
nearestSnapsY: Snaps,
|
||||||
): PointSnapLine[] => {
|
): PointSnapLine[] => {
|
||||||
const snapsX = {} as { [key: string]: GlobalPoint[] };
|
const snapsX = {} as { [key: string]: PointSnapLineBucket };
|
||||||
const snapsY = {} as { [key: string]: GlobalPoint[] };
|
const snapsY = {} as { [key: string]: PointSnapLineBucket };
|
||||||
|
|
||||||
if (nearestSnapsX.length > 0) {
|
if (nearestSnapsX.length > 0) {
|
||||||
for (const snap of nearestSnapsX) {
|
for (const snap of nearestSnapsX) {
|
||||||
@@ -838,13 +1010,14 @@ const createPointSnapLines = (
|
|||||||
// key = thisPoint.x
|
// key = thisPoint.x
|
||||||
const key = round(snap.points[0][0]);
|
const key = round(snap.points[0][0]);
|
||||||
if (!snapsX[key]) {
|
if (!snapsX[key]) {
|
||||||
snapsX[key] = [];
|
snapsX[key] = { points: [], matches: [] };
|
||||||
}
|
}
|
||||||
snapsX[key].push(
|
snapsX[key].points.push(
|
||||||
...snap.points.map((p) =>
|
...snap.points.map((p) =>
|
||||||
pointFrom<GlobalPoint>(round(p[0]), round(p[1])),
|
pointFrom<GlobalPoint>(round(p[0]), round(p[1])),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
snapsX[key].matches.push(getPointSnapLineMatch(snap));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -855,44 +1028,55 @@ const createPointSnapLines = (
|
|||||||
// key = thisPoint.y
|
// key = thisPoint.y
|
||||||
const key = round(snap.points[0][1]);
|
const key = round(snap.points[0][1]);
|
||||||
if (!snapsY[key]) {
|
if (!snapsY[key]) {
|
||||||
snapsY[key] = [];
|
snapsY[key] = { points: [], matches: [] };
|
||||||
}
|
}
|
||||||
snapsY[key].push(
|
snapsY[key].points.push(
|
||||||
...snap.points.map((p) =>
|
...snap.points.map((p) =>
|
||||||
pointFrom<GlobalPoint>(round(p[0]), round(p[1])),
|
pointFrom<GlobalPoint>(round(p[0]), round(p[1])),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
snapsY[key].matches.push(getPointSnapLineMatch(snap));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Object.entries(snapsX)
|
const snapLinesX = Object.entries(snapsX).map(([key, snap]) => {
|
||||||
.map(([key, points]) => {
|
return {
|
||||||
return {
|
type: "points",
|
||||||
type: "points",
|
points: dedupePoints(
|
||||||
points: dedupePoints(
|
snap.points
|
||||||
points
|
.map((p) => {
|
||||||
.map((p) => {
|
return pointFrom<GlobalPoint>(Number(key), p[1]);
|
||||||
return pointFrom<GlobalPoint>(Number(key), p[1]);
|
})
|
||||||
})
|
.sort((a, b) => a[1] - b[1]),
|
||||||
.sort((a, b) => a[1] - b[1]),
|
),
|
||||||
),
|
matches: snap.matches,
|
||||||
} as PointSnapLine;
|
} as PointSnapLineCandidate;
|
||||||
})
|
});
|
||||||
.concat(
|
|
||||||
Object.entries(snapsY).map(([key, points]) => {
|
const snapLinesY = Object.entries(snapsY).map(([key, snap]) => {
|
||||||
return {
|
return {
|
||||||
type: "points",
|
type: "points",
|
||||||
points: dedupePoints(
|
points: dedupePoints(
|
||||||
points
|
snap.points
|
||||||
.map((p) => {
|
.map((p) => {
|
||||||
return pointFrom<GlobalPoint>(p[0], Number(key));
|
return pointFrom<GlobalPoint>(p[0], Number(key));
|
||||||
})
|
})
|
||||||
.sort((a, b) => a[0] - b[0]),
|
.sort((a, b) => a[0] - b[0]),
|
||||||
),
|
),
|
||||||
} as PointSnapLine;
|
matches: snap.matches,
|
||||||
}),
|
} as PointSnapLineCandidate;
|
||||||
);
|
});
|
||||||
|
|
||||||
|
return filterRedundantPointSnapLines(
|
||||||
|
snapLinesX,
|
||||||
|
(snapLine) => snapLine.points[0][0],
|
||||||
|
).concat(
|
||||||
|
filterRedundantPointSnapLines(
|
||||||
|
snapLinesY,
|
||||||
|
(snapLine) => snapLine.points[0][1],
|
||||||
|
),
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const dedupeGapSnapLines = (gapSnapLines: GapSnapLine[]) => {
|
const dedupeGapSnapLines = (gapSnapLines: GapSnapLine[]) => {
|
||||||
@@ -1143,40 +1327,52 @@ export const snapResizingElements = (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const selectionSnapPoints: GlobalPoint[] = [];
|
const selectionSnapPoints: SnapPoint[] = [];
|
||||||
|
|
||||||
if (transformHandle) {
|
if (transformHandle) {
|
||||||
switch (transformHandle) {
|
switch (transformHandle) {
|
||||||
case "e": {
|
case "e": {
|
||||||
selectionSnapPoints.push(pointFrom(maxX, minY), pointFrom(maxX, maxY));
|
selectionSnapPoints.push(
|
||||||
|
outerSnapPoint(pointFrom(maxX, minY)),
|
||||||
|
outerSnapPoint(pointFrom(maxX, maxY)),
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "w": {
|
case "w": {
|
||||||
selectionSnapPoints.push(pointFrom(minX, minY), pointFrom(minX, maxY));
|
selectionSnapPoints.push(
|
||||||
|
outerSnapPoint(pointFrom(minX, minY)),
|
||||||
|
outerSnapPoint(pointFrom(minX, maxY)),
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "n": {
|
case "n": {
|
||||||
selectionSnapPoints.push(pointFrom(minX, minY), pointFrom(maxX, minY));
|
selectionSnapPoints.push(
|
||||||
|
outerSnapPoint(pointFrom(minX, minY)),
|
||||||
|
outerSnapPoint(pointFrom(maxX, minY)),
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "s": {
|
case "s": {
|
||||||
selectionSnapPoints.push(pointFrom(minX, maxY), pointFrom(maxX, maxY));
|
selectionSnapPoints.push(
|
||||||
|
outerSnapPoint(pointFrom(minX, maxY)),
|
||||||
|
outerSnapPoint(pointFrom(maxX, maxY)),
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "ne": {
|
case "ne": {
|
||||||
selectionSnapPoints.push(pointFrom(maxX, minY));
|
selectionSnapPoints.push(outerSnapPoint(pointFrom(maxX, minY)));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "nw": {
|
case "nw": {
|
||||||
selectionSnapPoints.push(pointFrom(minX, minY));
|
selectionSnapPoints.push(outerSnapPoint(pointFrom(minX, minY)));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "se": {
|
case "se": {
|
||||||
selectionSnapPoints.push(pointFrom(maxX, maxY));
|
selectionSnapPoints.push(outerSnapPoint(pointFrom(maxX, maxY)));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "sw": {
|
case "sw": {
|
||||||
selectionSnapPoints.push(pointFrom(minX, maxY));
|
selectionSnapPoints.push(outerSnapPoint(pointFrom(minX, maxY)));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1218,11 +1414,11 @@ export const snapResizingElements = (
|
|||||||
round(bound),
|
round(bound),
|
||||||
);
|
);
|
||||||
|
|
||||||
const corners: GlobalPoint[] = [
|
const corners: SnapPoint[] = [
|
||||||
pointFrom(x1, y1),
|
outerSnapPoint(pointFrom(x1, y1)),
|
||||||
pointFrom(x1, y2),
|
outerSnapPoint(pointFrom(x1, y2)),
|
||||||
pointFrom(x2, y1),
|
outerSnapPoint(pointFrom(x2, y1)),
|
||||||
pointFrom(x2, y2),
|
outerSnapPoint(pointFrom(x2, y2)),
|
||||||
];
|
];
|
||||||
|
|
||||||
getPointSnaps(
|
getPointSnaps(
|
||||||
@@ -1258,8 +1454,8 @@ export const snapNewElement = (
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const selectionSnapPoints: GlobalPoint[] = [
|
const selectionSnapPoints: SnapPoint[] = [
|
||||||
pointFrom(origin.x + dragOffset.x, origin.y + dragOffset.y),
|
outerSnapPoint(pointFrom(origin.x + dragOffset.x, origin.y + dragOffset.y)),
|
||||||
];
|
];
|
||||||
|
|
||||||
const snapDistance = getSnapDistance(app.state.zoom.value);
|
const snapDistance = getSnapDistance(app.state.zoom.value);
|
||||||
@@ -1292,7 +1488,7 @@ export const snapNewElement = (
|
|||||||
nearestSnapsX.length = 0;
|
nearestSnapsX.length = 0;
|
||||||
nearestSnapsY.length = 0;
|
nearestSnapsY.length = 0;
|
||||||
|
|
||||||
const corners = getElementsCorners([newElement], elementsMap, {
|
const corners = getElementsSnapPoints([newElement], elementsMap, {
|
||||||
boundingBoxCorners: true,
|
boundingBoxCorners: true,
|
||||||
omitCenter: true,
|
omitCenter: true,
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user