feat: support segment midpoints in line editor (#5641)
* feat: support segment midpoints in line editor * fix tests * midpoints working in bezier curve * midpoint working with non zero roughness * calculate beizer curve control points for points >2 * unnecessary rerender * don't show phantom points inside editor for short segments * don't show phantom points for small curves * improve the algo for plotting midpoints on bezier curve by taking arc lengths and doing binary search * fix tests finally * fix naming * cache editor midpoints * clear midpoint cache when undo * fix caching * calculate index properly when not all segments have midpoints * make sure correct element version is fetched from cache * chore * fix * direct comparison for equal points * create arePointsEqual util * upate name * don't update cache except inside getter * don't compute midpoints outside editor unless 2pointer lines * update cache to object and burst when Zoom updated as well * early return if midpoints not present outside editor * don't early return * cleanup * Add specs * fixpull/5698/head
parent
c5869979c8
commit
0d1058a596
@ -0,0 +1,60 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[` Test Linear Elements Inside editor should allow dragging line from midpoint in 2 pointer lines 1`] = `
|
||||
Array [
|
||||
Array [
|
||||
0,
|
||||
0,
|
||||
],
|
||||
Array [
|
||||
70,
|
||||
50,
|
||||
],
|
||||
Array [
|
||||
40,
|
||||
0,
|
||||
],
|
||||
]
|
||||
`;
|
||||
|
||||
exports[` Test Linear Elements Inside editor should allow dragging lines from midpoints in between segments 1`] = `
|
||||
Array [
|
||||
Array [
|
||||
0,
|
||||
0,
|
||||
],
|
||||
Array [
|
||||
85,
|
||||
75,
|
||||
],
|
||||
Array [
|
||||
70,
|
||||
50,
|
||||
],
|
||||
Array [
|
||||
105,
|
||||
75,
|
||||
],
|
||||
Array [
|
||||
40,
|
||||
0,
|
||||
],
|
||||
]
|
||||
`;
|
||||
|
||||
exports[` Test Linear Elements should allow dragging line from midpoint in 2 pointer lines outside editor 1`] = `
|
||||
Array [
|
||||
Array [
|
||||
0,
|
||||
0,
|
||||
],
|
||||
Array [
|
||||
70,
|
||||
50,
|
||||
],
|
||||
Array [
|
||||
40,
|
||||
0,
|
||||
],
|
||||
]
|
||||
`;
|
@ -0,0 +1,146 @@
|
||||
import ReactDOM from "react-dom";
|
||||
import { ExcalidrawLinearElement } from "../element/types";
|
||||
import ExcalidrawApp from "../excalidraw-app";
|
||||
import { centerPoint } from "../math";
|
||||
import { reseed } from "../random";
|
||||
import * as Renderer from "../renderer/renderScene";
|
||||
import { Keyboard } from "./helpers/ui";
|
||||
import { screen } from "./test-utils";
|
||||
|
||||
import { render, fireEvent } from "./test-utils";
|
||||
import { Point } from "../types";
|
||||
import { KEYS } from "../keys";
|
||||
import { LinearElementEditor } from "../element/linearElementEditor";
|
||||
|
||||
const renderScene = jest.spyOn(Renderer, "renderScene");
|
||||
|
||||
const { h } = window;
|
||||
|
||||
describe(" Test Linear Elements", () => {
|
||||
let getByToolName: (...args: string[]) => HTMLElement;
|
||||
let container: HTMLElement;
|
||||
let canvas: HTMLCanvasElement;
|
||||
|
||||
beforeEach(async () => {
|
||||
// Unmount ReactDOM from root
|
||||
ReactDOM.unmountComponentAtNode(document.getElementById("root")!);
|
||||
localStorage.clear();
|
||||
renderScene.mockClear();
|
||||
reseed(7);
|
||||
const comp = await render(<ExcalidrawApp />);
|
||||
getByToolName = comp.getByToolName;
|
||||
container = comp.container;
|
||||
canvas = container.querySelector("canvas")!;
|
||||
});
|
||||
|
||||
const p1: Point = [20, 20];
|
||||
const p2: Point = [60, 20];
|
||||
const midpoint = centerPoint(p1, p2);
|
||||
|
||||
const createTwoPointerLinearElement = (
|
||||
type: ExcalidrawLinearElement["type"],
|
||||
edge: "Sharp" | "Round" = "Sharp",
|
||||
roughness: "Architect" | "Cartoonist" | "Artist" = "Architect",
|
||||
) => {
|
||||
const tool = getByToolName(type);
|
||||
fireEvent.click(tool);
|
||||
fireEvent.click(screen.getByTitle(edge));
|
||||
fireEvent.click(screen.getByTitle(roughness));
|
||||
fireEvent.pointerDown(canvas, { clientX: p1[0], clientY: p1[1] });
|
||||
fireEvent.pointerMove(canvas, { clientX: p2[0], clientY: p2[1] });
|
||||
fireEvent.pointerUp(canvas, { clientX: p2[0], clientY: p2[1] });
|
||||
};
|
||||
|
||||
const createThreePointerLinearElement = (
|
||||
type: ExcalidrawLinearElement["type"],
|
||||
edge: "Sharp" | "Round" = "Sharp",
|
||||
) => {
|
||||
createTwoPointerLinearElement("line");
|
||||
// Extending line via midpoint
|
||||
fireEvent.pointerDown(canvas, {
|
||||
clientX: midpoint[0],
|
||||
clientY: midpoint[1],
|
||||
});
|
||||
fireEvent.pointerMove(canvas, {
|
||||
clientX: midpoint[0] + 50,
|
||||
clientY: midpoint[1] + 50,
|
||||
});
|
||||
fireEvent.pointerUp(canvas, {
|
||||
clientX: midpoint[0] + 50,
|
||||
clientY: midpoint[1] + 50,
|
||||
});
|
||||
};
|
||||
|
||||
const dragLinearElementFromPoint = (point: Point) => {
|
||||
fireEvent.pointerDown(canvas, {
|
||||
clientX: point[0],
|
||||
clientY: point[1],
|
||||
});
|
||||
fireEvent.pointerMove(canvas, {
|
||||
clientX: point[0] + 50,
|
||||
clientY: point[1] + 50,
|
||||
});
|
||||
fireEvent.pointerUp(canvas, {
|
||||
clientX: point[0] + 50,
|
||||
clientY: point[1] + 50,
|
||||
});
|
||||
};
|
||||
|
||||
it("should allow dragging line from midpoint in 2 pointer lines outside editor", async () => {
|
||||
createTwoPointerLinearElement("line");
|
||||
const line = h.elements[0] as ExcalidrawLinearElement;
|
||||
|
||||
expect(renderScene).toHaveBeenCalledTimes(10);
|
||||
expect((h.elements[0] as ExcalidrawLinearElement).points.length).toEqual(2);
|
||||
|
||||
// drag line from midpoint
|
||||
dragLinearElementFromPoint(midpoint);
|
||||
expect(renderScene).toHaveBeenCalledTimes(13);
|
||||
expect(line.points.length).toEqual(3);
|
||||
expect(line.points).toMatchSnapshot();
|
||||
});
|
||||
|
||||
describe("Inside editor", () => {
|
||||
it("should allow dragging line from midpoint in 2 pointer lines", async () => {
|
||||
createTwoPointerLinearElement("line");
|
||||
const line = h.elements[0] as ExcalidrawLinearElement;
|
||||
|
||||
fireEvent.click(canvas, { clientX: p1[0], clientY: p1[1] });
|
||||
|
||||
Keyboard.keyPress(KEYS.ENTER);
|
||||
expect(h.state.editingLinearElement?.elementId).toEqual(h.elements[0].id);
|
||||
|
||||
// drag line from midpoint
|
||||
dragLinearElementFromPoint(midpoint);
|
||||
expect(line.points.length).toEqual(3);
|
||||
expect(line.points).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("should allow dragging lines from midpoints in between segments", async () => {
|
||||
createThreePointerLinearElement("line");
|
||||
|
||||
const line = h.elements[0] as ExcalidrawLinearElement;
|
||||
expect(line.points.length).toEqual(3);
|
||||
fireEvent.click(canvas, { clientX: p1[0], clientY: p1[1] });
|
||||
|
||||
Keyboard.keyPress(KEYS.ENTER);
|
||||
expect(h.state.editingLinearElement?.elementId).toEqual(h.elements[0].id);
|
||||
|
||||
let points = LinearElementEditor.getPointsGlobalCoordinates(line);
|
||||
const firstSegmentMidpoint = centerPoint(points[0], points[1]);
|
||||
// drag line via first segment midpoint
|
||||
dragLinearElementFromPoint(firstSegmentMidpoint);
|
||||
expect(line.points.length).toEqual(4);
|
||||
|
||||
// drag line from last segment midpoint
|
||||
points = LinearElementEditor.getPointsGlobalCoordinates(line);
|
||||
const lastSegmentMidpoint = centerPoint(points.at(-2)!, points.at(-1)!);
|
||||
dragLinearElementFromPoint(lastSegmentMidpoint);
|
||||
expect(line.points.length).toEqual(5);
|
||||
|
||||
expect(
|
||||
(h.elements[0] as ExcalidrawLinearElement).points,
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue