mirror of
https://github.com/excalidraw/excalidraw.git
synced 2025-12-10 14:35:11 +01:00
Compare commits
4 Commits
fix/mobile
...
feat/bette
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a9f57f0fc1 | ||
|
|
4a67c3e9b7 | ||
|
|
fdb8aaf44e | ||
|
|
6f4081e371 |
@@ -1,5 +1,3 @@
|
||||
import mobile from "is-mobile";
|
||||
|
||||
export type StylesPanelMode = "compact" | "full" | "mobile";
|
||||
|
||||
export type EditorInterface = Readonly<{
|
||||
@@ -141,13 +139,12 @@ export const getFormFactor = (
|
||||
editorWidth: number,
|
||||
editorHeight: number,
|
||||
): EditorInterface["formFactor"] => {
|
||||
if (mobile()) {
|
||||
if (isMobileBreakpoint(editorWidth, editorHeight)) {
|
||||
return "phone";
|
||||
} else if (mobile({ tablet: true })) {
|
||||
}
|
||||
|
||||
if (isTabletBreakpoint(editorWidth, editorHeight)) {
|
||||
return "tablet";
|
||||
} else if (isMobileBreakpoint(editorWidth, editorHeight)) {
|
||||
// NOTE: Very small editor sizes should be treated as phone
|
||||
return "phone";
|
||||
}
|
||||
|
||||
return "desktop";
|
||||
|
||||
255
packages/element/src/freedraw.ts
Normal file
255
packages/element/src/freedraw.ts
Normal file
@@ -0,0 +1,255 @@
|
||||
import {
|
||||
type GlobalPoint,
|
||||
type LineSegment,
|
||||
lineSegment,
|
||||
lineSegmentIntersectionPoints,
|
||||
type LocalPoint,
|
||||
pointDistanceSq,
|
||||
pointFrom,
|
||||
pointFromVector,
|
||||
vectorAntiNormal,
|
||||
vectorFromPoint,
|
||||
vectorNormal,
|
||||
vectorNormalize,
|
||||
vectorScale,
|
||||
} from "@excalidraw/math";
|
||||
import { debugDrawLine } from "@excalidraw/common";
|
||||
|
||||
import { type ExcalidrawFreeDrawElement } from "./types";
|
||||
|
||||
const offset = (
|
||||
x: number,
|
||||
y: number,
|
||||
pressure: number,
|
||||
direction: "left" | "right",
|
||||
origin: LocalPoint,
|
||||
) => {
|
||||
const p = pointFrom<LocalPoint>(x, y);
|
||||
const v = vectorNormalize(vectorFromPoint(p, origin));
|
||||
const normal = direction === "left" ? vectorNormal(v) : vectorAntiNormal(v);
|
||||
const scaled = vectorScale(normal, pressure / 2);
|
||||
|
||||
return pointFromVector(scaled, origin);
|
||||
};
|
||||
|
||||
function generateSegments(
|
||||
input:
|
||||
| readonly [x: number, y: number, pressure: number][]
|
||||
| readonly [x: number, y: number][],
|
||||
element: ExcalidrawFreeDrawElement,
|
||||
pressureMultiplier: number = 1,
|
||||
minimumPressure: number = 1,
|
||||
): LineSegment<LocalPoint>[] {
|
||||
if (input.length < 3) {
|
||||
return [];
|
||||
}
|
||||
|
||||
let idx = 0;
|
||||
const segments = Array(input.length * 4 - 4);
|
||||
|
||||
segments[idx++] = lineSegment(
|
||||
offset(
|
||||
input[1][0],
|
||||
input[1][1],
|
||||
Math.max((input[1][2] ?? 5) * pressureMultiplier, minimumPressure),
|
||||
"left",
|
||||
pointFrom<LocalPoint>(input[0][0], input[0][1]),
|
||||
),
|
||||
offset(
|
||||
input[0][0],
|
||||
input[0][1],
|
||||
Math.max((input[1][2] ?? 5) * pressureMultiplier, minimumPressure),
|
||||
"right",
|
||||
pointFrom<LocalPoint>(input[1][0], input[1][1]),
|
||||
),
|
||||
);
|
||||
|
||||
for (let i = 2; i < input.length; i++) {
|
||||
const a = segments[idx - 1][1];
|
||||
const b = offset(
|
||||
input[i][0],
|
||||
input[i][1],
|
||||
Math.max((input[1][2] ?? 5) * pressureMultiplier, minimumPressure),
|
||||
"left",
|
||||
pointFrom<LocalPoint>(input[i - 1][0], input[i - 1][1]),
|
||||
);
|
||||
const c = offset(
|
||||
input[i - 1][0],
|
||||
input[i - 1][1],
|
||||
Math.max((input[1][2] ?? 5) * pressureMultiplier, minimumPressure),
|
||||
"right",
|
||||
pointFrom<LocalPoint>(input[i][0], input[i][1]),
|
||||
);
|
||||
|
||||
segments[idx++] = lineSegment(a, b); // Bridge segment
|
||||
segments[idx++] = lineSegment(b, c); // Main segment
|
||||
}
|
||||
|
||||
// Turnaround segments
|
||||
const prev = segments[idx - 1][1];
|
||||
segments[idx++] = lineSegment(
|
||||
prev,
|
||||
pointFrom<LocalPoint>(
|
||||
input[input.length - 1][0],
|
||||
input[input.length - 1][1],
|
||||
),
|
||||
);
|
||||
segments[idx++] = lineSegment(
|
||||
pointFrom<LocalPoint>(
|
||||
input[input.length - 1][0],
|
||||
input[input.length - 1][1],
|
||||
),
|
||||
offset(
|
||||
input[input.length - 2][0],
|
||||
input[input.length - 2][1],
|
||||
Math.max((input[1][2] ?? 5) * pressureMultiplier, minimumPressure),
|
||||
"left",
|
||||
pointFrom<LocalPoint>(
|
||||
input[input.length - 1][0],
|
||||
input[input.length - 1][1],
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
for (let i = input.length - 2; i > 0; i--) {
|
||||
const a = segments[idx - 1][1];
|
||||
const b = offset(
|
||||
input[i + 1][0],
|
||||
input[i + 1][1],
|
||||
Math.max((input[1][2] ?? 5) * pressureMultiplier, minimumPressure),
|
||||
"right",
|
||||
pointFrom<LocalPoint>(input[i][0], input[i][1]),
|
||||
);
|
||||
const c = offset(
|
||||
input[i - 1][0],
|
||||
input[i - 1][1],
|
||||
Math.max((input[1][2] ?? 5) * pressureMultiplier, minimumPressure),
|
||||
"left",
|
||||
pointFrom<LocalPoint>(input[i][0], input[i][1]),
|
||||
);
|
||||
|
||||
segments[idx++] = lineSegment(a, b); // Main segment
|
||||
segments[idx++] = lineSegment(b, c); // Bridge segment
|
||||
}
|
||||
|
||||
const last = segments[idx - 1][1];
|
||||
segments[idx++] = lineSegment(
|
||||
last,
|
||||
offset(
|
||||
input[1][0],
|
||||
input[1][1],
|
||||
Math.max((input[1][2] ?? 5) * pressureMultiplier, minimumPressure),
|
||||
"right",
|
||||
pointFrom<LocalPoint>(input[0][0], input[0][1]),
|
||||
),
|
||||
);
|
||||
|
||||
// Closing cap
|
||||
segments[idx++] = lineSegment(
|
||||
segments[idx - 2][1],
|
||||
pointFrom<LocalPoint>(input[0][0], input[0][1]),
|
||||
);
|
||||
segments[idx++] = lineSegment(
|
||||
pointFrom<LocalPoint>(input[0][0], input[0][1]),
|
||||
segments[0][0],
|
||||
);
|
||||
|
||||
return segments;
|
||||
}
|
||||
|
||||
export function getStroke(
|
||||
input:
|
||||
| readonly [x: number, y: number, pressure: number][]
|
||||
| readonly [x: number, y: number][],
|
||||
options: any,
|
||||
element: ExcalidrawFreeDrawElement,
|
||||
): LocalPoint[] {
|
||||
const segments: (LineSegment<LocalPoint> | undefined)[] = generateSegments(
|
||||
input,
|
||||
element,
|
||||
);
|
||||
|
||||
const MIN_DIST_SQ = 0.2 ** 2;
|
||||
for (let j = 0; j < segments.length; j++) {
|
||||
for (let i = j + 1; i < segments.length; i++) {
|
||||
const a = segments[j];
|
||||
const b = segments[i];
|
||||
if (!a || !b) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const intersection = lineSegmentIntersectionPoints(a, b);
|
||||
|
||||
if (
|
||||
intersection &&
|
||||
pointDistanceSq(a[0], intersection) > MIN_DIST_SQ &&
|
||||
pointDistanceSq(a[1], intersection) > MIN_DIST_SQ &&
|
||||
i === j + 2
|
||||
) {
|
||||
a[1] = intersection;
|
||||
segments[j + 1] = undefined;
|
||||
b[0] = intersection;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
debugSegments(
|
||||
segments.filter((s): s is LineSegment<LocalPoint> => !!s),
|
||||
input,
|
||||
element,
|
||||
);
|
||||
|
||||
return [
|
||||
...(segments[0] ? [segments[0][0]] : []),
|
||||
...segments
|
||||
.filter((s): s is LineSegment<LocalPoint> => !!s)
|
||||
.map((s) => s[1]),
|
||||
];
|
||||
}
|
||||
|
||||
function debugSegments(
|
||||
segments: LineSegment<LocalPoint>[],
|
||||
input: readonly [number, number, number][] | readonly [number, number][],
|
||||
element: ExcalidrawFreeDrawElement,
|
||||
): void {
|
||||
const colors = [
|
||||
"#FF0000",
|
||||
"#00FF00",
|
||||
"#0000FF",
|
||||
// "#FFFF00",
|
||||
// "#00FFFF",
|
||||
// "#FF00FF",
|
||||
// "#C0C0C0",
|
||||
// "#800000",
|
||||
// "#808000",
|
||||
// "#008000",
|
||||
// "#800080",
|
||||
// "#008080",
|
||||
// "#000080",
|
||||
];
|
||||
segments.forEach((s, i) => {
|
||||
debugDrawLine(
|
||||
lineSegment(
|
||||
pointFrom<GlobalPoint>(element.x + s[0][0], element.y + s[0][1]),
|
||||
pointFrom<GlobalPoint>(element.x + s[1][0], element.y + s[1][1]),
|
||||
),
|
||||
{ color: colors[i % colors.length], permanent: true },
|
||||
);
|
||||
});
|
||||
input.forEach((p, i) => {
|
||||
if (i === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
debugDrawLine(
|
||||
lineSegment(
|
||||
pointFrom<GlobalPoint>(
|
||||
element.x + input[i - 1][0],
|
||||
element.y + input[i - 1][1],
|
||||
),
|
||||
pointFrom<GlobalPoint>(element.x + p[0], element.y + p[1]),
|
||||
),
|
||||
{ color: "#000000", permanent: true },
|
||||
);
|
||||
});
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import rough from "roughjs/bin/rough";
|
||||
import { getStroke } from "perfect-freehand";
|
||||
//import { getStroke } from "perfect-freehand";
|
||||
|
||||
import {
|
||||
type GlobalPoint,
|
||||
@@ -63,8 +63,8 @@ import {
|
||||
} from "./typeChecks";
|
||||
import { getContainingFrame } from "./frame";
|
||||
import { getCornerRadius } from "./utils";
|
||||
|
||||
import { ShapeCache } from "./shape";
|
||||
import { getStroke } from "./freedraw";
|
||||
|
||||
import type {
|
||||
ExcalidrawElement,
|
||||
@@ -1102,10 +1102,10 @@ 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]];
|
||||
? (element.points as readonly [number, number][])
|
||||
: ((element.points.length
|
||||
? element.points.map(([x, y], i) => [x, y, element.pressures[i]])
|
||||
: [[0, 0, 0.5]]) as [number, number, number][]);
|
||||
|
||||
// Consider changing the options for simulated pressure vs real pressure
|
||||
const options: StrokeOptions = {
|
||||
@@ -1118,7 +1118,16 @@ export function getFreedrawOutlinePoints(element: ExcalidrawFreeDrawElement) {
|
||||
last: true,
|
||||
};
|
||||
|
||||
return getStroke(inputPoints as number[][], options) as [number, number][];
|
||||
// return getStroke(
|
||||
// [
|
||||
// [0, 0],
|
||||
// [30, -30],
|
||||
// [60, -30],
|
||||
// ],
|
||||
// options,
|
||||
// element,
|
||||
// );
|
||||
return getStroke(inputPoints, options, element) as [number, number][];
|
||||
}
|
||||
|
||||
function med(A: number[], B: number[]) {
|
||||
|
||||
@@ -493,7 +493,6 @@ describe("binding for simple arrows", () => {
|
||||
expect(arrow.endBinding?.elementId).toBe(rectRight.id);
|
||||
|
||||
mouse.downAt(-100, -100);
|
||||
mouse.moveTo(0, 0);
|
||||
mouse.moveTo(650, 750);
|
||||
mouse.up(0, 0);
|
||||
|
||||
|
||||
@@ -11463,10 +11463,13 @@ class App extends React.Component<AppProps, AppState> {
|
||||
): void => {
|
||||
const selectionElement = this.state.selectionElement;
|
||||
const pointerCoords = pointerDownState.lastCoords;
|
||||
const selectedElements = this.scene.getSelectedElements(this.state);
|
||||
const onlyBindingElementSelected =
|
||||
selectedElements?.length === 1 && isBindingElement(selectedElements[0]);
|
||||
if (
|
||||
selectionElement &&
|
||||
pointerDownState.boxSelection.hasOccurred &&
|
||||
this.state.activeTool.type !== "eraser"
|
||||
this.state.activeTool.type !== "eraser" &&
|
||||
!onlyBindingElementSelected
|
||||
) {
|
||||
dragNewElement({
|
||||
newElement: selectionElement,
|
||||
|
||||
@@ -95,7 +95,6 @@
|
||||
"fractional-indexing": "3.2.0",
|
||||
"fuzzy": "0.1.3",
|
||||
"image-blob-reduce": "3.0.1",
|
||||
"is-mobile": "5.0.0",
|
||||
"jotai": "2.11.0",
|
||||
"jotai-scope": "0.7.2",
|
||||
"lodash.debounce": "4.0.8",
|
||||
|
||||
@@ -1223,7 +1223,7 @@ exports[`contextMenu element > selecting 'Add to library' in context menu adds e
|
||||
"type": "rectangle",
|
||||
"updated": 1,
|
||||
"version": 3,
|
||||
"versionNonce": 401146281,
|
||||
"versionNonce": 2019559783,
|
||||
"width": 10,
|
||||
"x": -20,
|
||||
"y": -10,
|
||||
@@ -1431,14 +1431,14 @@ exports[`contextMenu element > selecting 'Bring forward' in context menu brings
|
||||
"opacity": 100,
|
||||
"roughness": 1,
|
||||
"roundness": null,
|
||||
"seed": 1014066025,
|
||||
"seed": 238820263,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"strokeStyle": "solid",
|
||||
"strokeWidth": 2,
|
||||
"type": "rectangle",
|
||||
"updated": 1,
|
||||
"version": 3,
|
||||
"versionNonce": 1604849351,
|
||||
"versionNonce": 1505387817,
|
||||
"width": 20,
|
||||
"x": 20,
|
||||
"y": 30,
|
||||
@@ -1463,14 +1463,14 @@ exports[`contextMenu element > selecting 'Bring forward' in context menu brings
|
||||
"opacity": 100,
|
||||
"roughness": 1,
|
||||
"roundness": null,
|
||||
"seed": 1278240551,
|
||||
"seed": 449462985,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"strokeStyle": "solid",
|
||||
"strokeWidth": 2,
|
||||
"type": "rectangle",
|
||||
"updated": 1,
|
||||
"version": 4,
|
||||
"versionNonce": 493213705,
|
||||
"versionNonce": 915032327,
|
||||
"width": 20,
|
||||
"x": -10,
|
||||
"y": 0,
|
||||
@@ -1765,14 +1765,14 @@ exports[`contextMenu element > selecting 'Bring to front' in context menu brings
|
||||
"opacity": 100,
|
||||
"roughness": 1,
|
||||
"roundness": null,
|
||||
"seed": 1014066025,
|
||||
"seed": 238820263,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"strokeStyle": "solid",
|
||||
"strokeWidth": 2,
|
||||
"type": "rectangle",
|
||||
"updated": 1,
|
||||
"version": 3,
|
||||
"versionNonce": 1604849351,
|
||||
"versionNonce": 1505387817,
|
||||
"width": 20,
|
||||
"x": 20,
|
||||
"y": 30,
|
||||
@@ -1797,14 +1797,14 @@ exports[`contextMenu element > selecting 'Bring to front' in context menu brings
|
||||
"opacity": 100,
|
||||
"roughness": 1,
|
||||
"roundness": null,
|
||||
"seed": 1278240551,
|
||||
"seed": 449462985,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"strokeStyle": "solid",
|
||||
"strokeWidth": 2,
|
||||
"type": "rectangle",
|
||||
"updated": 1,
|
||||
"version": 4,
|
||||
"versionNonce": 493213705,
|
||||
"versionNonce": 915032327,
|
||||
"width": 20,
|
||||
"x": -10,
|
||||
"y": 0,
|
||||
@@ -2108,7 +2108,7 @@ exports[`contextMenu element > selecting 'Copy styles' in context menu copies st
|
||||
"type": "rectangle",
|
||||
"updated": 1,
|
||||
"version": 3,
|
||||
"versionNonce": 401146281,
|
||||
"versionNonce": 2019559783,
|
||||
"width": 10,
|
||||
"x": -20,
|
||||
"y": -10,
|
||||
@@ -2321,7 +2321,7 @@ exports[`contextMenu element > selecting 'Delete' in context menu deletes elemen
|
||||
"type": "rectangle",
|
||||
"updated": 1,
|
||||
"version": 4,
|
||||
"versionNonce": 1116226695,
|
||||
"versionNonce": 1014066025,
|
||||
"width": 10,
|
||||
"x": -20,
|
||||
"y": -10,
|
||||
@@ -2567,7 +2567,7 @@ exports[`contextMenu element > selecting 'Duplicate' in context menu duplicates
|
||||
"type": "rectangle",
|
||||
"updated": 1,
|
||||
"version": 3,
|
||||
"versionNonce": 401146281,
|
||||
"versionNonce": 2019559783,
|
||||
"width": 10,
|
||||
"x": -20,
|
||||
"y": -10,
|
||||
@@ -2592,14 +2592,14 @@ exports[`contextMenu element > selecting 'Duplicate' in context menu duplicates
|
||||
"opacity": 100,
|
||||
"roughness": 1,
|
||||
"roundness": null,
|
||||
"seed": 1014066025,
|
||||
"seed": 238820263,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"strokeStyle": "solid",
|
||||
"strokeWidth": 2,
|
||||
"type": "rectangle",
|
||||
"updated": 1,
|
||||
"version": 5,
|
||||
"versionNonce": 400692809,
|
||||
"versionNonce": 1604849351,
|
||||
"width": 10,
|
||||
"x": -10,
|
||||
"y": 0,
|
||||
@@ -2868,14 +2868,14 @@ exports[`contextMenu element > selecting 'Group selection' in context menu group
|
||||
"opacity": 100,
|
||||
"roughness": 1,
|
||||
"roundness": null,
|
||||
"seed": 1278240551,
|
||||
"seed": 449462985,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"strokeStyle": "solid",
|
||||
"strokeWidth": 2,
|
||||
"type": "rectangle",
|
||||
"updated": 1,
|
||||
"version": 4,
|
||||
"versionNonce": 915032327,
|
||||
"versionNonce": 81784553,
|
||||
"width": 20,
|
||||
"x": -10,
|
||||
"y": 0,
|
||||
@@ -2902,14 +2902,14 @@ exports[`contextMenu element > selecting 'Group selection' in context menu group
|
||||
"opacity": 100,
|
||||
"roughness": 1,
|
||||
"roundness": null,
|
||||
"seed": 1014066025,
|
||||
"seed": 238820263,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"strokeStyle": "solid",
|
||||
"strokeWidth": 2,
|
||||
"type": "rectangle",
|
||||
"updated": 1,
|
||||
"version": 4,
|
||||
"versionNonce": 81784553,
|
||||
"versionNonce": 747212839,
|
||||
"width": 20,
|
||||
"x": 20,
|
||||
"y": 30,
|
||||
@@ -3238,14 +3238,14 @@ exports[`contextMenu element > selecting 'Paste styles' in context menu pastes s
|
||||
"opacity": 60,
|
||||
"roughness": 2,
|
||||
"roundness": null,
|
||||
"seed": 1278240551,
|
||||
"seed": 449462985,
|
||||
"strokeColor": "#e03131",
|
||||
"strokeStyle": "dotted",
|
||||
"strokeWidth": 2,
|
||||
"type": "rectangle",
|
||||
"updated": 1,
|
||||
"version": 4,
|
||||
"versionNonce": 1402203177,
|
||||
"versionNonce": 1359939303,
|
||||
"width": 20,
|
||||
"x": -10,
|
||||
"y": 0,
|
||||
@@ -3270,14 +3270,14 @@ exports[`contextMenu element > selecting 'Paste styles' in context menu pastes s
|
||||
"opacity": 60,
|
||||
"roughness": 2,
|
||||
"roundness": null,
|
||||
"seed": 1898319239,
|
||||
"seed": 640725609,
|
||||
"strokeColor": "#e03131",
|
||||
"strokeStyle": "dotted",
|
||||
"strokeWidth": 2,
|
||||
"type": "rectangle",
|
||||
"updated": 1,
|
||||
"version": 9,
|
||||
"versionNonce": 941653321,
|
||||
"versionNonce": 908564423,
|
||||
"width": 20,
|
||||
"x": 20,
|
||||
"y": 30,
|
||||
@@ -3732,14 +3732,14 @@ exports[`contextMenu element > selecting 'Send backward' in context menu sends e
|
||||
"opacity": 100,
|
||||
"roughness": 1,
|
||||
"roundness": null,
|
||||
"seed": 238820263,
|
||||
"seed": 400692809,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"strokeStyle": "solid",
|
||||
"strokeWidth": 2,
|
||||
"type": "rectangle",
|
||||
"updated": 1,
|
||||
"version": 4,
|
||||
"versionNonce": 915032327,
|
||||
"versionNonce": 81784553,
|
||||
"width": 20,
|
||||
"x": 20,
|
||||
"y": 30,
|
||||
@@ -3764,14 +3764,14 @@ exports[`contextMenu element > selecting 'Send backward' in context menu sends e
|
||||
"opacity": 100,
|
||||
"roughness": 1,
|
||||
"roundness": null,
|
||||
"seed": 1278240551,
|
||||
"seed": 449462985,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"strokeStyle": "solid",
|
||||
"strokeWidth": 2,
|
||||
"type": "rectangle",
|
||||
"updated": 1,
|
||||
"version": 3,
|
||||
"versionNonce": 2019559783,
|
||||
"versionNonce": 1150084233,
|
||||
"width": 20,
|
||||
"x": -10,
|
||||
"y": 0,
|
||||
@@ -4058,14 +4058,14 @@ exports[`contextMenu element > selecting 'Send to back' in context menu sends el
|
||||
"opacity": 100,
|
||||
"roughness": 1,
|
||||
"roundness": null,
|
||||
"seed": 1014066025,
|
||||
"seed": 238820263,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"strokeStyle": "solid",
|
||||
"strokeWidth": 2,
|
||||
"type": "rectangle",
|
||||
"updated": 1,
|
||||
"version": 4,
|
||||
"versionNonce": 493213705,
|
||||
"versionNonce": 915032327,
|
||||
"width": 20,
|
||||
"x": 20,
|
||||
"y": 30,
|
||||
@@ -4090,14 +4090,14 @@ exports[`contextMenu element > selecting 'Send to back' in context menu sends el
|
||||
"opacity": 100,
|
||||
"roughness": 1,
|
||||
"roundness": null,
|
||||
"seed": 1278240551,
|
||||
"seed": 449462985,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"strokeStyle": "solid",
|
||||
"strokeWidth": 2,
|
||||
"type": "rectangle",
|
||||
"updated": 1,
|
||||
"version": 3,
|
||||
"versionNonce": 2019559783,
|
||||
"versionNonce": 1150084233,
|
||||
"width": 20,
|
||||
"x": -10,
|
||||
"y": 0,
|
||||
@@ -4387,14 +4387,14 @@ exports[`contextMenu element > selecting 'Ungroup selection' in context menu ung
|
||||
"opacity": 100,
|
||||
"roughness": 1,
|
||||
"roundness": null,
|
||||
"seed": 1278240551,
|
||||
"seed": 449462985,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"strokeStyle": "solid",
|
||||
"strokeWidth": 2,
|
||||
"type": "rectangle",
|
||||
"updated": 1,
|
||||
"version": 5,
|
||||
"versionNonce": 760410951,
|
||||
"versionNonce": 1006504105,
|
||||
"width": 20,
|
||||
"x": -10,
|
||||
"y": 0,
|
||||
@@ -4419,14 +4419,14 @@ exports[`contextMenu element > selecting 'Ungroup selection' in context menu ung
|
||||
"opacity": 100,
|
||||
"roughness": 1,
|
||||
"roundness": null,
|
||||
"seed": 238820263,
|
||||
"seed": 400692809,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"strokeStyle": "solid",
|
||||
"strokeWidth": 2,
|
||||
"type": "rectangle",
|
||||
"updated": 1,
|
||||
"version": 5,
|
||||
"versionNonce": 1006504105,
|
||||
"versionNonce": 289600103,
|
||||
"width": 20,
|
||||
"x": 20,
|
||||
"y": 30,
|
||||
@@ -5675,14 +5675,14 @@ exports[`contextMenu element > shows 'Group selection' in context menu for multi
|
||||
"opacity": 100,
|
||||
"roughness": 1,
|
||||
"roundness": null,
|
||||
"seed": 1278240551,
|
||||
"seed": 453191,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"strokeStyle": "solid",
|
||||
"strokeWidth": 2,
|
||||
"type": "rectangle",
|
||||
"updated": 1,
|
||||
"version": 3,
|
||||
"versionNonce": 1150084233,
|
||||
"versionNonce": 1014066025,
|
||||
"width": 10,
|
||||
"x": -10,
|
||||
"y": 0,
|
||||
@@ -5707,14 +5707,14 @@ exports[`contextMenu element > shows 'Group selection' in context menu for multi
|
||||
"opacity": 100,
|
||||
"roughness": 1,
|
||||
"roundness": null,
|
||||
"seed": 400692809,
|
||||
"seed": 1505387817,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"strokeStyle": "solid",
|
||||
"strokeWidth": 2,
|
||||
"type": "rectangle",
|
||||
"updated": 1,
|
||||
"version": 3,
|
||||
"versionNonce": 23633383,
|
||||
"versionNonce": 915032327,
|
||||
"width": 10,
|
||||
"x": 12,
|
||||
"y": 0,
|
||||
@@ -6899,14 +6899,14 @@ exports[`contextMenu element > shows 'Ungroup selection' in context menu for gro
|
||||
"opacity": 100,
|
||||
"roughness": 1,
|
||||
"roundness": null,
|
||||
"seed": 1278240551,
|
||||
"seed": 449462985,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"strokeStyle": "solid",
|
||||
"strokeWidth": 2,
|
||||
"type": "rectangle",
|
||||
"updated": 1,
|
||||
"version": 4,
|
||||
"versionNonce": 747212839,
|
||||
"versionNonce": 1723083209,
|
||||
"width": 10,
|
||||
"x": -10,
|
||||
"y": 0,
|
||||
@@ -6933,14 +6933,14 @@ exports[`contextMenu element > shows 'Ungroup selection' in context menu for gro
|
||||
"opacity": 100,
|
||||
"roughness": 1,
|
||||
"roundness": null,
|
||||
"seed": 238820263,
|
||||
"seed": 400692809,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"strokeStyle": "solid",
|
||||
"strokeWidth": 2,
|
||||
"type": "rectangle",
|
||||
"updated": 1,
|
||||
"version": 4,
|
||||
"versionNonce": 1723083209,
|
||||
"versionNonce": 760410951,
|
||||
"width": 10,
|
||||
"x": 12,
|
||||
"y": 0,
|
||||
@@ -9837,7 +9837,7 @@ exports[`contextMenu element > shows context menu for element > [end of test] el
|
||||
"type": "rectangle",
|
||||
"updated": 1,
|
||||
"version": 3,
|
||||
"versionNonce": 401146281,
|
||||
"versionNonce": 2019559783,
|
||||
"width": 10,
|
||||
"x": -20,
|
||||
"y": -10,
|
||||
|
||||
@@ -13935,7 +13935,7 @@ exports[`regression tests > switches from group of selected elements to another
|
||||
"opacity": 100,
|
||||
"roughness": 1,
|
||||
"roundness": null,
|
||||
"seed": 1006504105,
|
||||
"seed": 289600103,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"strokeStyle": "solid",
|
||||
"strokeWidth": 2,
|
||||
|
||||
@@ -41,8 +41,8 @@ exports[`select single element on the scene > arrow 1`] = `
|
||||
"strokeWidth": 2,
|
||||
"type": "arrow",
|
||||
"updated": 1,
|
||||
"version": 5,
|
||||
"versionNonce": 1116226695,
|
||||
"version": 4,
|
||||
"versionNonce": 2019559783,
|
||||
"width": 30,
|
||||
"x": 10,
|
||||
"y": 10,
|
||||
@@ -88,8 +88,8 @@ exports[`select single element on the scene > arrow escape 1`] = `
|
||||
"strokeWidth": 2,
|
||||
"type": "line",
|
||||
"updated": 1,
|
||||
"version": 5,
|
||||
"versionNonce": 1116226695,
|
||||
"version": 4,
|
||||
"versionNonce": 2019559783,
|
||||
"width": 30,
|
||||
"x": 10,
|
||||
"y": 10,
|
||||
@@ -120,8 +120,8 @@ exports[`select single element on the scene > diamond 1`] = `
|
||||
"strokeWidth": 2,
|
||||
"type": "diamond",
|
||||
"updated": 1,
|
||||
"version": 4,
|
||||
"versionNonce": 2019559783,
|
||||
"version": 3,
|
||||
"versionNonce": 401146281,
|
||||
"width": 30,
|
||||
"x": 10,
|
||||
"y": 10,
|
||||
@@ -152,8 +152,8 @@ exports[`select single element on the scene > ellipse 1`] = `
|
||||
"strokeWidth": 2,
|
||||
"type": "ellipse",
|
||||
"updated": 1,
|
||||
"version": 4,
|
||||
"versionNonce": 2019559783,
|
||||
"version": 3,
|
||||
"versionNonce": 401146281,
|
||||
"width": 30,
|
||||
"x": 10,
|
||||
"y": 10,
|
||||
@@ -184,8 +184,8 @@ exports[`select single element on the scene > rectangle 1`] = `
|
||||
"strokeWidth": 2,
|
||||
"type": "rectangle",
|
||||
"updated": 1,
|
||||
"version": 4,
|
||||
"versionNonce": 2019559783,
|
||||
"version": 3,
|
||||
"versionNonce": 401146281,
|
||||
"width": 30,
|
||||
"x": 10,
|
||||
"y": 10,
|
||||
|
||||
@@ -315,7 +315,6 @@ describe("history", () => {
|
||||
]);
|
||||
|
||||
mouse.downAt(0, 0);
|
||||
mouse.moveTo(25, 25);
|
||||
mouse.moveTo(50, 50);
|
||||
mouse.upAt(50, 50);
|
||||
expect(API.getUndoStack().length).toBe(3);
|
||||
|
||||
@@ -467,7 +467,6 @@ describe("regression tests", () => {
|
||||
|
||||
mouse.reset();
|
||||
mouse.down();
|
||||
mouse.move(-1000, -1000);
|
||||
mouse.restorePosition(...end);
|
||||
mouse.up();
|
||||
|
||||
@@ -518,7 +517,6 @@ describe("regression tests", () => {
|
||||
|
||||
mouse.reset();
|
||||
mouse.down();
|
||||
mouse.move(-1000, -1000);
|
||||
mouse.restorePosition(...end);
|
||||
mouse.up();
|
||||
|
||||
@@ -536,7 +534,6 @@ describe("regression tests", () => {
|
||||
|
||||
mouse.moveTo(-10, -10); // the NW resizing handle is at [0, 0], so moving further
|
||||
mouse.down();
|
||||
mouse.move(-1000, -1000);
|
||||
mouse.restorePosition(...end);
|
||||
mouse.up();
|
||||
|
||||
|
||||
@@ -65,7 +65,6 @@ describe("box-selection", () => {
|
||||
API.setElements([rect1, rect2]);
|
||||
|
||||
mouse.downAt(175, -20);
|
||||
mouse.move(-1000, -1000);
|
||||
mouse.moveTo(85, 70);
|
||||
mouse.up();
|
||||
|
||||
@@ -73,7 +72,6 @@ describe("box-selection", () => {
|
||||
|
||||
Keyboard.withModifierKeys({ shift: true }, () => {
|
||||
mouse.downAt(75, -20);
|
||||
mouse.move(-1000, -1000);
|
||||
mouse.moveTo(-15, 70);
|
||||
mouse.up();
|
||||
});
|
||||
@@ -95,7 +93,6 @@ describe("box-selection", () => {
|
||||
API.setElements([rect1]);
|
||||
|
||||
mouse.downAt(75, -20);
|
||||
mouse.move(-1000, -1000);
|
||||
mouse.moveTo(-15, 70);
|
||||
|
||||
assertSelectedElements([rect1.id]);
|
||||
@@ -141,7 +138,6 @@ describe("inner box-selection", () => {
|
||||
API.setElements([rect1, rect2, rect3]);
|
||||
Keyboard.withModifierKeys({ ctrl: true }, () => {
|
||||
mouse.downAt(40, 40);
|
||||
mouse.move(-1000, -1000);
|
||||
mouse.moveTo(290, 290);
|
||||
mouse.up();
|
||||
|
||||
@@ -179,7 +175,6 @@ describe("inner box-selection", () => {
|
||||
|
||||
Keyboard.withModifierKeys({ ctrl: true }, () => {
|
||||
mouse.downAt(40, 40);
|
||||
mouse.move(-1000, -1000);
|
||||
mouse.moveTo(rect2.x + rect2.width + 10, rect2.y + rect2.height + 10);
|
||||
mouse.up();
|
||||
|
||||
@@ -217,7 +212,6 @@ describe("inner box-selection", () => {
|
||||
API.setElements([rect1, rect2, rect3]);
|
||||
Keyboard.withModifierKeys({ ctrl: true }, () => {
|
||||
mouse.downAt(rect2.x - 20, rect2.y - 20);
|
||||
mouse.move(-1000, -1000);
|
||||
mouse.moveTo(rect2.x + rect2.width + 10, rect2.y + rect2.height + 10);
|
||||
assertSelectedElements([rect2.id, rect3.id]);
|
||||
expect(h.state.selectedGroupIds).toEqual({ A: true });
|
||||
@@ -259,10 +253,9 @@ describe("selection element", () => {
|
||||
|
||||
const canvas = container.querySelector("canvas.interactive")!;
|
||||
fireEvent.pointerDown(canvas, { clientX: 60, clientY: 100 });
|
||||
fireEvent.pointerMove(canvas, { clientX: -1000, clientY: -1000 });
|
||||
fireEvent.pointerMove(canvas, { clientX: 150, clientY: 30 });
|
||||
|
||||
expect(renderInteractiveScene).toHaveBeenCalledTimes(5);
|
||||
expect(renderInteractiveScene).toHaveBeenCalledTimes(4);
|
||||
expect(renderStaticScene).toHaveBeenCalledTimes(3);
|
||||
const selectionElement = h.state.selectionElement!;
|
||||
expect(selectionElement).not.toBeNull();
|
||||
@@ -282,11 +275,10 @@ describe("selection element", () => {
|
||||
|
||||
const canvas = container.querySelector("canvas.interactive")!;
|
||||
fireEvent.pointerDown(canvas, { clientX: 60, clientY: 100 });
|
||||
fireEvent.pointerMove(canvas, { clientX: -1000, clientY: -1000 });
|
||||
fireEvent.pointerMove(canvas, { clientX: 150, clientY: 30 });
|
||||
fireEvent.pointerUp(canvas);
|
||||
|
||||
expect(renderInteractiveScene).toHaveBeenCalledTimes(6);
|
||||
expect(renderInteractiveScene).toHaveBeenCalledTimes(5);
|
||||
expect(renderStaticScene).toHaveBeenCalledTimes(3);
|
||||
expect(h.state.selectionElement).toBeNull();
|
||||
});
|
||||
@@ -311,7 +303,6 @@ describe("select single element on the scene", () => {
|
||||
const tool = getByToolName("rectangle");
|
||||
fireEvent.click(tool);
|
||||
fireEvent.pointerDown(canvas, { clientX: 30, clientY: 20 });
|
||||
fireEvent.pointerMove(canvas, { clientX: -1000, clientY: -1000 });
|
||||
fireEvent.pointerMove(canvas, { clientX: 60, clientY: 70 });
|
||||
fireEvent.pointerUp(canvas);
|
||||
fireEvent.keyDown(document, {
|
||||
@@ -344,7 +335,6 @@ describe("select single element on the scene", () => {
|
||||
const tool = getByToolName("diamond");
|
||||
fireEvent.click(tool);
|
||||
fireEvent.pointerDown(canvas, { clientX: 30, clientY: 20 });
|
||||
fireEvent.pointerMove(canvas, { clientX: -1000, clientY: -1000 });
|
||||
fireEvent.pointerMove(canvas, { clientX: 60, clientY: 70 });
|
||||
fireEvent.pointerUp(canvas);
|
||||
fireEvent.keyDown(document, {
|
||||
@@ -377,7 +367,6 @@ describe("select single element on the scene", () => {
|
||||
const tool = getByToolName("ellipse");
|
||||
fireEvent.click(tool);
|
||||
fireEvent.pointerDown(canvas, { clientX: 30, clientY: 20 });
|
||||
fireEvent.pointerMove(canvas, { clientX: -1000, clientY: -1000 });
|
||||
fireEvent.pointerMove(canvas, { clientX: 60, clientY: 70 });
|
||||
fireEvent.pointerUp(canvas);
|
||||
fireEvent.keyDown(document, {
|
||||
@@ -410,7 +399,6 @@ describe("select single element on the scene", () => {
|
||||
const tool = getByToolName("arrow");
|
||||
fireEvent.click(tool);
|
||||
fireEvent.pointerDown(canvas, { clientX: 30, clientY: 20 });
|
||||
fireEvent.pointerMove(canvas, { clientX: -1000, clientY: -1000 });
|
||||
fireEvent.pointerMove(canvas, { clientX: 60, clientY: 70 });
|
||||
fireEvent.pointerUp(canvas);
|
||||
fireEvent.keyDown(document, {
|
||||
@@ -437,8 +425,8 @@ describe("select single element on the scene", () => {
|
||||
fireEvent.pointerDown(canvas, { clientX: 40, clientY: 40 });
|
||||
fireEvent.pointerUp(canvas);
|
||||
|
||||
expect(renderInteractiveScene).toHaveBeenCalledTimes(10);
|
||||
expect(renderStaticScene).toHaveBeenCalledTimes(8);
|
||||
expect(renderInteractiveScene).toHaveBeenCalledTimes(9);
|
||||
expect(renderStaticScene).toHaveBeenCalledTimes(7);
|
||||
expect(h.state.selectionElement).toBeNull();
|
||||
expect(h.elements.length).toEqual(1);
|
||||
expect(h.state.selectedElementIds[h.elements[0].id]).toBeTruthy();
|
||||
@@ -455,7 +443,6 @@ describe("select single element on the scene", () => {
|
||||
const tool = getByToolName("line");
|
||||
fireEvent.click(tool);
|
||||
fireEvent.pointerDown(canvas, { clientX: 30, clientY: 20 });
|
||||
fireEvent.pointerMove(canvas, { clientX: -1000, clientY: -1000 });
|
||||
fireEvent.pointerMove(canvas, { clientX: 60, clientY: 70 });
|
||||
fireEvent.pointerUp(canvas);
|
||||
fireEvent.keyDown(document, {
|
||||
@@ -482,8 +469,8 @@ describe("select single element on the scene", () => {
|
||||
fireEvent.pointerDown(canvas, { clientX: 40, clientY: 40 });
|
||||
fireEvent.pointerUp(canvas);
|
||||
|
||||
expect(renderInteractiveScene).toHaveBeenCalledTimes(10);
|
||||
expect(renderStaticScene).toHaveBeenCalledTimes(8);
|
||||
expect(renderInteractiveScene).toHaveBeenCalledTimes(9);
|
||||
expect(renderStaticScene).toHaveBeenCalledTimes(7);
|
||||
expect(h.state.selectionElement).toBeNull();
|
||||
expect(h.elements.length).toEqual(1);
|
||||
expect(h.state.selectedElementIds[h.elements[0].id]).toBeTruthy();
|
||||
@@ -538,7 +525,6 @@ describe("selectedElementIds stability", () => {
|
||||
expect(h.state.selectedElementIds).toBe(selectedElementIds_1);
|
||||
|
||||
mouse.downAt(-50, -50);
|
||||
mouse.move(-1000, -1000);
|
||||
mouse.moveTo(50, 50);
|
||||
|
||||
const selectedElementIds_2 = h.state.selectedElementIds;
|
||||
|
||||
@@ -158,3 +158,8 @@ 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]);
|
||||
|
||||
@@ -6398,11 +6398,6 @@ is-map@^2.0.3:
|
||||
resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.3.tgz#ede96b7fe1e270b3c4465e3a465658764926d62e"
|
||||
integrity sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==
|
||||
|
||||
is-mobile@5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/is-mobile/-/is-mobile-5.0.0.tgz#1e08a0ef2c38a67bff84a52af68d67bcef445333"
|
||||
integrity sha512-Tz/yndySvLAEXh+Uk8liFCxOwVH6YutuR74utvOcu7I9Di+DwM0mtdPVZNaVvvBUM2OXxne/NhOs1zAO7riusQ==
|
||||
|
||||
is-module@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/is-module/-/is-module-1.0.0.tgz#3258fb69f78c14d5b815d664336b4cffb6441591"
|
||||
|
||||
Reference in New Issue
Block a user