Feat: group resize for horizontal constituents (#23553)

Feat: group resize for horizontal constituents
This commit is contained in:
Robert Monfera 2018-10-03 09:08:48 +02:00 committed by GitHub
parent 52df40e42f
commit 57b4b144fc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 88 additions and 63 deletions

View file

@ -14,7 +14,7 @@ const atopZ = 1000;
const depthSelect = true;
const devColor = 'magenta';
const groupName = 'group';
const groupResize = false;
const groupResize = true;
const guideDistance = 3;
const hoverAnnotationName = 'hoverAnnotation';
const intraGroupManipulation = false;

View file

@ -79,7 +79,11 @@ const not = fun => (...args) => !fun(...args);
const removeDuplicates = (idFun, a) =>
a.filter((d, i) => a.findIndex(s => idFun(s) === idFun(d)) === i);
const epsilon = 1 / 1000;
const applyTolerance = d => Math.round(d / epsilon) * epsilon;
module.exports = {
applyTolerance,
disjunctiveUnion,
flatten,
identity,

View file

@ -28,6 +28,7 @@ const matrix2d = require('./matrix2d');
const config = require('./config');
const {
applyTolerance,
disjunctiveUnion,
identity,
flatten,
@ -904,19 +905,31 @@ function resizeAnnotation(shapes, selectedShapes, shape) {
// fixme left active: snap wobble. right active: opposite side wobble.
const a = snappedA(properShape);
const b = snappedB(properShape);
const resizeVertices =
config.groupResize || properShape.type !== 'group' // todo remove the limitation of no group resize
? [
[-1, -1, 315],
[1, -1, 45],
[1, 1, 135],
[-1, 1, 225], // corners
[0, -1, 0],
[1, 0, 90],
[0, 1, 180],
[-1, 0, 270], // edge midpoints
]
: [];
const groupedShape = shape =>
shape.parent === properShape.id &&
shape.type !== 'annotation' &&
shape.subtype !== config.adHocGroupName;
// fixme broaden resizableChild to other multiples of 90 degrees
const resizableChild = shape =>
shallowEqual(
matrix.compositeComponent(shape.localTransformMatrix).map(applyTolerance),
matrix.UNITMATRIX
);
const allowResize =
properShape.type !== 'group' ||
(config.groupResize && shapes.filter(groupedShape).every(resizableChild));
const resizeVertices = allowResize
? [
[-1, -1, 315],
[1, -1, 45],
[1, 1, 135],
[-1, 1, 225], // corners
[0, -1, 0],
[1, 0, 90],
[0, 1, 180],
[-1, 0, 270], // edge midpoints
]
: [];
const resizePoints = resizeVertices.map(resizePointAnnotations(shape.id, a, b));
const connectors = connectorVertices.map(resizeEdgeAnnotations(shape.id, a, b));
return [...resizePoints, ...connectors];
@ -1160,43 +1173,47 @@ const axisAlignedBoundingBoxShape = shapesToBox => {
return aabbShape;
};
const resizeGroup = (shapes, selectedShapes) => {
const extending = shape => {
const children = shapes.filter(s => s.parent === shape.id && s.type !== 'annotation');
const axisAlignedBoundingBox = getAABB(children);
const { a, b, localTransformMatrix, rigTransform } = projectAABB(axisAlignedBoundingBox);
const resizeGroup = (shapes, selectedShapes, elements) => {
if (!elements.length) return { shapes, selectedShapes };
const e = elements[0];
if (e.subtype !== 'adHocGroup') return { shapes, selectedShapes };
if (!e.baseAB) {
return {
...shape,
localTransformMatrix,
a,
b,
rigTransform,
deltaLocalTransformMatrix: matrix.multiply(
shape.localTransformMatrix,
matrix.invert(localTransformMatrix)
),
shapes: shapes.map(s => ({ ...s, childBaseAB: null, baseLocalTransformMatrix: null })),
selectedShapes,
};
};
const extender = (shapes, shape) => {
if (!shape.parent) return shape;
const parent = shapes.find(s => s.id === shape.parent);
return {
...shape,
localTransformMatrix: matrix.multiply(
shape.localTransformMatrix,
parent.deltaLocalTransformMatrix
),
};
};
const extendingIfNeeded = shape => (isAdHocGroup(shape) ? extending(shape) : shape);
const extenderIfNeeded = (shape, i, shapes) =>
isAdHocGroup(shape) || shape.type === 'annotation' ? shape : extender(shapes, shape);
const extendingShapes = shapes.map(extendingIfNeeded);
}
const groupScaleX = e.a / e.baseAB[0];
const groupScaleY = e.b / e.baseAB[1];
const groupScale = matrix.scale(groupScaleX, groupScaleY, 1);
return {
shapes: extendingShapes.map(extenderIfNeeded),
selectedShapes: selectedShapes
.map(extendingIfNeeded)
.map(d => extenderIfNeeded(d, undefined, extendingShapes)),
shapes: shapes.map(s => {
if (s.parent !== e.id || s.type === 'annotation') return s;
const childBaseAB = s.childBaseAB || [s.a, s.b];
const impliedScale = matrix.scale(...childBaseAB, 1);
const inverseImpliedScale = matrix.invert(impliedScale);
const baseLocalTransformMatrix = s.baseLocalTransformMatrix || s.localTransformMatrix;
const normalizedBaseLocalTransformMatrix = matrix.multiply(
baseLocalTransformMatrix,
impliedScale
);
const T = matrix.multiply(groupScale, normalizedBaseLocalTransformMatrix);
const backScaler = groupScale.map(d => Math.abs(d));
const transformShit = matrix.invert(backScaler);
const abTuple = matrix.mvMultiply(matrix.multiply(backScaler, impliedScale), [1, 1, 1, 1]);
return {
...s,
localTransformMatrix: matrix.multiply(
T,
matrix.multiply(inverseImpliedScale, transformShit)
),
a: abTuple[0],
b: abTuple[1],
childBaseAB,
baseLocalTransformMatrix,
};
}),
selectedShapes,
};
};
@ -1212,8 +1229,10 @@ const getLeafs = (descendCondition, allShapes, shapes) =>
const grouping = select((shapes, selectedShapes) => {
const preexistingAdHocGroups = shapes.filter(isAdHocGroup);
const freshSelectedShapes = shapes.filter(idsMatch(selectedShapes));
const freshNonSelectedShapes = shapes.filter(not(idsMatch(selectedShapes)));
const matcher = idsMatch(selectedShapes);
const selectedFn = shape => matcher(shape) && shape.type !== 'annotation';
const freshSelectedShapes = shapes.filter(selectedFn);
const freshNonSelectedShapes = shapes.filter(not(selectedFn));
const someSelectedShapesAreGrouped = selectedShapes.some(isOrBelongsToAdHocGroup);
const selectionOutsideGroup = !someSelectedShapesAreGrouped;
@ -1228,16 +1247,16 @@ const grouping = select((shapes, selectedShapes) => {
}
// preserve the current selection if the sole ad hoc group is being manipulated
if (
selectedShapes.length === 1 &&
contentShapes(shapes, selectedShapes)[0].subtype === 'adHocGroup'
)
return { shapes, selectedShapes };
const elements = contentShapes(shapes, selectedShapes);
if (selectedShapes.length === 1 && elements[0].subtype === 'adHocGroup') {
return config.groupResize
? resizeGroup(shapes, selectedShapes, elements)
: { shapes, selectedShapes };
}
// group items or extend group bounding box (if enabled)
if (selectedShapes.length < 2) {
// resize the group if needed (ad-hoc group resize is manipulated)
return config.groupResize ? resizeGroup(shapes, selectedShapes) : { shapes, selectedShapes };
return { shapes, selectedShapes };
} else {
// group together the multiple items
const group = axisAlignedBoundingBoxShape(freshSelectedShapes);
@ -1351,9 +1370,9 @@ const nextScene = select(
const selectedLeafShapes = getLeafs(
shape => shape.subtype === config.adHocGroupName,
shapes,
selectionState.shapes.map(
s => (s.type === 'annotation' ? shapes.find(ss => ss.id === s.parent) : s)
)
selectionState.shapes
.map(s => (s.type === 'annotation' ? shapes.find(ss => ss.id === s.parent) : s))
.filter(identity)
)
.filter(shape => shape.type !== 'annotation')
.map(s => s.id);

View file

@ -4,6 +4,8 @@
* you may not use this file except in compliance with the Elastic License.
*/
/*eslint no-unused-vars: ["error", { "argsIgnorePattern": "^_" }]*/
/**
* transpose
*
@ -231,9 +233,9 @@ const invert = ([a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p]) => {
}
};
const translateComponent = a => [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, a[12], a[13], a[14], a[15]];
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]) => [
const compositeComponent = ([a, b, c, d, e, f, g, h, i, j, k, l, _m, _n, _o, p]) => [
a,
b,
c,
@ -249,7 +251,7 @@ const compositeComponent = ([a, b, c, d, e, f, g, h, i, j, k, l]) => [
0,
0,
0,
1,
p,
];
const add = (