[Canvas][Layout Engine] TypeScript conversion - Stage I (#29390) (#30927)

* Perf: avoid matrix transpose in canvas layout that served only initial docu purpose, don't cons extra arrays
* Chore: switch to ES2015 import/export and direct function access
* Chore: TS conversion for some of the layout engine files
* Chore: rework TS linting for all files under `aeroelastic` (even the `.js` ones)
This commit is contained in:
Robert Monfera 2019-02-13 14:12:54 +01:00 committed by GitHub
parent 52107b7ad1
commit f8e6dcdf62
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 2098 additions and 2207 deletions

View file

@ -6,7 +6,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import aero from '../../lib/aeroelastic';
import { toCSS } from '../../lib/aeroelastic';
export const AlignmentGuide = ({ transformMatrix, width, height }) => {
const newStyle = {
@ -16,7 +16,7 @@ export const AlignmentGuide = ({ transformMatrix, width, height }) => {
marginTop: -height / 2,
background: 'magenta',
position: 'absolute',
transform: aero.dom.matrixToCSS(transformMatrix),
transform: toCSS(transformMatrix),
};
return (
<div

View file

@ -6,7 +6,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import aero from '../../lib/aeroelastic';
import { toCSS } from '../../lib/aeroelastic';
export const BorderConnection = ({ transformMatrix, width, height }) => {
const newStyle = {
@ -15,7 +15,7 @@ export const BorderConnection = ({ transformMatrix, width, height }) => {
marginLeft: -width / 2,
marginTop: -height / 2,
position: 'absolute',
transform: aero.dom.matrixToCSS(transformMatrix),
transform: toCSS(transformMatrix),
};
return <div className="canvasBorder--connection canvasLayoutAnnotation" style={newStyle} />;
};

View file

@ -6,12 +6,12 @@
import React from 'react';
import PropTypes from 'prop-types';
import aero from '../../lib/aeroelastic';
import { toCSS } from '../../lib/aeroelastic';
export const BorderResizeHandle = ({ transformMatrix }) => (
<div
className="canvasBorderResizeHandle canvasLayoutAnnotation"
style={{ transform: aero.dom.matrixToCSS(transformMatrix) }}
style={{ transform: toCSS(transformMatrix) }}
/>
);

View file

@ -6,7 +6,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import aero from '../../lib/aeroelastic';
import { toCSS } from '../../lib/aeroelastic';
export const HoverAnnotation = ({ transformMatrix, width, height }) => {
const newStyle = {
@ -14,7 +14,7 @@ export const HoverAnnotation = ({ transformMatrix, width, height }) => {
height,
marginLeft: -width / 2,
marginTop: -height / 2,
transform: aero.dom.matrixToCSS(transformMatrix),
transform: toCSS(transformMatrix),
};
return <div className="canvasHoverAnnotation canvasLayoutAnnotation" style={newStyle} />;
};

View file

@ -6,7 +6,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import aero from '../../lib/aeroelastic';
import { toCSS } from '../../lib/aeroelastic';
export const Positionable = ({ children, transformMatrix, width, height }) => {
// Throw if there is more than one child
@ -19,7 +19,7 @@ export const Positionable = ({ children, transformMatrix, width, height }) => {
marginLeft: -width / 2,
marginTop: -height / 2,
position: 'absolute',
transform: aero.dom.matrixToCSS(transformMatrix.map((n, i) => (i < 12 ? n : Math.round(n)))),
transform: toCSS(transformMatrix.map((n, i) => (i < 12 ? n : Math.round(n)))),
};
const stepChild = React.cloneElement(child, { size: { width, height } });

View file

@ -6,12 +6,12 @@
import React from 'react';
import PropTypes from 'prop-types';
import aero from '../../lib/aeroelastic';
import { toCSS } from '../../lib/aeroelastic';
export const RotationHandle = ({ transformMatrix }) => (
<div
className="canvasRotationHandle canvasRotationHandle--connector canvasLayoutAnnotation"
style={{ transform: aero.dom.matrixToCSS(transformMatrix) }}
style={{ transform: toCSS(transformMatrix) }}
>
<div className="canvasRotationHandle--handle" />
</div>

View file

@ -1,69 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
/**
* Mock config
*/
const adHocGroupName = 'adHocGroup';
const alignmentGuideName = 'alignmentGuide';
const atopZ = 1000;
const depthSelect = true;
const devColor = 'magenta';
const groupName = 'group';
const groupResize = true;
const guideDistance = 3;
const hoverAnnotationName = 'hoverAnnotation';
const hoverLift = 100;
const intraGroupManipulation = false;
const intraGroupSnapOnly = false;
const persistentGroupName = 'persistentGroup';
const resizeAnnotationOffset = 0;
const resizeAnnotationOffsetZ = 0.1; // causes resize markers to be slightly above the shape plane
const resizeAnnotationSize = 10;
const resizeAnnotationConnectorOffset = 0; //resizeAnnotationSize //+ 2
const resizeConnectorName = 'resizeConnector';
const rotateAnnotationOffset = 12;
const rotationEpsilon = 0.001;
const rotationHandleName = 'rotationHandle';
const rotationHandleSize = 14;
const resizeHandleName = 'resizeHandle';
const rotateSnapInPixels = 10;
const shortcuts = false;
const singleSelect = false;
const snapConstraint = true;
const minimumElementSize = 0; // guideDistance / 2 + 1;
module.exports = {
adHocGroupName,
alignmentGuideName,
atopZ,
depthSelect,
devColor,
groupName,
groupResize,
guideDistance,
hoverAnnotationName,
hoverLift,
intraGroupManipulation,
intraGroupSnapOnly,
minimumElementSize,
persistentGroupName,
resizeAnnotationOffset,
resizeAnnotationOffsetZ,
resizeAnnotationSize,
resizeAnnotationConnectorOffset,
resizeConnectorName,
resizeHandleName,
rotateAnnotationOffset,
rotationEpsilon,
rotateSnapInPixels,
rotationHandleName,
rotationHandleSize,
shortcuts,
singleSelect,
snapConstraint,
};

View file

@ -4,14 +4,11 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { transformMatrix3d } from './types';
// converts a transform matrix to a CSS string
const matrixToCSS = transformMatrix =>
export const matrixToCSS = (transformMatrix: transformMatrix3d): string =>
transformMatrix ? 'matrix3d(' + transformMatrix.join(',') + ')' : 'translate3d(0,0,0)';
// converts to string, and adds `px` if non-zero
const px = value => (value === 0 ? '0' : value + 'px');
module.exports = {
matrixToCSS,
px,
};
export const px = (value: number): string => (value === 0 ? '0' : value + 'px');

View file

@ -12,7 +12,7 @@
* @param {*[][]} arrays
* @returns *[]
*/
const flatten = arrays => [].concat(...arrays);
export const flatten = arrays => [].concat(...arrays);
/**
* identity
@ -20,7 +20,7 @@ const flatten = arrays => [].concat(...arrays);
* @param d
* @returns d
*/
const identity = d => d;
export const identity = d => d;
/**
* map
@ -32,19 +32,7 @@ const identity = d => d;
* @param {Function} fun
* @returns {function(*): *}
*/
const map = fun => array => array.map(value => fun(value));
/**
* log
*
* @param d
* @param {Function} printerFun
* @returns d
*/
const log = (d, printerFun = identity) => {
console.log(printerFun(d));
return d;
};
export const map = fun => array => array.map(value => fun(value));
/**
* disjunctiveUnion
@ -54,7 +42,7 @@ const log = (d, printerFun = identity) => {
* @param {*[]} set2
* @returns *[]
*/
const disjunctiveUnion = (keyFun, set1, set2) =>
export const disjunctiveUnion = (keyFun, set1, set2) =>
set1
.filter(s1 => !set2.find(s2 => keyFun(s2) === keyFun(s1)))
.concat(set2.filter(s2 => !set1.find(s1 => keyFun(s1) === keyFun(s2))));
@ -65,9 +53,9 @@ const disjunctiveUnion = (keyFun, set1, set2) =>
* @param {number} b
* @returns {number} the mean of the two parameters
*/
const mean = (a, b) => (a + b) / 2;
export const mean = (a, b) => (a + b) / 2;
const shallowEqual = (a, b) => {
export const shallowEqual = (a, b) => {
if (a === b) {
return true;
}
@ -82,31 +70,21 @@ const shallowEqual = (a, b) => {
return true;
};
const not = fun => (...args) => !fun(...args);
export const not = fun => (...args) => !fun(...args);
const removeDuplicates = (idFun, a) =>
export const removeDuplicates = (idFun, a) =>
a.filter((d, i) => a.findIndex(s => idFun(s) === idFun(d)) === i);
const arrayToMap = a => Object.assign({}, ...a.map(d => ({ [d]: true })));
export const arrayToMap = a => Object.assign({}, ...a.map(d => ({ [d]: true })));
const subMultitree = (pk, fk, elements, roots) => {
export const subMultitree = (pk, fk, elements, inputRoots) => {
const getSubgraphs = roots => {
const children = flatten(roots.map(r => elements.filter(e => fk(e) === pk(r))));
return [...roots, ...(children.length && getSubgraphs(children, elements))];
if (children.length) {
return [...roots, ...getSubgraphs(children, elements)];
} else {
return roots;
}
};
return getSubgraphs(roots);
};
module.exports = {
arrayToMap,
disjunctiveUnion,
flatten,
subMultitree,
identity,
log,
map,
mean,
not,
removeDuplicates,
shallowEqual,
return getSubgraphs(inputRoots);
};

View file

@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
const matrix = require('./matrix');
import { invert, mvMultiply, normalize, ORIGIN } from './matrix';
/**
* Pure calculations with geometry awareness - a set of rectangles with known size (a, b) and projection (transform matrix)
@ -40,9 +40,9 @@ const shapesAtPoint = (shapes, x, y) =>
// Determine z (depth) by composing the x, y vector out of local unit x and unit y vectors; by knowing the
// scalar multipliers for the unit x and unit y vectors, we can determine z from their respective 'slope' (gradient)
const centerPoint = matrix.normalize(matrix.mvMultiply(transformMatrix, matrix.ORIGIN));
const rightPoint = matrix.normalize(matrix.mvMultiply(transformMatrix, [1, 0, 0, 1]));
const upPoint = matrix.normalize(matrix.mvMultiply(transformMatrix, [0, 1, 0, 1]));
const centerPoint = normalize(mvMultiply(transformMatrix, ORIGIN));
const rightPoint = normalize(mvMultiply(transformMatrix, [1, 0, 0, 1]));
const upPoint = normalize(mvMultiply(transformMatrix, [0, 1, 0, 1]));
const x0 = rightPoint[0] - centerPoint[0];
const y0 = rightPoint[1] - centerPoint[1];
const x1 = upPoint[0] - centerPoint[0];
@ -58,8 +58,8 @@ const shapesAtPoint = (shapes, x, y) =>
// Hmm maybe we should reuse the above right and up unit vectors to establish whether we're within the (a, b) 'radius'
// rather than using matrix inversion. Bound to be cheaper.
const inverseProjection = matrix.invert(transformMatrix);
const intersection = matrix.normalize(matrix.mvMultiply(inverseProjection, [x, y, z, 1]));
const inverseProjection = invert(transformMatrix);
const intersection = normalize(mvMultiply(inverseProjection, [x, y, z, 1]));
const [sx, sy] = intersection;
// z is needed downstream, to tell which one is the closest shape hit by an x, y ray (shapes can be tilted in z)
@ -76,18 +76,13 @@ const shapesAtPoint = (shapes, x, y) =>
//
// If it were a right handed coordinate system, AND Y still pointed down, then Z should increase away from the
// viewer. But that's not the case. So we maximize the Z value to tell what's on top.
const shapesAt = (shapes, { x, y }) =>
export const shapesAt = (shapes, { x, y }) =>
shapesAtPoint(shapes, x, y)
.filter(shape => shape.inside)
.sort((shape1, shape2) => shape2.z - shape1.z || shape2.index - shape1.index) // stable sort: DOM insertion order!!!
.map(shape => shape.shape); // decreasing order, ie. from front (closest to viewer) to back
const getExtremum = (transformMatrix, a, b) =>
matrix.normalize(matrix.mvMultiply(transformMatrix, [a, b, 0, 1]));
const getExtremum = (transformMatrix, a, b) => normalize(mvMultiply(transformMatrix, [a, b, 0, 1]));
const landmarkPoint = (a, b, transformMatrix, k, l) => getExtremum(transformMatrix, k * a, l * b);
module.exports = {
landmarkPoint,
shapesAt,
};
export const landmarkPoint = (a, b, transformMatrix, k, l) =>
getExtremum(transformMatrix, k * a, l * b);

View file

@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
const { select, selectReduce } = require('./state');
import { select, selectReduce } from './state';
// Only needed to shuffle some modifier keys for Apple keyboards as per vector editing software conventions,
// so it's OK that user agent strings are not reliable; in case it's spoofed, it'll just work with a slightly
@ -43,15 +43,16 @@ const keyFromMouse = select(({ type, payload: { altKey, metaKey, shiftKey, ctrlK
type === 'cursorPosition' || type === 'mouseEvent' ? { altKey, metaKey, shiftKey, ctrlKey } : {}
)(primaryUpdate);
const metaHeld = select(appleKeyboard ? e => e.metaKey : e => e.altKey)(keyFromMouse);
const optionHeld = select(appleKeyboard ? e => e.altKey : e => e.ctrlKey)(keyFromMouse);
const shiftHeld = select(e => e.shiftKey)(keyFromMouse);
export const metaHeld = select(appleKeyboard ? e => e.metaKey : e => e.altKey)(keyFromMouse);
export const optionHeld = select(appleKeyboard ? e => e.altKey : e => e.ctrlKey)(keyFromMouse);
export const shiftHeld = select(e => e.shiftKey)(keyFromMouse);
const cursorPosition = selectReduce((previous, position) => position || previous, { x: 0, y: 0 })(
rawCursorPosition
);
export const cursorPosition = selectReduce((previous, position) => position || previous, {
x: 0,
y: 0,
})(rawCursorPosition);
const mouseButton = selectReduce(
export const mouseButton = selectReduce(
(prev, next) => {
if (!next) {
return prev;
@ -66,12 +67,12 @@ const mouseButton = selectReduce(
{ down: false, uid: null }
)(mouseButtonEvent);
const mouseIsDown = selectReduce(
export const mouseIsDown = selectReduce(
(previous, next) => (next ? next.event === 'mouseDown' : previous),
false
)(mouseButtonEvent);
const gestureEnd = select(
export const gestureEnd = select(
action =>
action &&
(action.type === 'actionEvent' ||
@ -85,39 +86,39 @@ const gestureEnd = select(
* Edit: http://stable.ascii-flow.appspot.com/#567671116534197027/776257435
*
*
* mouseIsDown
* initial state: 'up' +-----------> 'downed'
* ^ ^ + +
* | | !mouseIsDown | |
* !mouseIsDown | +-----------------+ | mouseIsDown && movedAlready
* | |
* +----+ 'dragging' <----+
* mouseNowDown
* initial state: 'up' +------------> 'downed'
* ^ ^ + +
* | | !mouseNowDown | |
* !mouseNowDown | +------------------+ | mouseNowDown && movedAlready
* | |
* +----+ 'dragging' <-----+
* + ^
* | |
* +------+
* mouseIsDown
* mouseNowDown
*
*/
const mouseButtonStateTransitions = (state, mouseIsDown, movedAlready) => {
const mouseButtonStateTransitions = (state, mouseNowDown, movedAlready) => {
switch (state) {
case 'up':
return mouseIsDown ? 'downed' : 'up';
return mouseNowDown ? 'downed' : 'up';
case 'downed':
if (mouseIsDown) {
if (mouseNowDown) {
return movedAlready ? 'dragging' : 'downed';
} else {
return 'up';
}
case 'dragging':
return mouseIsDown ? 'dragging' : 'up';
return mouseNowDown ? 'dragging' : 'up';
}
};
const mouseButtonState = selectReduce(
({ buttonState, downX, downY }, mouseIsDown, { x, y }) => {
({ buttonState, downX, downY }, mouseNowDown, { x, y }) => {
const movedAlready = x !== downX || y !== downY;
const newButtonState = mouseButtonStateTransitions(buttonState, mouseIsDown, movedAlready);
const newButtonState = mouseButtonStateTransitions(buttonState, mouseNowDown, movedAlready);
return {
buttonState: newButtonState,
downX: newButtonState === 'downed' ? x : downX,
@ -127,11 +128,11 @@ const mouseButtonState = selectReduce(
{ buttonState: 'up', downX: null, downY: null }
)(mouseIsDown, cursorPosition);
const mouseDowned = select(state => state.buttonState === 'downed')(mouseButtonState);
export const mouseDowned = select(state => state.buttonState === 'downed')(mouseButtonState);
const dragging = select(state => state.buttonState === 'dragging')(mouseButtonState);
export const dragging = select(state => state.buttonState === 'dragging')(mouseButtonState);
const dragVector = select(({ buttonState, downX, downY }, { x, y }) => ({
export const dragVector = select(({ buttonState, downX, downY }, { x, y }) => ({
down: buttonState !== 'up',
x0: downX,
y0: downY,
@ -139,20 +140,6 @@ const dragVector = select(({ buttonState, downX, downY }, { x, y }) => ({
y1: y,
}))(mouseButtonState, cursorPosition);
const actionEvent = select(action => (action.type === 'actionEvent' ? action.payload : null))(
primaryUpdate
);
module.exports = {
actionEvent,
dragging,
dragVector,
cursorPosition,
gestureEnd,
metaHeld,
mouseButton,
mouseDowned,
mouseIsDown,
optionHeld,
shiftHeld,
};
export const actionEvent = select(action =>
action.type === 'actionEvent' ? action.payload : null
)(primaryUpdate);

View file

@ -4,18 +4,13 @@
* you may not use this file except in compliance with the Elastic License.
*/
const dom = require('./dom');
const geometry = require('./geometry');
const gestures = require('./gestures');
const layout = require('./layout');
const matrix = require('./matrix');
const state = require('./state');
import { matrixToCSS } from './dom';
import { nextScene } from './layout';
import { primaryUpdate } from './layout_functions';
import { multiply, rotateZ, translate } from './matrix';
import { createStore, select } from './state';
module.exports = {
dom,
geometry,
gestures,
layout,
matrix,
state,
};
export const layout = { nextScene, primaryUpdate };
export const matrix = { multiply, rotateZ, translate };
export const state = { createStore, select };
export const toCSS = matrixToCSS;

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,345 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
/*eslint no-unused-vars: ["error", { "argsIgnorePattern": "^_" }]*/
/**
* transpose
*
* Turns a row major ordered vector representation of a 4 x 4 matrix into a column major ordered vector representation, or
* the other way around.
*
* Must pass a row major ordered vector if the goal is to obtain a column major ordered vector.
*
* We're using row major order in the _source code_ as this results in the correct visual shape of the matrix, but
* `transform3d` needs column major order.
*
* This is what the matrix is: Eg. this is the equivalent matrix of `translate3d(${x}px, ${y}px, ${z}px)`:
*
* a e i m 1 0 0 x
* b f j n 0 1 0 y
* c g k o 0 0 1 z
* d h l p 0 0 0 1
*
* but it's _not_ represented as a 2D array or array of arrays. CSS3 `transform3d` expects it as this vector:
*
* [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p]
*
* so it's clear that the first _column vector_ corresponds to a, b, c, d but in source code, we must write a, e, i, m in
* the first row if we want to visually resemble the above 4x4 matrix, ie. if we don't want that us programmers transpose
* matrices in our heads.
*
*/
const transpose = ([a, e, i, m, b, f, j, n, c, g, k, o, d, h, l, p]) => [
a,
b,
c,
d,
e,
f,
g,
h,
i,
j,
k,
l,
m,
n,
o,
p,
];
const ORIGIN = [0, 0, 0, 1];
const NULLVECTOR = [0, 0, 0, 0];
const NULLMATRIX = transpose([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
const UNITMATRIX = transpose([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]);
// currently these functions expensively transpose; in a future version we can have way more efficient matrix operations
// (eg. pre-transpose)
const translate = (x, y, z) => transpose([1, 0, 0, x, 0, 1, 0, y, 0, 0, 1, z, 0, 0, 0, 1]);
const scale = (x, y, z) => transpose([x, 0, 0, 0, 0, y, 0, 0, 0, 0, z, 0, 0, 0, 0, 1]);
const shear = (x, y) => transpose([1, x, 0, 0, y, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]);
const perspective = d => transpose([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, -1 / d, 1]);
/**
* rotate
*
* @param {number} x the x coordinate of the vector around which to rotate
* @param {number} y the y coordinate of the vector around which to rotate
* @param {number} z the z coordinate of the vector around which to rotate
* @param {number} a rotation angle in radians
* @returns {number[][]} a 4x4 transform matrix in column major order
*/
const rotate = (x, y, z, a) => {
// it looks like the formula but inefficient; common terms could be precomputed, transpose can be avoided.
// an optimizing compiler eg. Google Closure Advanced could perform most of the optimizations and JIT also watches out
// for eg. common expressions
const sinA = Math.sin(a);
const coshAi = 1 - Math.cos(a);
return transpose([
1 + coshAi * (x * x - 1),
z * sinA + x * y * coshAi,
-y * sinA + x * y * coshAi,
0,
-z * sinA + x * y * coshAi,
1 + coshAi * (y * y - 1),
x * sinA + y * x * coshAi,
0,
y * sinA + x * z * coshAi,
-x * sinA + y * z * coshAi,
1 + coshAi * (z * z - 1),
0,
0,
0,
0,
1,
]);
};
/**
* rotate_ functions
*
* @param {number} a
* @returns {number[][]}
*
* Should be replaced with more efficient direct versions rather than going through the generic `rotate3d` function.
*/
const rotateX = a => rotate(1, 0, 0, a);
const rotateY = a => rotate(0, 1, 0, a);
const rotateZ = a => rotate(0, 0, 1, a);
/**
* multiply
*
* Matrix multiplies two matrices of column major format, returning the result in the same format
*
*
* A E I M
* B F J N
* C G K O
* D H L P
*
* a e i m . . . .
* b f j n . . . .
* c g k o . . . .
* d h l p . . . d * M + h * N + l * O + p * P
*
*/
const mult = (
[a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p],
[A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P]
) => [
a * A + e * B + i * C + m * D,
b * A + f * B + j * C + n * D,
c * A + g * B + k * C + o * D,
d * A + h * B + l * C + p * D,
a * E + e * F + i * G + m * H,
b * E + f * F + j * G + n * H,
c * E + g * F + k * G + o * H,
d * E + h * F + l * G + p * H,
a * I + e * J + i * K + m * L,
b * I + f * J + j * K + n * L,
c * I + g * J + k * K + o * L,
d * I + h * J + l * K + p * L,
a * M + e * N + i * O + m * P,
b * M + f * N + j * O + n * P,
c * M + g * N + k * O + o * P,
d * M + h * N + l * O + p * P,
];
const multiply = (...elements) =>
elements.slice(1).reduce((prev, next) => mult(prev, next), elements[0]);
/**
* mvMultiply
*
* Multiplies a matrix and a vector
*
*
* A
* B
* C
* D
*
* a e i m .
* b f j n .
* c g k o .
* d h l p d * A + h * B + l * C + p * D
*
*/
const mvMultiply = ([a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p], [A, B, C, D]) => [
a * A + e * B + i * C + m * D,
b * A + f * B + j * C + n * D,
c * A + g * B + k * C + o * D,
d * A + h * B + l * C + p * D,
];
const normalize = ([A, B, C, D]) => (D === 1 ? [A, B, C, D] : [A / D, B / D, C / D, 1]);
/**
* invert
*
* Inverts the matrix
*
* a e i m
* b f j n
* c g k o
* d h l p
*/
const invert = ([a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p]) => {
const inv = [
f * k * p - f * l * o - j * g * p + j * h * o + n * g * l - n * h * k,
-b * k * p + b * l * o + j * c * p - j * d * o - n * c * l + n * d * k,
b * g * p - b * h * o - f * c * p + f * d * o + n * c * h - n * d * g,
-b * g * l + b * h * k + f * c * l - f * d * k - j * c * h + j * d * g,
-e * k * p + e * l * o + i * g * p - i * h * o - m * g * l + m * h * k,
a * k * p - a * l * o - i * c * p + i * d * o + m * c * l - m * d * k,
-a * g * p + a * h * o + e * c * p - e * d * o - m * c * h + m * d * g,
a * g * l - a * h * k - e * c * l + e * d * k + i * c * h - i * d * g,
e * j * p - e * l * n - i * f * p + i * h * n + m * f * l - m * h * j,
-a * j * p + a * l * n + i * b * p - i * d * n - m * b * l + m * d * j,
a * f * p - a * h * n - e * b * p + e * d * n + m * b * h - m * d * f,
-a * f * l + a * h * j + e * b * l - e * d * j - i * b * h + i * d * f,
-e * j * o + e * k * n + i * f * o - i * g * n - m * f * k + m * g * j,
a * j * o - a * k * n - i * b * o + i * c * n + m * b * k - m * c * j,
-a * f * o + a * g * n + e * b * o - e * c * n - m * b * g + m * c * f,
a * f * k - a * g * j - e * b * k + e * c * j + i * b * g - i * c * f,
];
const det = a * inv[0] + b * inv[4] + c * inv[8] + d * inv[12];
if (det === 0) {
return false; // no solution
} else {
const recDet = 1 / det;
for (let index = 0; index < 16; index++) {
inv[index] *= recDet;
}
return inv;
}
};
const translateComponent = a => [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, a[12], a[13], a[14], 1];
const compositeComponent = ([a, b, c, d, e, f, g, h, i, j, k, l, _m, _n, _o, p]) => [
a,
b,
c,
d,
e,
f,
g,
h,
i,
j,
k,
l,
0,
0,
0,
p,
];
const add = (
[a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p],
[A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P]
) => [
a + A,
b + B,
c + C,
d + D,
e + E,
f + F,
g + G,
h + H,
i + I,
j + J,
k + K,
l + L,
m + M,
n + N,
o + O,
p + P,
];
const subtract = (
[a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p],
[A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P]
) => [
a - A,
b - B,
c - C,
d - D,
e - E,
f - F,
g - G,
h - H,
i - I,
j - J,
k - K,
l - L,
m - M,
n - N,
o - O,
p - P,
];
const reduceTransforms = transforms =>
transforms.length === 1
? transforms[0]
: transforms.slice(1).reduce((prev, next) => multiply(prev, next), transforms[0]);
// applies an arbitrary number of transforms - left to right - to a preexisting transform matrix
const applyTransforms = (transforms, previousTransformMatrix) =>
transforms.reduce((prev, next) => multiply(prev, next), previousTransformMatrix);
const clamp = (low, high, value) => Math.min(high, Math.max(low, value));
const matrixToAngle = transformMatrix => {
// clamping is needed, otherwise inevitable floating point inaccuracies can cause NaN
const z0 = Math.acos(clamp(-1, 1, transformMatrix[0]));
const z1 = Math.asin(clamp(-1, 1, transformMatrix[1]));
return z1 > 0 ? z0 : -z0;
};
module.exports = {
ORIGIN,
NULLVECTOR,
NULLMATRIX,
UNITMATRIX,
transpose,
translate,
shear,
rotateX,
rotateY,
rotateZ,
scale,
perspective,
matrixToAngle,
multiply,
mvMultiply,
invert,
normalize,
applyTransforms,
reduceTransforms,
translateComponent,
compositeComponent,
add,
subtract,
};

View file

@ -0,0 +1,285 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
/**
* Column major order:
*
* Instead of a row major ordered vector representation of a 4 x 4 matrix, we use column major ordered vectors.
*
* This is what the matrix is: Eg. this is the equivalent matrix of `translate3d(${x}px, ${y}px, ${z}px)`:
*
* a e i m 1 0 0 x
* b f j n 0 1 0 y
* c g k o 0 0 1 z
* d h l p 0 0 0 1
*
* but it's _not_ represented as a 2D array or array of arrays. CSS3 `transform3d` expects it as this vector:
*
* [a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p]
*
* so it's clear that the first _column vector_ corresponds to a, b, c, d.
*
*/
import { transformMatrix3d, vector3d } from './types';
const NANMATRIX = [
NaN,
NaN,
NaN,
NaN,
NaN,
NaN,
NaN,
NaN,
NaN,
NaN,
NaN,
NaN,
NaN,
NaN,
NaN,
NaN,
] as transformMatrix3d;
export const ORIGIN = [0, 0, 0, 1] as vector3d;
export const translate = (x: number, y: number, z: number): transformMatrix3d =>
[1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, x, y, z, 1] as transformMatrix3d;
export const scale = (x: number, y: number, z: number): transformMatrix3d =>
[x, 0, 0, 0, 0, y, 0, 0, 0, 0, z, 0, 0, 0, 0, 1] as transformMatrix3d;
export const rotateZ = (a: number): transformMatrix3d => {
const sinA = Math.sin(a);
const cosA = Math.cos(a);
return [cosA, -sinA, 0, 0, sinA, cosA, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1] as transformMatrix3d;
};
/**
* multiply
*
* Matrix multiplies two matrices of column major format, returning the result in the same format
*
*
* A E I M
* B F J N
* C G K O
* D H L P
*
* a e i m . . . .
* b f j n . . . .
* c g k o . . . .
* d h l p . . . d * M + h * N + l * O + p * P
*
*/
const mult = (
[a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p]: transformMatrix3d,
[A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P]: transformMatrix3d
): transformMatrix3d =>
[
a * A + e * B + i * C + m * D,
b * A + f * B + j * C + n * D,
c * A + g * B + k * C + o * D,
d * A + h * B + l * C + p * D,
a * E + e * F + i * G + m * H,
b * E + f * F + j * G + n * H,
c * E + g * F + k * G + o * H,
d * E + h * F + l * G + p * H,
a * I + e * J + i * K + m * L,
b * I + f * J + j * K + n * L,
c * I + g * J + k * K + o * L,
d * I + h * J + l * K + p * L,
a * M + e * N + i * O + m * P,
b * M + f * N + j * O + n * P,
c * M + g * N + k * O + o * P,
d * M + h * N + l * O + p * P,
] as transformMatrix3d;
export const multiply = (
first: transformMatrix3d,
...rest: transformMatrix3d[]
): transformMatrix3d => rest.reduce((prev, next) => mult(prev, next), first);
/**
* mvMultiply
*
* Multiplies a matrix and a vector
*
*
* A
* B
* C
* D
*
* a e i m .
* b f j n .
* c g k o .
* d h l p d * A + h * B + l * C + p * D
*
*/
export const mvMultiply = (
[a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p]: transformMatrix3d,
[A, B, C, D]: vector3d
): vector3d =>
[
a * A + e * B + i * C + m * D,
b * A + f * B + j * C + n * D,
c * A + g * B + k * C + o * D,
d * A + h * B + l * C + p * D,
] as vector3d;
export const normalize = ([A, B, C, D]: vector3d): vector3d =>
D === 1 ? ([A, B, C, D] as vector3d) : ([A / D, B / D, C / D, 1] as vector3d);
/**
* invert
*
* Inverts the matrix
*
* a e i m
* b f j n
* c g k o
* d h l p
*/
export const invert = ([
a,
b,
c,
d,
e,
f,
g,
h,
i,
j,
k,
l,
m,
n,
o,
p,
]: transformMatrix3d): transformMatrix3d => {
const inv = [
f * k * p - f * l * o - j * g * p + j * h * o + n * g * l - n * h * k,
-b * k * p + b * l * o + j * c * p - j * d * o - n * c * l + n * d * k,
b * g * p - b * h * o - f * c * p + f * d * o + n * c * h - n * d * g,
-b * g * l + b * h * k + f * c * l - f * d * k - j * c * h + j * d * g,
-e * k * p + e * l * o + i * g * p - i * h * o - m * g * l + m * h * k,
a * k * p - a * l * o - i * c * p + i * d * o + m * c * l - m * d * k,
-a * g * p + a * h * o + e * c * p - e * d * o - m * c * h + m * d * g,
a * g * l - a * h * k - e * c * l + e * d * k + i * c * h - i * d * g,
e * j * p - e * l * n - i * f * p + i * h * n + m * f * l - m * h * j,
-a * j * p + a * l * n + i * b * p - i * d * n - m * b * l + m * d * j,
a * f * p - a * h * n - e * b * p + e * d * n + m * b * h - m * d * f,
-a * f * l + a * h * j + e * b * l - e * d * j - i * b * h + i * d * f,
-e * j * o + e * k * n + i * f * o - i * g * n - m * f * k + m * g * j,
a * j * o - a * k * n - i * b * o + i * c * n + m * b * k - m * c * j,
-a * f * o + a * g * n + e * b * o - e * c * n - m * b * g + m * c * f,
a * f * k - a * g * j - e * b * k + e * c * j + i * b * g - i * c * f,
] as transformMatrix3d;
const det = a * inv[0] + b * inv[4] + c * inv[8] + d * inv[12];
if (det === 0) {
return NANMATRIX; // no real solution
} else {
const recDet = 1 / det;
for (let index = 0; index < 16; index++) {
inv[index] *= recDet;
}
return inv;
}
};
export const translateComponent = (a: transformMatrix3d): transformMatrix3d =>
[1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, a[12], a[13], a[14], 1] as transformMatrix3d;
export const compositeComponent = ([
a,
b,
c,
d,
e,
f,
g,
h,
i,
j,
k,
l,
m,
n,
o,
p,
]: transformMatrix3d): transformMatrix3d =>
[a, b, c, d, e, f, g, h, i, j, k, l, 0, 0, 0, p] as transformMatrix3d;
export const add = (
[a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p]: transformMatrix3d,
[A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P]: transformMatrix3d
): transformMatrix3d =>
[
a + A,
b + B,
c + C,
d + D,
e + E,
f + F,
g + G,
h + H,
i + I,
j + J,
k + K,
l + L,
m + M,
n + N,
o + O,
p + P,
] as transformMatrix3d;
export const subtract = (
[a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p]: transformMatrix3d,
[A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P]: transformMatrix3d
): transformMatrix3d =>
[
a - A,
b - B,
c - C,
d - D,
e - E,
f - F,
g - G,
h - H,
i - I,
j - J,
k - K,
l - L,
m - M,
n - N,
o - O,
p - P,
] as transformMatrix3d;
export const reduceTransforms = (transforms: transformMatrix3d[]): transformMatrix3d =>
transforms.length === 1
? transforms[0]
: transforms.slice(1).reduce((prev, next) => multiply(prev, next), transforms[0]);
const clamp = (low: number, high: number, value: number): number =>
Math.min(high, Math.max(low, value));
export const matrixToAngle = (transformMatrix: transformMatrix3d): number => {
// clamping is needed, otherwise inevitable floating point inaccuracies can cause NaN
const z0 = Math.acos(clamp(-1, 1, transformMatrix[0]));
const z1 = Math.asin(clamp(-1, 1, transformMatrix[1]));
return z1 > 0 ? z0 : -z0;
};

View file

@ -1,159 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
/**
* transpose
*
* Turns a row major ordered vector representation of a 4 x 4 matrix into a column major ordered vector representation, or
* the other way around.
*
* Must pass a row major ordered vector if the goal is to obtain a column major ordered vector.
*
* We're using row major order in the _source code_ as this results in the correct visual shape of the matrix, but
* `transform3d` needs column major order.
*
* This is what the matrix is: Eg. this is the equivalent matrix of `translate(${x}px, ${y}px)`:
*
* a d g 1 0 x
* b e h 0 1 y
* c f i 0 0 1
*
* but it's _not_ represented as a 2D array or array of arrays.
*
* [a, b, c, d, e, f, g, h, i]
*
*/
const transpose = ([a, d, g, b, e, h, c, f, i]) => [a, b, c, d, e, f, g, h, i];
const ORIGIN = [0, 0, 1];
const NULLVECTOR = [0, 0, 0];
const NULLMATRIX = transpose([0, 0, 0, 0, 0, 0, 0, 0, 0]);
const UNITMATRIX = transpose([1, 0, 0, 0, 1, 0, 0, 0, 1]);
// currently these functions expensively transpose; in a future version we can have way more efficient matrix operations
// (eg. pre-transpose)
const translate = (x, y) => transpose([1, 0, x, 0, 1, y, 0, 0, 1]);
const scale = (x, y) => transpose([x, 0, 0, 0, y, 0, 0, 0, 1]);
const shear = (x, y) => transpose([1, x, 0, y, 1, 0, 0, 0, 1]);
/**
* multiply
*
* Matrix multiplies two matrices of column major format, returning the result in the same format
*
*
* A D G
* B E H
* C F I
*
* a d g . . .
* b e h . . .
* c f i . . c * G + f * H + i * I
*
*/
const mult = ([a, b, c, d, e, f, g, h, i], [A, B, C, D, E, F, G, H, I]) => [
a * A + d * B + g * C,
b * A + e * B + h * C,
c * A + f * B + i * C,
a * D + d * E + g * F,
b * D + e * E + h * F,
c * D + f * E + i * F,
a * G + d * H + g * I,
b * G + e * H + h * I,
c * G + f * H + i * I,
];
const multiply = (...elements) =>
elements.slice(1).reduce((prev, next) => mult(prev, next), elements[0]);
/**
* mvMultiply
*
* Multiplies a matrix and a vector
*
*
* A
* B
* C
*
* a d g .
* b e h .
* c f i c * A + f * B + i * C
*
*/
const mvMultiply = ([a, b, c, d, e, f, g, h, i], [A, B, C]) => [
a * A + d * B + g * C,
b * A + e * B + h * C,
c * A + f * B + i * C,
];
const normalize = ([A, B, C]) => (C === 1 ? [A, B, C] : [A / C, B / C, 1]);
const add = ([a, b, c, d, e, f, g, h, i], [A, B, C, D, E, F, G, H, I]) => [
a + A,
b + B,
c + C,
d + D,
e + E,
f + F,
g + G,
h + H,
i + I,
];
const subtract = ([a, b, c, d, e, f, g, h, i], [A, B, C, D, E, F, G, H, I]) => [
a - A,
b - B,
c - C,
d - D,
e - E,
f - F,
g - G,
h - H,
i - I,
];
const reduceTransforms = transforms =>
transforms.length === 1
? transforms[0]
: transforms.slice(1).reduce((prev, next) => multiply(prev, next), transforms[0]);
// applies an arbitrary number of transforms - left to right - to a preexisting transform matrix
const applyTransforms = (transforms, previousTransformMatrix) =>
transforms.reduce((prev, next) => multiply(prev, next), previousTransformMatrix);
/**
*
* componentProduct
*
*/
const componentProduct = ([a, b, c], [A, B, C]) => [a * A, b * B, c * C];
module.exports = {
ORIGIN,
NULLVECTOR,
NULLMATRIX,
UNITMATRIX,
transpose,
translate,
shear,
scale,
multiply,
mvMultiply,
normalize,
applyTransforms,
reduceTransforms,
add,
subtract,
componentProduct,
};

View file

@ -0,0 +1,93 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { transformMatrix2d, vector2d } from './types';
export const ORIGIN = [0, 0, 1] as vector2d;
export const UNITMATRIX = [1, 0, 0, 0, 1, 0, 0, 0, 1] as transformMatrix2d;
export const translate = (x: number, y: number): transformMatrix2d =>
[1, 0, 0, 0, 1, 0, x, y, 1] as transformMatrix2d;
export const scale = (x: number, y: number): transformMatrix2d =>
[x, 0, 0, 0, y, 0, 0, 0, 1] as transformMatrix2d;
/**
* multiply
*
* Matrix multiplies two matrices of column major format, returning the result in the same format
*
*
* A D G
* B E H
* C F I
*
* a d g . . .
* b e h . . .
* c f i . . c * G + f * H + i * I
*
*/
const mult = (
[a, b, c, d, e, f, g, h, i]: transformMatrix2d,
[A, B, C, D, E, F, G, H, I]: transformMatrix2d
): transformMatrix2d =>
[
a * A + d * B + g * C,
b * A + e * B + h * C,
c * A + f * B + i * C,
a * D + d * E + g * F,
b * D + e * E + h * F,
c * D + f * E + i * F,
a * G + d * H + g * I,
b * G + e * H + h * I,
c * G + f * H + i * I,
] as transformMatrix2d;
export const multiply = (
first: transformMatrix2d,
...rest: transformMatrix2d[]
): transformMatrix2d => rest.reduce((prev, next) => mult(prev, next), first);
/**
* mvMultiply
*
* Multiplies a matrix and a vector
*
*
* A
* B
* C
*
* a d g .
* b e h .
* c f i c * A + f * B + i * C
*
*/
export const mvMultiply = (
[a, b, c, d, e, f, g, h, i]: transformMatrix2d,
[A, B, C]: vector2d
): vector2d => [a * A + d * B + g * C, b * A + e * B + h * C, c * A + f * B + i * C] as vector2d;
export const normalize = ([A, B, C]: vector2d): vector2d =>
C === 1 ? ([A, B, C] as vector2d) : ([A / C, B / C, 1] as vector2d);
export const add = (
[a, b, c, d, e, f, g, h, i]: transformMatrix2d,
[A, B, C, D, E, F, G, H, I]: transformMatrix2d
): transformMatrix2d =>
[a + A, b + B, c + C, d + D, e + E, f + F, g + G, h + H, i + I] as transformMatrix2d;
export const subtract = (
[a, b, c, d, e, f, g, h, i]: transformMatrix2d,
[A, B, C, D, E, F, G, H, I]: transformMatrix2d
): transformMatrix2d =>
[a - A, b - B, c - C, d - D, e - E, f - F, g - G, h - H, i - I] as transformMatrix2d;
export const componentProduct = ([a, b, c]: vector2d, [A, B, C]: vector2d): vector2d =>
[a * A, b * B, c * C] as vector2d;

View file

@ -4,47 +4,66 @@
* you may not use this file except in compliance with the Elastic License.
*/
const { shallowEqual } = require('./functional');
import {
ActionId,
ChangeCallbackFunction,
Meta,
NodeFunction,
NodeResult,
Payload,
TypeName,
UpdaterFunction,
} from './types';
/**
* PoC action dispatch
*/
export const shallowEqual = (a: any, b: any): boolean => {
if (a === b) {
return true;
}
if (a.length !== b.length) {
return false;
}
for (let i = 0; i < a.length; i++) {
if (a[i] !== b[i]) {
return false;
}
}
return true;
};
const makeUid = () => 1e11 + Math.floor((1e12 - 1e11) * Math.random());
const makeUid = (): ActionId => 1e11 + Math.floor((1e12 - 1e11) * Math.random());
const selectReduce = (fun, previousValue, mapFun = d => d, logFun) => (...inputs) => {
export const selectReduce = (fun: NodeFunction, previousValue: NodeResult): NodeFunction => (
...inputs: NodeFunction[]
): NodeResult => {
// last-value memoizing version of this single line function:
// (fun, previousValue) => (...inputs) => state => previousValue = fun(previousValue, ...inputs.map(input => input(state)))
let argumentValues = [];
let argumentValues = [] as NodeResult[];
let value = previousValue;
let prevValue = previousValue;
let mappedValue;
return state => {
return (state: NodeResult) => {
if (
shallowEqual(argumentValues, (argumentValues = inputs.map(input => input(state)))) &&
value === prevValue
) {
return mappedValue;
return value;
}
prevValue = value;
value = fun(prevValue, ...argumentValues);
if (logFun) {
logFun(value, argumentValues);
}
mappedValue = mapFun(value);
return mappedValue;
return value;
};
};
const select = (fun, logFun) => (...inputs) => {
export const select = (fun: NodeFunction): NodeFunction => (
...inputs: NodeFunction[]
): NodeResult => {
// last-value memoizing version of this single line function:
// fun => (...inputs) => state => fun(...inputs.map(input => input(state)))
let argumentValues = [];
let value;
let actionId;
return state => {
const lastActionId = state.primaryUpdate.payload.uid;
let argumentValues = [] as NodeResult[];
let value: NodeResult;
let actionId: ActionId;
return (state: NodeResult) => {
const lastActionId: ActionId = state.primaryUpdate.payload.uid;
if (
actionId === lastActionId ||
shallowEqual(argumentValues, (argumentValues = inputs.map(input => input(state))))
@ -54,21 +73,20 @@ const select = (fun, logFun) => (...inputs) => {
value = fun(...argumentValues);
actionId = lastActionId;
if (logFun) {
logFun(value, argumentValues);
}
return value;
};
};
const createStore = (initialState, onChangeCallback = () => {}) => {
export const createStore = (initialState: NodeResult, onChangeCallback: ChangeCallbackFunction) => {
let currentState = initialState;
let updater = state => state; // default: no side effect
let updater: UpdaterFunction = (state: NodeResult): NodeResult => state; // default: no side effect
const getCurrentState = () => currentState;
// const setCurrentState = newState => (currentState = newState);
const setUpdater = updaterFunction => (updater = updaterFunction);
const setUpdater = (updaterFunction: UpdaterFunction) => {
updater = updaterFunction;
};
const commit = (type, payload, meta = {}) => {
const commit = (type: TypeName, payload: Payload, meta: Meta = { silent: false }) => {
currentState = updater({
...currentState,
primaryUpdate: {
@ -81,14 +99,7 @@ const createStore = (initialState, onChangeCallback = () => {}) => {
}
};
const dispatch = (type, payload) => setTimeout(() => commit(type, payload));
const dispatch = (type: TypeName, payload: Payload) => commit(type, payload);
return { getCurrentState, setUpdater, commit, dispatch };
};
module.exports = {
createStore,
select,
selectReduce,
makeUid,
};

View file

@ -0,0 +1,30 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
type f64 = number; // eventual AssemblyScript compatibility; doesn't hurt with vanilla TS either
type f = f64; // shorthand
export type vector2d = [f, f, f] & ReadonlyArray<f> & { __nominal: 'vector2d' };
export type vector3d = [f, f, f, f] & ReadonlyArray<f> & { __nominal: 'vector3d' };
export type transformMatrix2d = [f, f, f, f, f, f, f, f, f] &
ReadonlyArray<f> & { __nominal: 'transformMatrix2d' };
export type transformMatrix3d = [f, f, f, f, f, f, f, f, f, f, f, f, f, f, f, f] &
ReadonlyArray<f> & { __nominal: 'transformMatrix3d' };
export interface Meta {
silent: boolean;
}
export type ActionId = number;
export type TypeName = string;
export type NodeResult = any;
export type Payload = any;
export type NodeFunction = (...args: any[]) => any;
export type UpdaterFunction = (arg: NodeResult) => NodeResult;
export type ChangeCallbackFunction = (
{ type, state }: { type: TypeName; state: NodeResult },
meta: Meta
) => void;

View file

@ -4,12 +4,12 @@
* you may not use this file except in compliance with the Elastic License.
*/
import aero from './aeroelastic';
import { layout, matrix, state } from './aeroelastic';
const stores = new Map();
export const aeroelastic = {
matrix: aero.matrix,
matrix,
clearStores() {
stores.clear();
@ -20,14 +20,14 @@ export const aeroelastic = {
throw new Error('Only a single aeroelastic store per page should exist');
}
stores.set(page, aero.state.createStore(initialState, onChangeCallback));
stores.set(page, state.createStore(initialState, onChangeCallback));
const updateScene = aero.state.select((nextScene, primaryUpdate) => ({
const updateScene = state.select((nextScene, primaryUpdate) => ({
shapeAdditions: nextScene.shapes,
primaryUpdate,
currentScene: nextScene,
configuration: nextScene.configuration,
}))(aero.layout.nextScene, aero.layout.primaryUpdate);
}))(layout.nextScene, layout.primaryUpdate);
stores.get(page).setUpdater(updateScene);
},

View file

@ -8,7 +8,6 @@ import { shallowEqual } from 'recompose';
import { aeroelastic as aero } from '../../lib/aeroelastic_kibana';
import { matrixToAngle } from '../../lib/aeroelastic/matrix';
import { arrayToMap, identity } from '../../lib/aeroelastic/functional';
import defaultConfiguration from '../../lib/aeroelastic/config';
import {
addElement,
removeElements,
@ -24,7 +23,38 @@ import { appReady } from '../actions/app';
import { setWorkpad } from '../actions/workpad';
import { getNodes, getPages, getSelectedPage, getSelectedElement } from '../selectors/workpad';
const isGroupId = id => id.startsWith(defaultConfiguration.groupName);
const aeroelasticConfiguration = {
adHocGroupName: 'adHocGroup',
alignmentGuideName: 'alignmentGuide',
atopZ: 1000,
depthSelect: true,
devColor: 'magenta',
groupName: 'group',
groupResize: true,
guideDistance: 3,
hoverAnnotationName: 'hoverAnnotation',
hoverLift: 100,
intraGroupManipulation: false,
intraGroupSnapOnly: false,
minimumElementSize: 0,
persistentGroupName: 'persistentGroup',
resizeAnnotationConnectorOffset: 0,
resizeAnnotationOffset: 0,
resizeAnnotationOffsetZ: 0.1, // causes resize markers to be slightly above the shape plane
resizeAnnotationSize: 10,
resizeConnectorName: 'resizeConnector',
resizeHandleName: 'resizeHandle',
rotateAnnotationOffset: 12,
rotateSnapInPixels: 10,
rotationEpsilon: 0.001,
rotationHandleName: 'rotationHandle',
rotationHandleSize: 14,
shortcuts: false,
singleSelect: false,
snapConstraint: true,
};
const isGroupId = id => id.startsWith(aeroelasticConfiguration.groupName);
/**
* elementToShape
@ -229,7 +259,7 @@ export const aeroelastic = ({ dispatch, getState }) => {
shapeAdditions: [],
primaryUpdate: null,
currentScene: { shapes: [] },
configuration: defaultConfiguration,
configuration: aeroelasticConfiguration,
},
onChangeCallback,
page