Compare commits

..

3 Commits

Author SHA1 Message Date
Márk Tolmács
d87620b239 fix: Circular reference (#10544)
* fix: Circular reference

Signed-off-by: Mark Tolmacs <mark@lazycat.hu>

* fix: Lint

Signed-off-by: Mark Tolmacs <mark@lazycat.hu>

* Trigger CI

---------

Signed-off-by: Mark Tolmacs <mark@lazycat.hu>
2025-12-21 22:14:21 +01:00
Viczián András
7cc31ac64a fix: Context menu paste adding image twice #10542 (#10543)
removed line that was adding image file twice to paste
2025-12-20 06:31:02 +01:00
zsviczian
071b17a217 fix: Embeddables lost stroke color option in element properties after #9996 (#10541)
Add 'embeddable' type to comparisons
2025-12-19 18:23:09 +01:00
31 changed files with 76 additions and 577 deletions

View File

@@ -0,0 +1,17 @@
/**
* x and y position of top left corner, x and y position of bottom right corner
*/
export type Bounds = readonly [
minX: number,
minY: number,
maxX: number,
maxY: number,
];
export const isBounds = (box: unknown): box is Bounds =>
Array.isArray(box) &&
box.length === 4 &&
typeof box[0] === "number" &&
typeof box[1] === "number" &&
typeof box[2] === "number" &&
typeof box[3] === "number";

View File

@@ -1,4 +1,5 @@
export * from "./binary-heap";
export * from "./bounds";
export * from "./colors";
export * from "./constants";
export * from "./font-metadata";

View File

@@ -6,12 +6,10 @@ import {
type LocalPoint,
} from "@excalidraw/math";
import { isBounds } from "@excalidraw/element";
import type { Curve } from "@excalidraw/math";
import type { LineSegment } from "@excalidraw/utils";
import type { Bounds } from "@excalidraw/element";
import { type Bounds, isBounds } from "./bounds";
// The global data holder to collect the debug operations
declare global {

View File

@@ -58,7 +58,6 @@
},
"dependencies": {
"@excalidraw/common": "0.18.0",
"@excalidraw/math": "0.18.0",
"polygon-clipping": "0.15.7"
"@excalidraw/math": "0.18.0"
}
}

View File

@@ -22,10 +22,9 @@ import {
} from "@excalidraw/math";
import type { LineSegment, LocalPoint, Radians } from "@excalidraw/math";
import type { AppState } from "@excalidraw/excalidraw/types";
import type { MapEntry, Mutable } from "@excalidraw/common/utility-types";
import type { Bounds } from "@excalidraw/common";
import {
doBoundsIntersect,
@@ -64,7 +63,6 @@ import { projectFixedPointOntoDiagonal } from "./utils";
import type { Scene } from "./Scene";
import type { Bounds } from "./bounds";
import type { ElementUpdate } from "./mutateElement";
import type {
BindMode,

View File

@@ -2,6 +2,7 @@ import rough from "roughjs/bin/rough";
import {
arrayToMap,
type Bounds,
invariant,
rescalePoints,
sizeOf,
@@ -78,16 +79,6 @@ export type RectangleBox = {
type MaybeQuadraticSolution = [number | null, number | null] | false;
/**
* x and y position of top left corner, x and y position of bottom right corner
*/
export type Bounds = readonly [
minX: number,
minY: number,
maxX: number,
maxY: number,
];
export type SceneBounds = readonly [
sceneX: number,
sceneY: number,

View File

@@ -1,4 +1,4 @@
import { invariant, isTransparent } from "@excalidraw/common";
import { invariant, isTransparent, type Bounds } from "@excalidraw/common";
import {
curveIntersectLineSegment,
isPointWithinBounds,
@@ -29,7 +29,6 @@ import type { FrameNameBounds } from "@excalidraw/excalidraw/types";
import { isPathALoop } from "./utils";
import {
type Bounds,
doBoundsIntersect,
elementCenterPoint,
getCenterForBounds,

View File

@@ -16,7 +16,8 @@ export const hasStrokeColor = (type: ElementOrToolType) =>
type === "freedraw" ||
type === "arrow" ||
type === "line" ||
type === "text";
type === "text" ||
type === "embeddable";
export const hasStrokeWidth = (type: ElementOrToolType) =>
type === "rectangle" ||

View File

@@ -1,4 +1,5 @@
import {
type Bounds,
TEXT_AUTOWRAP_THRESHOLD,
getGridPoint,
getFontString,
@@ -29,7 +30,6 @@ import {
import type { Scene } from "./Scene";
import type { Bounds } from "./bounds";
import type { ExcalidrawElement } from "./types";
export const dragSelectedElements = (

View File

@@ -14,6 +14,7 @@ import {
} from "@excalidraw/math";
import {
type Bounds,
BinaryHeap,
invariant,
isAnyTrue,
@@ -54,7 +55,6 @@ import {
import { aabbForElement, pointInsideBounds } from "./bounds";
import { getHoveredElementForBinding } from "./collision";
import type { Bounds } from "./bounds";
import type { Heading } from "./heading";
import type {
Arrowhead,

View File

@@ -1,470 +0,0 @@
import polygonClipping from "polygon-clipping";
import {
lineSegment,
lineSegmentIntersectionPoints,
type LocalPoint,
pointFrom,
pointFromVector,
vector,
vectorAntiNormal,
vectorFromPoint,
vectorNormal,
vectorNormalize,
vectorScale,
} from "@excalidraw/math";
import { debugDrawLine } from "@excalidraw/common";
import type { GlobalPoint, LineSegment, Vector } from "@excalidraw/math";
import type { ExcalidrawFreeDrawElement } from "./types";
const detectSelfIntersection = (
stride: LocalPoint[],
segment: LineSegment<LocalPoint>,
) => {
return stride.findIndex((p, i) => {
if (i === stride.length - 1) {
return false;
}
const a = lineSegment(stride[i], stride[i + 1]);
return lineSegmentIntersectionPoints(a, segment);
});
};
const cutUpStrideAtIntersections = (
left: LocalPoint[],
right: LocalPoint[],
): [LocalPoint[][], LocalPoint[][]] => {
const collectSelfIntersectionIndices = (stride: LocalPoint[]) => {
const indices = new Set<number>();
for (let i = 0; i < stride.length - 1; i++) {
const segment = lineSegment(stride[i], stride[i + 1]);
for (let j = i + 2; j < stride.length - 1; j++) {
const otherSegment = lineSegment(stride[j], stride[j + 1]);
if (lineSegmentIntersectionPoints(segment, otherSegment)) {
indices.add(i);
indices.add(j);
}
}
}
return indices;
};
const intersectionIndices = new Set<number>();
for (let r = 0; r < right.length - 1; r++) {
const intersectionIndex = detectSelfIntersection(
left,
lineSegment(right[r], right[r + 1]),
);
if (intersectionIndex >= 0) {
intersectionIndices.add(intersectionIndex);
}
}
for (const index of collectSelfIntersectionIndices(left)) {
intersectionIndices.add(index);
}
for (const index of collectSelfIntersectionIndices(right)) {
intersectionIndices.add(index);
}
if (intersectionIndices.size === 0) {
return [[left], [right]];
}
const sortedIndices = Array.from(intersectionIndices).sort((a, b) => a - b);
const leftStrides: LocalPoint[][] = [];
const rightStrides: LocalPoint[][] = [];
let startIndex = 0;
for (const intersectionIndex of sortedIndices) {
leftStrides.push(left.slice(startIndex, intersectionIndex));
rightStrides.push(right.slice(startIndex, intersectionIndex));
startIndex = intersectionIndex - 1;
}
leftStrides.push(left.slice(startIndex));
rightStrides.push(right.slice(startIndex));
// if (intersectionIndex !== -1) {
// for (let l = 0; l < left.length - 2; l++) {
// const [left, right] = cutUpStrideAtIntersections(
// leftStrides[l],
// rightStrides[l],
// );
// leftStrides = [
// ...leftStrides.slice(0, l - 1),
// ...left,
// ...leftStrides.slice(l + 1),
// ];
// rightStrides = [
// ...rightStrides.slice(0, l - 1),
// ...right,
// ...rightStrides.slice(l + 1),
// ];
// }
// }
return [leftStrides, rightStrides];
};
const catmullRom = (
p0: number,
p1: number,
p2: number,
p3: number,
t: number,
) => {
const t2 = t * t;
const t3 = t2 * t;
return (
0.5 *
(2 * p1 +
(-p0 + p2) * t +
(2 * p0 - 5 * p1 + 4 * p2 - p3) * t2 +
(-p0 + 3 * p1 - 3 * p2 + p3) * t3)
);
};
const midpoint = (a: LocalPoint, b: LocalPoint): LocalPoint =>
[(a[0] + b[0]) / 2, (a[1] + b[1]) / 2] as LocalPoint;
const buildRoundedCapPoints = (
capLeft: LocalPoint,
capRight: LocalPoint,
capDir: Vector,
): LocalPoint[] => {
const center = midpoint(capLeft, capRight);
const radius = Math.hypot(capLeft[0] - center[0], capLeft[1] - center[1]);
if (radius === 0) {
return [capLeft, capRight];
}
const leftAngle = Math.atan2(capLeft[1] - center[1], capLeft[0] - center[0]);
const capDirNorm = vectorNormalize(capDir);
let sign = 1;
if (capDirNorm[0] !== 0 || capDirNorm[1] !== 0) {
const midAngleA = leftAngle + Math.PI / 2;
const midAngleB = leftAngle - Math.PI / 2;
const dotA =
Math.cos(midAngleA) * capDirNorm[0] + Math.sin(midAngleA) * capDirNorm[1];
const dotB =
Math.cos(midAngleB) * capDirNorm[0] + Math.sin(midAngleB) * capDirNorm[1];
sign = dotA >= dotB ? 1 : -1;
}
const angles = [
leftAngle - sign * (Math.PI / 2),
leftAngle,
leftAngle + sign * (Math.PI / 2),
leftAngle + sign * Math.PI,
leftAngle + sign * (Math.PI * 1.5),
];
const stepsPerSegment = 6;
const points: LocalPoint[] = [];
for (let i = 1; i < angles.length - 2; i++) {
const p0 = angles[i - 1];
const p1 = angles[i];
const p2 = angles[i + 1];
const p3 = angles[i + 2];
for (let step = 0; step <= stepsPerSegment; step++) {
if (i > 1 && step === 0) {
continue;
}
const t = step / stepsPerSegment;
const angle = catmullRom(p0, p1, p2, p3, t);
points.push(
pointFrom<LocalPoint>(
center[0] + Math.cos(angle) * radius,
center[1] + Math.sin(angle) * radius,
),
);
}
}
return points;
};
const addCapToOutlinePoints = (
left: LocalPoint[],
right: LocalPoint[],
): LocalPoint[] => {
if (left.length === 0 || right.length === 0) {
return [];
}
const getCapDirection = (isStart: boolean): Vector => {
if (left.length < 2 || right.length < 2) {
return vector(0, 0);
}
const index = isStart ? 0 : left.length - 1;
const adjacentIndex = isStart ? 1 : left.length - 2;
const mid = midpoint(left[index], right[index]);
const adjacentMid = midpoint(left[adjacentIndex], right[adjacentIndex]);
const dir = isStart
? vectorFromPoint(adjacentMid, mid)
: vectorFromPoint(mid, adjacentMid);
return vectorNormalize(dir);
};
const startDir = getCapDirection(true);
const endDir = getCapDirection(false);
const endCap = buildRoundedCapPoints(
left[left.length - 1],
right[right.length - 1],
endDir,
);
const startCap = buildRoundedCapPoints(
left[0],
right[0],
vector(-startDir[0], -startDir[1]),
).reverse();
const rightReversed = right.slice().reverse();
const outline = [
...left,
...endCap.slice(1, -1),
...rightReversed,
...startCap.slice(1, -1),
left[0],
];
return outline;
};
const normalizeUnionRing = (points: LocalPoint[]): LocalPoint[] => {
if (points.length < 2) {
return points;
}
const first = points[0];
const last = points[points.length - 1];
if (first[0] === last[0] && first[1] === last[1]) {
return points.slice(0, -1);
}
return points;
};
const closeUnionRing = (points: LocalPoint[]): LocalPoint[] => {
if (points.length < 2) {
return points;
}
const first = points[0];
const last = points[points.length - 1];
if (first[0] === last[0] && first[1] === last[1]) {
return points;
}
return [...points, first];
};
const getRadiusFromPressure = (
pressure: number | undefined,
prevPoint: LocalPoint,
nextPoint: LocalPoint,
strokeWidth: number,
) => {
return (pressure ?? 1) * strokeWidth * 2;
};
const streamlinePoints = (
points: readonly LocalPoint[],
streamline: number,
): LocalPoint[] => {
if (streamline <= 0 || points.length < 2) {
return [...points];
}
const streamlined: LocalPoint[] = [points[0]];
const t = 1 - streamline;
for (let i = 1; i < points.length; i++) {
const prev = streamlined[streamlined.length - 1];
const next: LocalPoint = pointFrom<LocalPoint>(
prev[0] + (points[i][0] - prev[0]) * t,
prev[1] + (points[i][1] - prev[1]) * t,
);
streamlined.push(next);
}
return streamlined;
};
const densifyPoints = (
points: readonly LocalPoint[],
insertsPerSegment: number,
): LocalPoint[] => {
if (points.length < 2 || insertsPerSegment <= 0) {
return [...points];
}
const densified: LocalPoint[] = [];
for (let i = 0; i < points.length - 1; i++) {
const p0 = points[i - 1] ?? points[i];
const p1 = points[i];
const p2 = points[i + 1];
const p3 = points[i + 2] ?? points[i + 1];
densified.push(p1);
for (let step = 1; step <= insertsPerSegment; step++) {
const t = step / (insertsPerSegment + 1);
densified.push(
pointFrom<LocalPoint>(
catmullRom(p0[0], p1[0], p2[0], p3[0], t),
catmullRom(p0[1], p1[1], p2[1], p3[1], t),
),
);
}
}
densified.push(points[points.length - 1]);
return densified;
};
export const getFreedrawOutlinePoints = (
element: ExcalidrawFreeDrawElement,
) => {
return getFreedrawOutlinePolygons(element).flat();
};
export const getFreedrawOutlinePolygons = (
element: ExcalidrawFreeDrawElement,
): [number, number][][] => {
if (element.points.length < 2) {
return [];
}
const simulatedPressure = element.pressures.length === 0;
const points = streamlinePoints(element.points, 0.2);
//const points = densifyPoints(streamlinePoints(element.points, 0.2), 2);
const leftOutlinePoints: LocalPoint[] = [];
const rightOutlinePoints: LocalPoint[] = [];
for (let i = 0; i < points.length - 2; i++) {
const radius =
!simulatedPressure && i === 0
? getRadiusFromPressure(
element.pressures[i + 1],
points[i + 1],
points[i + 2] ?? points[i + 1],
element.strokeWidth,
)
: !simulatedPressure && points.length > 2 && i === points.length - 1
? getRadiusFromPressure(
element.pressures[i - 1],
points[i - 1],
points[i] ?? points[i - 1],
element.strokeWidth,
)
: getRadiusFromPressure(
element.pressures[i],
points[i],
points[i + 1],
element.strokeWidth,
);
const unit = vectorNormalize(vectorFromPoint(points[i + 1], points[i]));
const v = vectorScale(unit, radius);
leftOutlinePoints.push(
pointFromVector<LocalPoint>(vectorAntiNormal(v), points[i]),
);
rightOutlinePoints.push(
pointFromVector<LocalPoint>(vectorNormal(v), points[i]),
);
const COLORS = [
"#FF0000",
"#00FF00",
"#0000FF",
"#FFFF00",
"#FF00FF",
"#00FFFF",
];
if (i > 0 && i < points.length - 2) {
debugDrawLine(
lineSegment(
pointFrom<GlobalPoint>(
element.x + points[i - 1][0],
element.y + points[i - 1][1],
),
pointFrom<GlobalPoint>(
element.x + points[i][0],
element.y + points[i][1],
),
),
{ permanent: true, color: COLORS[i % COLORS.length] },
);
debugDrawLine(
lineSegment(
pointFrom<GlobalPoint>(
element.x + leftOutlinePoints[i - 1][0],
element.y + leftOutlinePoints[i - 1][1],
),
pointFrom<GlobalPoint>(
element.x + leftOutlinePoints[i][0],
element.y + leftOutlinePoints[i][1],
),
),
{ permanent: true, color: COLORS[i % COLORS.length] },
);
debugDrawLine(
lineSegment(
pointFrom<GlobalPoint>(
element.x + rightOutlinePoints[i - 1][0],
element.y + rightOutlinePoints[i - 1][1],
),
pointFrom<GlobalPoint>(
element.x + rightOutlinePoints[i][0],
element.y + rightOutlinePoints[i][1],
),
),
{ permanent: true, color: COLORS[i % COLORS.length] },
);
}
}
const outline = addCapToOutlinePoints(leftOutlinePoints, rightOutlinePoints);
if (outline.length === 0) {
return [];
}
if (outline.length < 4) {
return [outline];
}
const segmentOutlines: LocalPoint[][] = [];
for (let i = 0; i < leftOutlinePoints.length - 1; i++) {
const segmentOutline = addCapToOutlinePoints(
[leftOutlinePoints[i], leftOutlinePoints[i + 1]],
[rightOutlinePoints[i], rightOutlinePoints[i + 1]],
);
if (segmentOutline.length > 0) {
segmentOutlines.push(segmentOutline);
}
}
if (segmentOutlines.length === 0) {
return [outline];
}
let unioned = [];
try {
unioned = polygonClipping.union(
// @ts-ignore
...segmentOutlines.map((segment) => [normalizeUnionRing(segment)]),
) as [number, number][][][];
} catch {
return [outline];
}
if (unioned.length === 0) {
return [outline];
}
const result: [number, number][][] = [];
for (const polygon of unioned) {
if (polygon.length === 0) {
continue;
}
for (const ring of polygon) {
if (ring.length === 0) {
continue;
}
result.push(closeUnionRing(ring as LocalPoint[]) as [number, number][]);
}
}
return result.length > 0 ? result : [outline];
};

View File

@@ -1,4 +1,9 @@
import { invariant, isDevEnv, isTestEnv } from "@excalidraw/common";
import {
invariant,
isDevEnv,
isTestEnv,
type Bounds,
} from "@excalidraw/common";
import {
pointFrom,
@@ -19,7 +24,7 @@ import type {
Vector,
} from "@excalidraw/math";
import { getCenterForBounds, type Bounds } from "./bounds";
import { getCenterForBounds } from "./bounds";
import type { ExcalidrawBindableElement } from "./types";

View File

@@ -71,7 +71,6 @@ export * from "./elementLink";
export * from "./embeddable";
export * from "./flowchart";
export * from "./fractionalIndex";
export * from "./freedraw";
export * from "./frame";
export * from "./groups";
export * from "./heading";

View File

@@ -42,6 +42,7 @@ import type {
NullableGridSize,
Zoom,
} from "@excalidraw/excalidraw/types";
import type { Bounds } from "@excalidraw/common";
import {
calculateFixedPointForNonElbowArrowBinding,
@@ -68,7 +69,6 @@ import { isLineElement } from "./typeChecks";
import type { Scene } from "./Scene";
import type { Bounds } from "./bounds";
import type {
NonDeleted,
ExcalidrawLinearElement,

View File

@@ -1,4 +1,5 @@
import rough from "roughjs/bin/rough";
import { getStroke } from "perfect-freehand";
import {
type GlobalPoint,
@@ -62,7 +63,6 @@ import {
} from "./typeChecks";
import { getContainingFrame } from "./frame";
import { getCornerRadius } from "./utils";
import { getFreedrawOutlinePolygons } from "./freedraw";
import { ShapeCache } from "./shape";
@@ -78,6 +78,7 @@ import type {
ElementsMap,
} from "./types";
import type { StrokeOptions } from "perfect-freehand";
import type { RoughCanvas } from "roughjs/bin/canvas";
// using a stronger invert (100% vs our regular 93%) and saturate
@@ -87,18 +88,6 @@ import type { RoughCanvas } from "roughjs/bin/canvas";
export const IMAGE_INVERT_FILTER =
"invert(100%) hue-rotate(180deg) saturate(1.25)";
const DEBUG_FREEDRAW_COLORS = [
"#e53935",
"#8e24aa",
"#3949ab",
"#1e88e5",
"#00897b",
"#43a047",
"#fdd835",
"#fb8c00",
"#6d4c41",
];
const isPendingImageElement = (
element: ExcalidrawElement,
renderConfig: StaticCanvasRenderConfig,
@@ -450,19 +439,15 @@ const drawElementOnCanvas = (
context.save();
context.fillStyle = element.strokeColor;
const polygons = getFreedrawOutlinePolygons(element);
const path = getFreeDrawPath2D(element) as Path2D;
const fillShape = ShapeCache.get(element);
if (fillShape) {
rc.draw(fillShape);
}
context.fillStyle = element.strokeColor;
const path =
getFreeDrawPath2D(element) ??
(generateFreeDrawShape(element) as Path2D);
context.fill(path, "evenodd");
context.fill(path);
context.restore();
break;
@@ -1055,10 +1040,7 @@ export function getFreeDrawPath2D(element: ExcalidrawFreeDrawElement) {
}
export function getFreeDrawSvgPath(element: ExcalidrawFreeDrawElement) {
return getFreedrawOutlinePolygons(element)
.map((polygon) => getSvgPathFromStroke(polygon))
.filter((path) => path.length > 0)
.join(" ");
return getSvgPathFromStroke(getFreedrawOutlinePoints(element));
}
export function getFreedrawOutlineAsSegments(
@@ -1117,6 +1099,28 @@ export function getFreedrawOutlineAsSegments(
);
}
export function getFreedrawOutlinePoints(element: ExcalidrawFreeDrawElement) {
// If input points are empty (should they ever be?) return a dot
const inputPoints = element.simulatePressure
? element.points
: element.points.length
? element.points.map(([x, y], i) => [x, y, element.pressures[i]])
: [[0, 0, 0.5]];
// Consider changing the options for simulated pressure vs real pressure
const options: StrokeOptions = {
simulatePressure: element.simulatePressure,
size: element.strokeWidth * 4.25,
thinning: 0.6,
smoothing: 0.5,
streamline: 0.5,
easing: (t) => Math.sin((t * Math.PI) / 2), // https://easings.net/#easeOutSine
last: true,
};
return getStroke(inputPoints as number[][], options) as [number, number][];
}
function med(A: number[], B: number[]) {
return [(A[0] + B[0]) / 2, (A[1] + B[1]) / 2];
}

View File

@@ -13,6 +13,7 @@ import {
import type { GlobalPoint, LineSegment, LocalPoint } from "@excalidraw/math";
import type { AppState, Zoom } from "@excalidraw/excalidraw/types";
import type { Bounds } from "@excalidraw/common";
import { getElementAbsoluteCoords } from "./bounds";
import {
@@ -23,7 +24,6 @@ import {
} from "./transformHandles";
import { isImageElement, isLinearElement } from "./typeChecks";
import type { Bounds } from "./bounds";
import type {
TransformHandleType,
TransformHandle,

View File

@@ -11,6 +11,7 @@ import type {
InteractiveCanvasAppState,
Zoom,
} from "@excalidraw/excalidraw/types";
import type { Bounds } from "@excalidraw/common";
import { getElementAbsoluteCoords } from "./bounds";
import {
@@ -20,7 +21,6 @@ import {
isLinearElement,
} from "./typeChecks";
import type { Bounds } from "./bounds";
import type {
ElementsMap,
ExcalidrawElement,

View File

@@ -6,7 +6,6 @@ import type { ElementOrToolType } from "@excalidraw/excalidraw/types";
import type { MarkNonNullable } from "@excalidraw/common/utility-types";
import type { Bounds } from "./bounds";
import type {
ExcalidrawElement,
ExcalidrawTextElement,
@@ -356,15 +355,6 @@ export const getDefaultRoundnessTypeForElement = (
return null;
};
// TODO: Move this to @excalidraw/math
export const isBounds = (box: unknown): box is Bounds =>
Array.isArray(box) &&
box.length === 4 &&
typeof box[0] === "number" &&
typeof box[1] === "number" &&
typeof box[2] === "number" &&
typeof box[3] === "number";
export const getLinearElementSubType = (
element: ExcalidrawLinearElement,
): ExcalidrawLinearElementSubType => {

View File

@@ -2,6 +2,7 @@ import { pointFrom } from "@excalidraw/math";
import { Excalidraw } from "@excalidraw/excalidraw";
import {
type Bounds,
KEYS,
getSizeFromPoints,
reseed,
@@ -22,7 +23,6 @@ import { resizeSingleElement } from "../src/resizeElements";
import { LinearElementEditor } from "../src/linearElementEditor";
import { getElementPointsCoords } from "../src/bounds";
import type { Bounds } from "../src/bounds";
import type {
ExcalidrawElbowArrowElement,
ExcalidrawFreeDrawElement,

View File

@@ -113,7 +113,6 @@ export const createPasteEvent = ({
if (typeof value !== "string") {
files = files || [];
files.push(value);
event.clipboardData?.items.add(value);
continue;
}
try {

View File

@@ -6,7 +6,7 @@ import { hitElementBoundingBox } from "@excalidraw/element";
import type { GlobalPoint, Radians } from "@excalidraw/math";
import type { Bounds } from "@excalidraw/element";
import type { Bounds } from "@excalidraw/common";
import type {
ElementsMap,
NonDeletedExcalidrawElement,

View File

@@ -28,7 +28,7 @@ import { shouldTestInside } from "@excalidraw/element";
import { hasBoundTextElement, isBoundToContainer } from "@excalidraw/element";
import { getBoundTextElementId } from "@excalidraw/element";
import type { Bounds } from "@excalidraw/element";
import type { Bounds } from "@excalidraw/common";
import type { GlobalPoint, LineSegment } from "@excalidraw/math/types";
import type { ElementsMap, ExcalidrawElement } from "@excalidraw/element/types";

View File

@@ -6,8 +6,9 @@ import {
polygonIncludesPointNonZero,
} from "@excalidraw/math";
import { type Bounds } from "@excalidraw/common";
import {
type Bounds,
computeBoundTextPosition,
doBoundsIntersect,
getBoundTextElement,

View File

@@ -398,7 +398,6 @@ const renderElementToSvg = (
node.setAttribute("stroke", "none");
const path = svgRoot.ownerDocument!.createElementNS(SVG_NS, "path");
path.setAttribute("fill", element.strokeColor);
path.setAttribute("fill-rule", "evenodd");
path.setAttribute("d", getFreeDrawSvgPath(element));
node.appendChild(path);

View File

@@ -39,7 +39,7 @@ import { type Mutable } from "@excalidraw/common/utility-types";
import { newTextElement } from "@excalidraw/element";
import type { Bounds } from "@excalidraw/element";
import type { Bounds } from "@excalidraw/common";
import type {
ExcalidrawElement,

View File

@@ -24,7 +24,7 @@ import {
import type { InclusiveRange } from "@excalidraw/math";
import type { Bounds } from "@excalidraw/element";
import type { Bounds } from "@excalidraw/common";
import type { MaybeTransformHandleType } from "@excalidraw/element";
import type {
ElementsMap,

View File

@@ -1,4 +1,4 @@
import type { GlobalPoint, LocalPoint, Radians, Vector } from "./types";
import type { GlobalPoint, LocalPoint, Vector } from "./types";
/**
* Create a vector from the x and y coordiante elements.
@@ -158,21 +158,3 @@ export const vectorNormalize = (v: Vector): Vector => {
* Calculate the right-hand normal of the vector.
*/
export const vectorNormal = (v: Vector): Vector => vector(v[1], -v[0]);
/**
* Calculate the left-hand normal of the vector.
*/
export const vectorAntiNormal = (v: Vector): Vector => vector(-v[1], v[0]);
/**
*
* @param v The vector to rotate
* @param angle The angle to rotate the vector by in radians
* @returns The rotated vector
*/
export const vectorRotate = (v: Vector, angle: Radians): Vector => {
const cos = Math.cos(angle);
const sin = Math.sin(angle);
return vector(v[0] * cos - v[1] * sin, v[0] * sin + v[1] * cos);
};

View File

@@ -5,7 +5,7 @@ import {
type LocalPoint,
} from "@excalidraw/math";
import type { Bounds } from "@excalidraw/element";
import type { Bounds } from "@excalidraw/common";
export type LineSegment<P extends LocalPoint | GlobalPoint> = [P, P];

View File

@@ -1,4 +1,4 @@
import { arrayToMap } from "@excalidraw/common";
import { arrayToMap, type Bounds } from "@excalidraw/common";
import { getElementBounds } from "@excalidraw/element";
import {
isArrowElement,
@@ -14,7 +14,6 @@ import {
rangeInclusive,
} from "@excalidraw/math";
import type { Bounds } from "@excalidraw/element";
import type {
ExcalidrawElement,
ExcalidrawFreeDrawElement,

View File

@@ -1,6 +1,6 @@
import { API } from "@excalidraw/excalidraw/tests/helpers/api";
import type { Bounds } from "@excalidraw/element";
import type { Bounds } from "@excalidraw/common";
import {
elementPartiallyOverlapsWithOrContainsBBox,

View File

@@ -7877,14 +7877,6 @@ points-on-path@^0.2.1:
path-data-parser "0.1.0"
points-on-curve "0.2.0"
polygon-clipping@0.15.7:
version "0.15.7"
resolved "https://registry.yarnpkg.com/polygon-clipping/-/polygon-clipping-0.15.7.tgz#3823ca1e372566f350795ce9dd9a7b19e97bdaad"
integrity sha512-nhfdr83ECBg6xtqOAJab1tbksbBAOMUltN60bU+llHVOL0e5Onm1WpAXXWXVB39L8AJFssoIhEVuy/S90MmotA==
dependencies:
robust-predicates "^3.0.2"
splaytree "^3.1.0"
portfinder@^1.0.28:
version "1.0.33"
resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.33.tgz#03dbc51455aa8f83ad9fb86af8345e063bb51101"
@@ -8764,11 +8756,6 @@ sourcemap-codec@^1.4.8:
resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4"
integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==
splaytree@^3.1.0:
version "3.1.2"
resolved "https://registry.yarnpkg.com/splaytree/-/splaytree-3.1.2.tgz#d1db2691665a3c69d630de98d55145a6546dc166"
integrity sha512-4OM2BJgC5UzrhVnnJA4BkHKGtjXNzzUfpQjCO8I05xYPsfS/VuQDwjCGGMi8rYQilHEV4j8NBqTFbls/PZEE7A==
split.js@^1.6.0:
version "1.6.5"
resolved "https://registry.yarnpkg.com/split.js/-/split.js-1.6.5.tgz#f7f61da1044c9984cb42947df4de4fadb5a3f300"