Prefer arrow functions (#2344)

This commit is contained in:
Lipis 2020-11-06 22:06:39 +02:00 committed by GitHub
parent e05acd6fd9
commit a20f3240fd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 304 additions and 336 deletions

View File

@ -29,7 +29,7 @@ config.entry = "./src/index-node";
// By default, webpack is going to replace the require of the canvas.node file
// to just a string with the path of the canvas.node file. We need to tell
// webpack to avoid rewriting that dependency.
config.externals = function (context, request, callback) {
config.externals = (context, request, callback) => {
if (/\.node$/.test(request)) {
return callback(
null,

View File

@ -23,11 +23,11 @@ const enableActionGroup = (
appState: AppState,
) => getSelectedElements(getNonDeletedElements(elements), appState).length > 1;
function alignSelectedElements(
const alignSelectedElements = (
elements: readonly ExcalidrawElement[],
appState: Readonly<AppState>,
alignment: Alignment,
) {
) => {
const selectedElements = getSelectedElements(
getNonDeletedElements(elements),
appState,
@ -38,7 +38,7 @@ function alignSelectedElements(
const updatedElementsMap = getElementMap(updatedElements);
return elements.map((element) => updatedElementsMap[element.id] || element);
}
};
export const actionAlignTop = register({
name: "alignTop",

View File

@ -31,10 +31,10 @@ const deleteSelectedElements = (
};
};
function handleGroupEditingState(
const handleGroupEditingState = (
appState: AppState,
elements: readonly ExcalidrawElement[],
): AppState {
): AppState => {
if (appState.editingGroupId) {
const siblingElements = getElementsInGroup(
getNonDeletedElements(elements),
@ -48,7 +48,7 @@ function handleGroupEditingState(
}
}
return appState;
}
};
export const actionDeleteSelected = register({
name: "deleteSelectedElements",

View File

@ -89,7 +89,7 @@ const calculateTranslation = (
};
};
function getCommonBoundingBox(elements: ExcalidrawElement[]): Box {
const getCommonBoundingBox = (elements: ExcalidrawElement[]): Box => {
const [minX, minY, maxX, maxY] = getCommonBounds(elements);
return { minX, minY, maxX, maxY };
}
};

View File

@ -24,21 +24,21 @@ type ParseSpreadsheetResult =
error: string;
};
function tryParseNumber(s: string): number | null {
const tryParseNumber = (s: string): number | null => {
const match = /^[$€£¥₩]?([0-9]+(\.[0-9]+)?)$/.exec(s);
if (!match) {
return null;
}
return parseFloat(match[1]);
}
};
function isNumericColumn(lines: string[][], columnIndex: number) {
const isNumericColumn = (lines: string[][], columnIndex: number) => {
return lines
.slice(1)
.every((line) => tryParseNumber(line[columnIndex]) !== null);
}
};
function tryParseCells(cells: string[][]): ParseSpreadsheetResult {
const tryParseCells = (cells: string[][]): ParseSpreadsheetResult => {
const numCols = cells[0].length;
if (numCols > 2) {
@ -94,9 +94,9 @@ function tryParseCells(cells: string[][]): ParseSpreadsheetResult {
values: rows.map((row) => tryParseNumber(row[valueColumnIndex])!),
},
};
}
};
function transposeCells(cells: string[][]) {
const transposeCells = (cells: string[][]) => {
const nextCells: string[][] = [];
for (let col = 0; col < cells[0].length; col++) {
const nextCellRow: string[] = [];
@ -107,9 +107,9 @@ function transposeCells(cells: string[][]) {
}
return nextCells;
}
};
export function tryParseSpreadsheet(text: string): ParseSpreadsheetResult {
export const tryParseSpreadsheet = (text: string): ParseSpreadsheetResult => {
// copy/paste from excel, in-browser excel, and google sheets is tsv
// for now we only accept 2 columns with an optional header
const lines = text
@ -139,7 +139,7 @@ export function tryParseSpreadsheet(text: string): ParseSpreadsheetResult {
}
return result;
}
};
const BAR_WIDTH = 32;
const BAR_SPACING = 12;
@ -148,12 +148,12 @@ const LABEL_SPACING = 3 * BAR_SPACING;
const Y_AXIS_LABEL_SPACING = LABEL_SPACING;
const ANGLE = 5.87;
export function renderSpreadsheet(
export const renderSpreadsheet = (
appState: AppState,
spreadsheet: Spreadsheet,
x: number,
y: number,
): ExcalidrawElement[] {
): ExcalidrawElement[] => {
const max = Math.max(...spreadsheet.values);
const min = Math.min(0, ...spreadsheet.values);
const range = max - min;
@ -268,4 +268,4 @@ export function renderSpreadsheet(
return [...bars, yAxisLabel, minYLabel, maxYLabel, ...xLabels].filter(
(element) => element !== null,
) as ExcalidrawElement[];
}
};

View File

@ -62,10 +62,10 @@ interface LayerUIProps {
lng: string;
}
function useOnClickOutside(
const useOnClickOutside = (
ref: RefObject<HTMLElement>,
cb: (event: MouseEvent) => void,
) {
) => {
useEffect(() => {
const listener = (event: MouseEvent) => {
if (!ref.current) {
@ -88,7 +88,7 @@ function useOnClickOutside(
document.removeEventListener("pointerdown", listener);
};
}, [ref, cb]);
}
};
const LibraryMenuItems = ({
library,

View File

@ -8,7 +8,7 @@ let firebasePromise: Promise<
typeof import("firebase/app").default
> | null = null;
async function loadFirebase() {
const loadFirebase = async () => {
const firebase = (
await import(/* webpackChunkName: "firebase" */ "firebase/app")
).default;
@ -18,15 +18,17 @@ async function loadFirebase() {
firebase.initializeApp(firebaseConfig);
return firebase;
}
};
async function getFirebase(): Promise<typeof import("firebase/app").default> {
const getFirebase = async (): Promise<
typeof import("firebase/app").default
> => {
if (!firebasePromise) {
firebasePromise = loadFirebase();
}
const firebase = await firebasePromise!;
return firebase;
}
};
interface FirebaseStoredScene {
sceneVersion: number;
@ -34,10 +36,10 @@ interface FirebaseStoredScene {
ciphertext: firebase.default.firestore.Blob;
}
async function encryptElements(
const encryptElements = async (
key: string,
elements: readonly ExcalidrawElement[],
): Promise<{ ciphertext: ArrayBuffer; iv: Uint8Array }> {
): Promise<{ ciphertext: ArrayBuffer; iv: Uint8Array }> => {
const importedKey = await getImportedKey(key, "encrypt");
const iv = createIV();
const json = JSON.stringify(elements);
@ -52,13 +54,13 @@ async function encryptElements(
);
return { ciphertext, iv };
}
};
async function decryptElements(
const decryptElements = async (
key: string,
iv: Uint8Array,
ciphertext: ArrayBuffer,
): Promise<readonly ExcalidrawElement[]> {
): Promise<readonly ExcalidrawElement[]> => {
const importedKey = await getImportedKey(key, "decrypt");
const decrypted = await window.crypto.subtle.decrypt(
{
@ -73,7 +75,7 @@ async function decryptElements(
new Uint8Array(decrypted) as any,
);
return JSON.parse(decodedData);
}
};
const firebaseSceneVersionCache = new WeakMap<SocketIOClient.Socket, number>();
@ -90,10 +92,10 @@ export const isSavedToFirebase = (
return true;
};
export async function saveToFirebase(
export const saveToFirebase = async (
portal: Portal,
elements: readonly ExcalidrawElement[],
) {
) => {
const { roomID, roomKey, socket } = portal;
if (
// if no room exists, consider the room saved because there's nothing we can
@ -141,12 +143,12 @@ export async function saveToFirebase(
}
return didUpdate;
}
};
export async function loadFromFirebase(
export const loadFromFirebase = async (
roomID: string,
roomKey: string,
): Promise<readonly ExcalidrawElement[] | null> {
): Promise<readonly ExcalidrawElement[] | null> => {
const firebase = await getFirebase();
const db = firebase.firestore();
@ -159,4 +161,4 @@ export async function loadFromFirebase(
const ciphertext = storedScene.ciphertext.toUint8Array();
const iv = storedScene.iv.toUint8Array();
return restoreElements(await decryptElements(roomKey, iv, ciphertext));
}
};

View File

@ -82,7 +82,7 @@ export const newElement = (
_newElementBase<ExcalidrawGenericElement>(opts.type, opts);
/** computes element x/y offset based on textAlign/verticalAlign */
function getTextElementPositionOffsets(
const getTextElementPositionOffsets = (
opts: {
textAlign: ExcalidrawTextElement["textAlign"];
verticalAlign: ExcalidrawTextElement["verticalAlign"];
@ -91,7 +91,7 @@ function getTextElementPositionOffsets(
width: number;
height: number;
},
) {
) => {
return {
x:
opts.textAlign === "center"
@ -101,7 +101,7 @@ function getTextElementPositionOffsets(
: 0,
y: opts.verticalAlign === "middle" ? metrics.height / 2 : 0,
};
}
};
export const newTextElement = (
opts: {

View File

@ -46,7 +46,7 @@ export const textWysiwyg = ({
getViewportCoords: (x: number, y: number) => [number, number];
element: ExcalidrawElement;
}) => {
function updateWysiwygStyle() {
const updateWysiwygStyle = () => {
const updatedElement = Scene.getScene(element)?.getElement(id);
if (updatedElement && isTextElement(updatedElement)) {
const [viewportX, viewportY] = getViewportCoords(
@ -80,7 +80,7 @@ export const textWysiwyg = ({
filter: "var(--appearance-filter)",
});
}
}
};
const editable = document.createElement("textarea");

277
src/ga.ts
View File

@ -22,22 +22,25 @@ export type Direction = NVector;
export type Line = NVector;
export type Transform = NVector;
export function point(x: number, y: number): Point {
return [0, 0, 0, 0, y, x, 1, 0];
}
export const point = (x: number, y: number): Point => [0, 0, 0, 0, y, x, 1, 0];
export function origin(): Point {
return [0, 0, 0, 0, 0, 0, 1, 0];
}
export const origin = (): Point => [0, 0, 0, 0, 0, 0, 1, 0];
export function direction(x: number, y: number): Direction {
export const direction = (x: number, y: number): Direction => {
const norm = Math.hypot(x, y); // same as `inorm(direction(x, y))`
return [0, 0, 0, 0, y / norm, x / norm, 0, 0];
}
};
export function offset(x: number, y: number): Direction {
return [0, 0, 0, 0, y, x, 0, 0];
}
export const offset = (x: number, y: number): Direction => [
0,
0,
0,
0,
y,
x,
0,
0,
];
/// This is the "implementation" part of the library
@ -56,7 +59,7 @@ type NVector = readonly [
const NVECTOR_BASE = ["1", "e0", "e1", "e2", "e01", "e20", "e12", "e012"];
// Used to represent points, lines and transformations
export function nvector(value: number = 0, index: number = 0): NVector {
export const nvector = (value: number = 0, index: number = 0): NVector => {
const result = [0, 0, 0, 0, 0, 0, 0, 0];
if (index < 0 || index > 7) {
throw new Error(`Expected \`index\` betwen 0 and 7, got \`${index}\``);
@ -65,10 +68,10 @@ export function nvector(value: number = 0, index: number = 0): NVector {
result[index] = value;
}
return (result as unknown) as NVector;
}
};
const STRING_EPSILON = 0.000001;
export function toString(nvector: NVector): string {
export const toString = (nvector: NVector): string => {
const result = nvector
.map((value, index) =>
Math.abs(value) > STRING_EPSILON
@ -79,66 +82,58 @@ export function toString(nvector: NVector): string {
.filter((representation) => representation != null)
.join(" + ");
return result === "" ? "0" : result;
}
};
// Reverse the order of the basis blades.
export function reverse(nvector: NVector): NVector {
return [
nvector[0],
nvector[1],
nvector[2],
nvector[3],
-nvector[4],
-nvector[5],
-nvector[6],
-nvector[7],
];
}
export const reverse = (nvector: NVector): NVector => [
nvector[0],
nvector[1],
nvector[2],
nvector[3],
-nvector[4],
-nvector[5],
-nvector[6],
-nvector[7],
];
// Poincare duality operator.
export function dual(nvector: NVector): NVector {
return [
nvector[7],
nvector[6],
nvector[5],
nvector[4],
nvector[3],
nvector[2],
nvector[1],
nvector[0],
];
}
export const dual = (nvector: NVector): NVector => [
nvector[7],
nvector[6],
nvector[5],
nvector[4],
nvector[3],
nvector[2],
nvector[1],
nvector[0],
];
// Clifford Conjugation
export function conjugate(nvector: NVector): NVector {
return [
nvector[0],
-nvector[1],
-nvector[2],
-nvector[3],
-nvector[4],
-nvector[5],
-nvector[6],
nvector[7],
];
}
export const conjugate = (nvector: NVector): NVector => [
nvector[0],
-nvector[1],
-nvector[2],
-nvector[3],
-nvector[4],
-nvector[5],
-nvector[6],
nvector[7],
];
// Main involution
export function involute(nvector: NVector): NVector {
return [
nvector[0],
-nvector[1],
-nvector[2],
-nvector[3],
nvector[4],
nvector[5],
nvector[6],
-nvector[7],
];
}
export const involute = (nvector: NVector): NVector => [
nvector[0],
-nvector[1],
-nvector[2],
-nvector[3],
nvector[4],
nvector[5],
nvector[6],
-nvector[7],
];
// Multivector addition
export function add(a: NVector, b: NVector | number): NVector {
export const add = (a: NVector, b: NVector | number): NVector => {
if (isNumber(b)) {
return [a[0] + b, a[1], a[2], a[3], a[4], a[5], a[6], a[7]];
}
@ -152,10 +147,10 @@ export function add(a: NVector, b: NVector | number): NVector {
a[6] + b[6],
a[7] + b[7],
];
}
};
// Multivector subtraction
export function sub(a: NVector, b: NVector | number): NVector {
export const sub = (a: NVector, b: NVector | number): NVector => {
if (isNumber(b)) {
return [a[0] - b, a[1], a[2], a[3], a[4], a[5], a[6], a[7]];
}
@ -169,10 +164,10 @@ export function sub(a: NVector, b: NVector | number): NVector {
a[6] - b[6],
a[7] - b[7],
];
}
};
// The geometric product.
export function mul(a: NVector, b: NVector | number): NVector {
export const mul = (a: NVector, b: NVector | number): NVector => {
if (isNumber(b)) {
return [
a[0] * b,
@ -223,112 +218,94 @@ export function mul(a: NVector, b: NVector | number): NVector {
b[1] * a[6] +
b[0] * a[7],
];
}
};
export function mulScalar(a: NVector, b: NVector): number {
return b[0] * a[0] + b[2] * a[2] + b[3] * a[3] - b[6] * a[6];
}
export const mulScalar = (a: NVector, b: NVector): number =>
b[0] * a[0] + b[2] * a[2] + b[3] * a[3] - b[6] * a[6];
// The outer/exterior/wedge product.
export function meet(a: NVector, b: NVector): NVector {
return [
b[0] * a[0],
b[1] * a[0] + b[0] * a[1],
b[2] * a[0] + b[0] * a[2],
b[3] * a[0] + b[0] * a[3],
b[4] * a[0] + b[2] * a[1] - b[1] * a[2] + b[0] * a[4],
b[5] * a[0] - b[3] * a[1] + b[1] * a[3] + b[0] * a[5],
b[6] * a[0] + b[3] * a[2] - b[2] * a[3] + b[0] * a[6],
b[7] * a[0] +
b[6] * a[1] +
b[5] * a[2] +
b[4] * a[3] +
b[3] * a[4] +
b[2] * a[5] +
b[1] * a[6],
];
}
export const meet = (a: NVector, b: NVector): NVector => [
b[0] * a[0],
b[1] * a[0] + b[0] * a[1],
b[2] * a[0] + b[0] * a[2],
b[3] * a[0] + b[0] * a[3],
b[4] * a[0] + b[2] * a[1] - b[1] * a[2] + b[0] * a[4],
b[5] * a[0] - b[3] * a[1] + b[1] * a[3] + b[0] * a[5],
b[6] * a[0] + b[3] * a[2] - b[2] * a[3] + b[0] * a[6],
b[7] * a[0] +
b[6] * a[1] +
b[5] * a[2] +
b[4] * a[3] +
b[3] * a[4] +
b[2] * a[5] +
b[1] * a[6],
];
// The regressive product.
export function join(a: NVector, b: NVector): NVector {
return [
joinScalar(a, b),
a[1] * b[7] + a[4] * b[5] - a[5] * b[4] + a[7] * b[1],
a[2] * b[7] - a[4] * b[6] + a[6] * b[4] + a[7] * b[2],
a[3] * b[7] + a[5] * b[6] - a[6] * b[5] + a[7] * b[3],
a[4] * b[7] + a[7] * b[4],
a[5] * b[7] + a[7] * b[5],
a[6] * b[7] + a[7] * b[6],
a[7] * b[7],
];
}
export const join = (a: NVector, b: NVector): NVector => [
joinScalar(a, b),
a[1] * b[7] + a[4] * b[5] - a[5] * b[4] + a[7] * b[1],
a[2] * b[7] - a[4] * b[6] + a[6] * b[4] + a[7] * b[2],
a[3] * b[7] + a[5] * b[6] - a[6] * b[5] + a[7] * b[3],
a[4] * b[7] + a[7] * b[4],
a[5] * b[7] + a[7] * b[5],
a[6] * b[7] + a[7] * b[6],
a[7] * b[7],
];
export function joinScalar(a: NVector, b: NVector): number {
return (
a[0] * b[7] +
a[1] * b[6] +
a[2] * b[5] +
a[3] * b[4] +
a[4] * b[3] +
a[5] * b[2] +
a[6] * b[1] +
a[7] * b[0]
);
}
export const joinScalar = (a: NVector, b: NVector): number =>
a[0] * b[7] +
a[1] * b[6] +
a[2] * b[5] +
a[3] * b[4] +
a[4] * b[3] +
a[5] * b[2] +
a[6] * b[1] +
a[7] * b[0];
// The inner product.
export function dot(a: NVector, b: NVector): NVector {
return [
b[0] * a[0] + b[2] * a[2] + b[3] * a[3] - b[6] * a[6],
b[1] * a[0] +
b[0] * a[1] -
b[4] * a[2] +
b[5] * a[3] +
b[2] * a[4] -
b[3] * a[5] -
b[7] * a[6] -
b[6] * a[7],
b[2] * a[0] + b[0] * a[2] - b[6] * a[3] + b[3] * a[6],
b[3] * a[0] + b[6] * a[2] + b[0] * a[3] - b[2] * a[6],
b[4] * a[0] + b[7] * a[3] + b[0] * a[4] + b[3] * a[7],
b[5] * a[0] + b[7] * a[2] + b[0] * a[5] + b[2] * a[7],
b[6] * a[0] + b[0] * a[6],
b[7] * a[0] + b[0] * a[7],
];
}
export const dot = (a: NVector, b: NVector): NVector => [
b[0] * a[0] + b[2] * a[2] + b[3] * a[3] - b[6] * a[6],
b[1] * a[0] +
b[0] * a[1] -
b[4] * a[2] +
b[5] * a[3] +
b[2] * a[4] -
b[3] * a[5] -
b[7] * a[6] -
b[6] * a[7],
b[2] * a[0] + b[0] * a[2] - b[6] * a[3] + b[3] * a[6],
b[3] * a[0] + b[6] * a[2] + b[0] * a[3] - b[2] * a[6],
b[4] * a[0] + b[7] * a[3] + b[0] * a[4] + b[3] * a[7],
b[5] * a[0] + b[7] * a[2] + b[0] * a[5] + b[2] * a[7],
b[6] * a[0] + b[0] * a[6],
b[7] * a[0] + b[0] * a[7],
];
export function norm(a: NVector): number {
return Math.sqrt(
Math.abs(a[0] * a[0] - a[2] * a[2] - a[3] * a[3] + a[6] * a[6]),
);
}
export const norm = (a: NVector): number =>
Math.sqrt(Math.abs(a[0] * a[0] - a[2] * a[2] - a[3] * a[3] + a[6] * a[6]));
export function inorm(a: NVector): number {
return Math.sqrt(
Math.abs(a[7] * a[7] - a[5] * a[5] - a[4] * a[4] + a[1] * a[1]),
);
}
export const inorm = (a: NVector): number =>
Math.sqrt(Math.abs(a[7] * a[7] - a[5] * a[5] - a[4] * a[4] + a[1] * a[1]));
export function normalized(a: NVector): NVector {
export const normalized = (a: NVector): NVector => {
const n = norm(a);
if (n === 0 || n === 1) {
return a;
}
const sign = a[6] < 0 ? -1 : 1;
return mul(a, sign / n);
}
};
export function inormalized(a: NVector): NVector {
export const inormalized = (a: NVector): NVector => {
const n = inorm(a);
if (n === 0 || n === 1) {
return a;
}
return mul(a, 1 / n);
}
};
function isNumber(a: any): a is number {
return typeof a === "number";
}
const isNumber = (a: any): a is number => typeof a === "number";
export const E0: NVector = nvector(1, 1);
export const E1: NVector = nvector(1, 2);

View File

@ -6,18 +6,21 @@ import { Line, Direction, Point } from "./ga";
* vector `(x, y)`.
*/
export function from(point: Point): Point {
return [0, 0, 0, 0, point[4], point[5], 0, 0];
}
export const from = (point: Point): Point => [
0,
0,
0,
0,
point[4],
point[5],
0,
0,
];
export function fromTo(from: Point, to: Point): Direction {
return GA.inormalized([0, 0, 0, 0, to[4] - from[4], to[5] - from[5], 0, 0]);
}
export const fromTo = (from: Point, to: Point): Direction =>
GA.inormalized([0, 0, 0, 0, to[4] - from[4], to[5] - from[5], 0, 0]);
export function orthogonal(direction: Direction): Direction {
return GA.inormalized([0, 0, 0, 0, -direction[5], direction[4], 0, 0]);
}
export const orthogonal = (direction: Direction): Direction =>
GA.inormalized([0, 0, 0, 0, -direction[5], direction[4], 0, 0]);
export function orthogonalToLine(line: Line): Direction {
return GA.mul(line, GA.I);
}
export const orthogonalToLine = (line: Line): Direction => GA.mul(line, GA.I);

View File

@ -15,48 +15,38 @@ import { Line, Point } from "./ga";
*/
// Returns line with direction (x, y) through origin
export function vector(x: number, y: number): Line {
return GA.normalized([0, 0, -y, x, 0, 0, 0, 0]);
}
export const vector = (x: number, y: number): Line =>
GA.normalized([0, 0, -y, x, 0, 0, 0, 0]);
// For equation ax + by + c = 0.
export function equation(a: number, b: number, c: number): Line {
return GA.normalized([0, c, a, b, 0, 0, 0, 0]);
}
export const equation = (a: number, b: number, c: number): Line =>
GA.normalized([0, c, a, b, 0, 0, 0, 0]);
export function through(from: Point, to: Point): Line {
return GA.normalized(GA.join(to, from));
}
export const through = (from: Point, to: Point): Line =>
GA.normalized(GA.join(to, from));
export function orthogonal(line: Line, point: Point): Line {
return GA.dot(line, point);
}
export const orthogonal = (line: Line, point: Point): Line =>
GA.dot(line, point);
// Returns a line perpendicular to the line through `against` and `intersection`
// going through `intersection`.
export function orthogonalThrough(against: Point, intersection: Point): Line {
return orthogonal(through(against, intersection), intersection);
}
export const orthogonalThrough = (against: Point, intersection: Point): Line =>
orthogonal(through(against, intersection), intersection);
export function parallel(line: Line, distance: number): Line {
export const parallel = (line: Line, distance: number): Line => {
const result = line.slice();
result[1] -= distance;
return (result as unknown) as Line;
}
};
export function parallelThrough(line: Line, point: Point): Line {
return orthogonal(orthogonal(point, line), point);
}
export const parallelThrough = (line: Line, point: Point): Line =>
orthogonal(orthogonal(point, line), point);
export function distance(line1: Line, line2: Line): number {
return GA.inorm(GA.meet(line1, line2));
}
export const distance = (line1: Line, line2: Line): number =>
GA.inorm(GA.meet(line1, line2));
export function angle(line1: Line, line2: Line): number {
return Math.acos(GA.dot(line1, line2)[0]);
}
export const angle = (line1: Line, line2: Line): number =>
Math.acos(GA.dot(line1, line2)[0]);
// The orientation of the line
export function sign(line: Line): number {
return Math.sign(line[1]);
}
export const sign = (line: Line): number => Math.sign(line[1]);

View File

@ -2,36 +2,40 @@ import * as GA from "./ga";
import * as GALine from "./galines";
import { Point, Line, join } from "./ga";
/**
* TODO: docs
*/
export const from = ([x, y]: readonly [number, number]): Point => [
0,
0,
0,
0,
y,
x,
1,
0,
];
export function from([x, y]: readonly [number, number]): Point {
return [0, 0, 0, 0, y, x, 1, 0];
}
export const toTuple = (point: Point): [number, number] => [point[5], point[4]];
export function toTuple(point: Point): [number, number] {
return [point[5], point[4]];
}
export const abs = (point: Point): Point => [
0,
0,
0,
0,
Math.abs(point[4]),
Math.abs(point[5]),
1,
0,
];
export function abs(point: Point): Point {
return [0, 0, 0, 0, Math.abs(point[4]), Math.abs(point[5]), 1, 0];
}
export function intersect(line1: Line, line2: Line): Point {
return GA.normalized(GA.meet(line1, line2));
}
export const intersect = (line1: Line, line2: Line): Point =>
GA.normalized(GA.meet(line1, line2));
// Projects `point` onto the `line`.
// The returned point is the closest point on the `line` to the `point`.
export function project(point: Point, line: Line): Point {
return intersect(GALine.orthogonal(line, point), line);
}
export const project = (point: Point, line: Line): Point =>
intersect(GALine.orthogonal(line, point), line);
export function distance(point1: Point, point2: Point): number {
return GA.norm(join(point1, point2));
}
export const distance = (point1: Point, point2: Point): number =>
GA.norm(join(point1, point2));
export function distanceToLine(point: Point, line: Line): number {
return GA.joinScalar(point, line);
}
export const distanceToLine = (point: Point, line: Line): number =>
GA.joinScalar(point, line);

View File

@ -6,33 +6,36 @@ import * as GADirection from "./gadirections";
* TODO: docs
*/
export function rotation(pivot: Point, angle: number): Transform {
return GA.add(GA.mul(pivot, Math.sin(angle / 2)), Math.cos(angle / 2));
}
export const rotation = (pivot: Point, angle: number): Transform =>
GA.add(GA.mul(pivot, Math.sin(angle / 2)), Math.cos(angle / 2));
export function translation(direction: Direction): Transform {
return [1, 0, 0, 0, -(0.5 * direction[5]), 0.5 * direction[4], 0, 0];
}
export const translation = (direction: Direction): Transform => [
1,
0,
0,
0,
-(0.5 * direction[5]),
0.5 * direction[4],
0,
0,
];
export function translationOrthogonal(
export const translationOrthogonal = (
direction: Direction,
distance: number,
): Transform {
): Transform => {
const scale = 0.5 * distance;
return [1, 0, 0, 0, scale * direction[4], scale * direction[5], 0, 0];
}
};
export function translationAlong(line: Line, distance: number): Transform {
return GA.add(GA.mul(GADirection.orthogonalToLine(line), 0.5 * distance), 1);
}
export const translationAlong = (line: Line, distance: number): Transform =>
GA.add(GA.mul(GADirection.orthogonalToLine(line), 0.5 * distance), 1);
export function compose(motor1: Transform, motor2: Transform): Transform {
return GA.mul(motor2, motor1);
}
export const compose = (motor1: Transform, motor2: Transform): Transform =>
GA.mul(motor2, motor1);
export function apply(
export const apply = (
motor: Transform,
nvector: Point | Direction | Line,
): Point | Direction | Line {
return GA.normalized(GA.mul(GA.mul(motor, nvector), GA.reverse(motor)));
}
): Point | Direction | Line =>
GA.normalized(GA.mul(GA.mul(motor, nvector), GA.reverse(motor)));

View File

@ -2,11 +2,11 @@ import { GroupId, ExcalidrawElement, NonDeleted } from "./element/types";
import { AppState } from "./types";
import { getSelectedElements } from "./scene";
export function selectGroup(
export const selectGroup = (
groupId: GroupId,
appState: AppState,
elements: readonly NonDeleted<ExcalidrawElement>[],
): AppState {
): AppState => {
const elementsInGroup = elements.filter((element) =>
element.groupIds.includes(groupId),
);
@ -35,42 +35,38 @@ export function selectGroup(
),
},
};
}
};
/**
* If the element's group is selected, don't render an individual
* selection border around it.
*/
export function isSelectedViaGroup(
export const isSelectedViaGroup = (
appState: AppState,
element: ExcalidrawElement,
) {
return getSelectedGroupForElement(appState, element) != null;
}
) => getSelectedGroupForElement(appState, element) != null;
export const getSelectedGroupForElement = (
appState: AppState,
element: ExcalidrawElement,
) => {
return element.groupIds
) =>
element.groupIds
.filter((groupId) => groupId !== appState.editingGroupId)
.find((groupId) => appState.selectedGroupIds[groupId]);
};
export function getSelectedGroupIds(appState: AppState): GroupId[] {
return Object.entries(appState.selectedGroupIds)
export const getSelectedGroupIds = (appState: AppState): GroupId[] =>
Object.entries(appState.selectedGroupIds)
.filter(([groupId, isSelected]) => isSelected)
.map(([groupId, isSelected]) => groupId);
}
/**
* When you select an element, you often want to actually select the whole group it's in, unless
* you're currently editing that group.
*/
export function selectGroupsForSelectedElements(
export const selectGroupsForSelectedElements = (
appState: AppState,
elements: readonly NonDeleted<ExcalidrawElement>[],
): AppState {
): AppState => {
let nextAppState = { ...appState };
const selectedElements = getSelectedElements(elements, appState);
@ -91,7 +87,7 @@ export function selectGroupsForSelectedElements(
}
return nextAppState;
}
};
export const editGroupForSelectedElement = (
appState: AppState,
@ -107,29 +103,24 @@ export const editGroupForSelectedElement = (
};
};
export function isElementInGroup(element: ExcalidrawElement, groupId: string) {
return element.groupIds.includes(groupId);
}
export const isElementInGroup = (element: ExcalidrawElement, groupId: string) =>
element.groupIds.includes(groupId);
export function getElementsInGroup(
export const getElementsInGroup = (
elements: readonly ExcalidrawElement[],
groupId: string,
) {
return elements.filter((element) => isElementInGroup(element, groupId));
}
) => elements.filter((element) => isElementInGroup(element, groupId));
export function getSelectedGroupIdForElement(
export const getSelectedGroupIdForElement = (
element: ExcalidrawElement,
selectedGroupIds: { [groupId: string]: boolean },
) {
return element.groupIds.find((groupId) => selectedGroupIds[groupId]);
}
) => element.groupIds.find((groupId) => selectedGroupIds[groupId]);
export function getNewGroupIdsForDuplication(
export const getNewGroupIdsForDuplication = (
groupIds: ExcalidrawElement["groupIds"],
editingGroupId: AppState["editingGroupId"],
mapper: (groupId: GroupId) => GroupId,
) {
) => {
const copy = [...groupIds];
const positionOfEditingGroupId = editingGroupId
? groupIds.indexOf(editingGroupId)
@ -141,13 +132,13 @@ export function getNewGroupIdsForDuplication(
}
return copy;
}
};
export function addToGroup(
export const addToGroup = (
prevGroupIds: ExcalidrawElement["groupIds"],
newGroupId: GroupId,
editingGroupId: AppState["editingGroupId"],
) {
) => {
// insert before the editingGroupId, or push to the end.
const groupIds = [...prevGroupIds];
const positionOfEditingGroupId = editingGroupId
@ -157,11 +148,9 @@ export function addToGroup(
positionOfEditingGroupId > -1 ? positionOfEditingGroupId : groupIds.length;
groupIds.splice(positionToInsert, 0, newGroupId);
return groupIds;
}
};
export function removeFromSelectedGroups(
export const removeFromSelectedGroups = (
groupIds: ExcalidrawElement["groupIds"],
selectedGroupIds: { [groupId: string]: boolean },
) {
return groupIds.filter((groupId) => !selectedGroupIds[groupId]);
}
) => groupIds.filter((groupId) => !selectedGroupIds[groupId]);

View File

@ -334,7 +334,7 @@ export const renderScene = (
return acc;
}, [] as { angle: number; elementX1: number; elementY1: number; elementX2: number; elementY2: number; selectionColors: string[] }[]);
function addSelectionForGroupId(groupId: GroupId) {
const addSelectionForGroupId = (groupId: GroupId) => {
const groupElements = getElementsInGroup(elements, groupId);
const [elementX1, elementY1, elementX2, elementY2] = getCommonBounds(
groupElements,
@ -347,7 +347,7 @@ export const renderScene = (
elementY2,
selectionColors: [oc.black],
});
}
};
for (const groupId of getSelectedGroupIds(appState)) {
// TODO: support multiplayer selected group IDs

View File

@ -10,11 +10,11 @@ import {
export const normalizeScroll = (pos: number) =>
Math.floor(pos) as FlooredNumber;
function isOutsideViewPort(
const isOutsideViewPort = (
appState: AppState,
canvas: HTMLCanvasElement | null,
cords: Array<number>,
) {
) => {
const [x1, y1, x2, y2] = cords;
const { x: viewportX1, y: viewportY1 } = sceneCoordsToViewportCoords(
{ sceneX: x1, sceneY: y1 },
@ -28,7 +28,7 @@ function isOutsideViewPort(
viewportX2 - viewportX1 > appState.width ||
viewportY2 - viewportY1 > appState.height
);
}
};
export const centerScrollOn = ({
scenePoint,

View File

@ -29,7 +29,7 @@ beforeEach(async () => {
render(<App />);
});
function createAndSelectTwoRectangles() {
const createAndSelectTwoRectangles = () => {
UI.clickTool("rectangle");
mouse.down();
mouse.up(100, 100);
@ -44,9 +44,9 @@ function createAndSelectTwoRectangles() {
Keyboard.withModifierKeys({ shift: true }, () => {
mouse.click();
});
}
};
function createAndSelectTwoRectanglesWithDifferentSizes() {
const createAndSelectTwoRectanglesWithDifferentSizes = () => {
UI.clickTool("rectangle");
mouse.down();
mouse.up(100, 100);
@ -61,7 +61,7 @@ function createAndSelectTwoRectanglesWithDifferentSizes() {
Keyboard.withModifierKeys({ shift: true }, () => {
mouse.click();
});
}
};
it("aligns two objects correctly to the top", () => {
createAndSelectTwoRectangles();
@ -185,7 +185,7 @@ it("centers two objects with different sizes correctly horizontally", () => {
expect(API.getSelectedElements()[1].y).toEqual(110);
});
function createAndSelectGroupAndRectangle() {
const createAndSelectGroupAndRectangle = () => {
UI.clickTool("rectangle");
mouse.down();
mouse.up(100, 100);
@ -213,7 +213,7 @@ function createAndSelectGroupAndRectangle() {
Keyboard.withModifierKeys({ shift: true }, () => {
mouse.click();
});
}
};
it("aligns a group with another element correctly to the top", () => {
createAndSelectGroupAndRectangle();
@ -299,7 +299,7 @@ it("centers a group with another element correctly horizontally", () => {
expect(API.getSelectedElements()[2].x).toEqual(100);
});
function createAndSelectTwoGroups() {
const createAndSelectTwoGroups = () => {
UI.clickTool("rectangle");
mouse.down();
mouse.up(100, 100);
@ -339,7 +339,7 @@ function createAndSelectTwoGroups() {
Keyboard.withModifierKeys({ shift: true }, () => {
mouse.click();
});
}
};
it("aligns two groups correctly to the top", () => {
createAndSelectTwoGroups();
@ -437,7 +437,7 @@ it("centers two groups correctly horizontally", () => {
expect(API.getSelectedElements()[3].x).toEqual(200);
});
function createAndSelectNestedGroupAndRectangle() {
const createAndSelectNestedGroupAndRectangle = () => {
UI.clickTool("rectangle");
mouse.down();
mouse.up(100, 100);
@ -480,7 +480,7 @@ function createAndSelectNestedGroupAndRectangle() {
Keyboard.withModifierKeys({ shift: true }, () => {
mouse.click();
});
}
};
it("aligns nested group and other element correctly to the top", () => {
createAndSelectNestedGroupAndRectangle();

View File

@ -242,12 +242,12 @@ const RE_RTL_CHECK = new RegExp(`^[^${RS_LTR_CHARS}]*[${RS_RTL_CHARS}]`);
*/
export const isRTL = (text: string) => RE_RTL_CHECK.test(text);
export function tupleToCoors(
export const tupleToCoors = (
xyTuple: readonly [number, number],
): { x: number; y: number } {
): { x: number; y: number } => {
const [x, y] = xyTuple;
return { x, y };
}
};
/** use as a rejectionHandler to mute filesystem Abort errors */
export const muteFSAbortError = (error?: Error) => {