mirror of
https://bitbucket.org/wisemapping/wisemapping-frontend.git
synced 2024-11-22 06:37:56 +01:00
Migrate layout classes Ts
This commit is contained in:
parent
c0068b4fee
commit
036cf40f0c
@ -67,7 +67,7 @@ class DragTopic {
|
|||||||
const cy = Math.ceil(y - size.height / 2);
|
const cy = Math.ceil(y - size.height / 2);
|
||||||
this._elem2d.setPosition(cx, cy);
|
this._elem2d.setPosition(cx, cy);
|
||||||
|
|
||||||
// In case is not free, pivot must be draw ...
|
// In case is not free, pivot must be drawn ...
|
||||||
if (this.isConnected()) {
|
if (this.isConnected()) {
|
||||||
const parent = this.getConnectedToTopic();
|
const parent = this.getConnectedToTopic();
|
||||||
const predict = this._layoutManager.predict(
|
const predict = this._layoutManager.predict(
|
||||||
|
@ -1044,7 +1044,6 @@ abstract class Topic extends NodeGraph {
|
|||||||
const connector = targetTopic.getShrinkConnector();
|
const connector = targetTopic.getShrinkConnector();
|
||||||
if (connector) {
|
if (connector) {
|
||||||
connector.setVisibility(false);
|
connector.setVisibility(false);
|
||||||
targetTopic.setChildrenShrunken(true);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,21 +24,25 @@ import RootedTreeSet from './RootedTreeSet';
|
|||||||
abstract class AbstractBasicSorter extends ChildrenSorterStrategy {
|
abstract class AbstractBasicSorter extends ChildrenSorterStrategy {
|
||||||
private INTERNODE_VERTICAL_PADDING = 5;
|
private INTERNODE_VERTICAL_PADDING = 5;
|
||||||
|
|
||||||
computeChildrenIdByHeights(treeSet: RootedTreeSet, node: Node) {
|
computeChildrenIdByHeights(treeSet: RootedTreeSet, node: Node): Map<number, number> {
|
||||||
const result = {};
|
const result = new Map<number, number>();
|
||||||
this._computeChildrenHeight(treeSet, node, result);
|
this._computeChildrenHeight(treeSet, node, result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected _getVerticalPadding() {
|
getVerticalPadding(): number {
|
||||||
return this.INTERNODE_VERTICAL_PADDING;
|
return this.INTERNODE_VERTICAL_PADDING;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected _computeChildrenHeight(treeSet: RootedTreeSet, node: Node, heightCache?) {
|
_computeChildrenHeight(
|
||||||
|
treeSet: RootedTreeSet,
|
||||||
|
node: Node,
|
||||||
|
heightCache?: Map<number, number>,
|
||||||
|
): number {
|
||||||
// 2* Top and down padding;
|
// 2* Top and down padding;
|
||||||
const height = node.getSize().height + this._getVerticalPadding() * 2;
|
const height = node.getSize().height + this.getVerticalPadding() * 2;
|
||||||
|
|
||||||
let result;
|
let result: number;
|
||||||
const children = treeSet.getChildren(node);
|
const children = treeSet.getChildren(node);
|
||||||
if (children.length === 0 || node.areChildrenShrunken()) {
|
if (children.length === 0 || node.areChildrenShrunken()) {
|
||||||
result = height;
|
result = height;
|
||||||
@ -53,8 +57,7 @@ abstract class AbstractBasicSorter extends ChildrenSorterStrategy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (heightCache) {
|
if (heightCache) {
|
||||||
// eslint-disable-next-line no-param-reassign
|
heightCache.set(node.getId(), result);
|
||||||
heightCache[node.getId()] = result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
@ -233,13 +233,13 @@ class BalancedSorter extends AbstractBasicSorter {
|
|||||||
return 'Balanced Sorter';
|
return 'Balanced Sorter';
|
||||||
}
|
}
|
||||||
|
|
||||||
protected _getChildrenForOrder(parent: Node, graph: RootedTreeSet, order: number): Node[] {
|
_getChildrenForOrder(parent: Node, graph: RootedTreeSet, order: number): Node[] {
|
||||||
return this._getSortedChildren(graph, parent).filter(
|
return this._getSortedChildren(graph, parent).filter(
|
||||||
(child) => child.getOrder() % 2 === order % 2,
|
(child) => child.getOrder() % 2 === order % 2,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected _getVerticalPadding(): number {
|
getVerticalPadding(): number {
|
||||||
return BalancedSorter.INTERNODE_VERTICAL_PADDING;
|
return BalancedSorter.INTERNODE_VERTICAL_PADDING;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@ import Node from './Node';
|
|||||||
import PositionType from '../PositionType';
|
import PositionType from '../PositionType';
|
||||||
|
|
||||||
abstract class ChildrenSorterStrategy {
|
abstract class ChildrenSorterStrategy {
|
||||||
abstract computeChildrenIdByHeights(treeSet: RootedTreeSet, node: Node): void;
|
abstract computeChildrenIdByHeights(treeSet: RootedTreeSet, node: Node): Map<number, number>;
|
||||||
|
|
||||||
abstract computeOffsets(treeSet: RootedTreeSet, node: Node): void;
|
abstract computeOffsets(treeSet: RootedTreeSet, node: Node): void;
|
||||||
|
|
||||||
@ -33,13 +33,15 @@ abstract class ChildrenSorterStrategy {
|
|||||||
parent: Node,
|
parent: Node,
|
||||||
node: Node | null,
|
node: Node | null,
|
||||||
position: PositionType | null,
|
position: PositionType | null,
|
||||||
);
|
): void;
|
||||||
|
|
||||||
abstract verify(treeSet: RootedTreeSet, node: Node): void;
|
abstract verify(treeSet: RootedTreeSet, node: Node): void;
|
||||||
|
|
||||||
abstract getChildDirection(treeSet: RootedTreeSet, node: Node): 1 | -1;
|
abstract getChildDirection(treeSet: RootedTreeSet, node: Node): 1 | -1;
|
||||||
|
|
||||||
abstract toString(): string;
|
abstract toString(): string;
|
||||||
|
|
||||||
|
abstract getVerticalPadding(): number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ChildrenSorterStrategy;
|
export default ChildrenSorterStrategy;
|
||||||
|
@ -33,6 +33,10 @@ class Node {
|
|||||||
// eslint-disable-next-line no-use-before-define
|
// eslint-disable-next-line no-use-before-define
|
||||||
_children!: Node[];
|
_children!: Node[];
|
||||||
|
|
||||||
|
_branchHeight: number;
|
||||||
|
|
||||||
|
_heightChanged: boolean;
|
||||||
|
|
||||||
constructor(id: number, size: SizeType, position: PositionType, sorter: ChildrenSorterStrategy) {
|
constructor(id: number, size: SizeType, position: PositionType, sorter: ChildrenSorterStrategy) {
|
||||||
$assert(typeof id === 'number' && Number.isFinite(id), 'id can not be null');
|
$assert(typeof id === 'number' && Number.isFinite(id), 'id can not be null');
|
||||||
this._id = id;
|
this._id = id;
|
||||||
@ -42,6 +46,8 @@ class Node {
|
|||||||
this.setSize(size);
|
this.setSize(size);
|
||||||
this.setPosition(position);
|
this.setPosition(position);
|
||||||
this.setShrunken(false);
|
this.setShrunken(false);
|
||||||
|
this._branchHeight = -1;
|
||||||
|
this._heightChanged = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** */
|
/** */
|
||||||
|
@ -19,27 +19,26 @@ import { $assert, $defined } from '@wisemapping/core-js';
|
|||||||
import Node from './Node';
|
import Node from './Node';
|
||||||
import SymmetricSorter from './SymmetricSorter';
|
import SymmetricSorter from './SymmetricSorter';
|
||||||
import BalancedSorter from './BalancedSorter';
|
import BalancedSorter from './BalancedSorter';
|
||||||
|
import RootedTreeSet from './RootedTreeSet';
|
||||||
|
import SizeType from '../SizeType';
|
||||||
|
import PositionType from '../PositionType';
|
||||||
|
import ChildrenSorterStrategy from './ChildrenSorterStrategy';
|
||||||
|
|
||||||
class OriginalLayout {
|
class OriginalLayout {
|
||||||
constructor(treeSet) {
|
private _treeSet: RootedTreeSet;
|
||||||
|
|
||||||
|
constructor(treeSet: RootedTreeSet) {
|
||||||
this._treeSet = treeSet;
|
this._treeSet = treeSet;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** */
|
createNode(id: number, size: SizeType, position: PositionType, type: string): Node {
|
||||||
// eslint-disable-next-line class-methods-use-this
|
|
||||||
createNode(id, size, position, type) {
|
|
||||||
$assert($defined(id), 'id can not be null');
|
$assert($defined(id), 'id can not be null');
|
||||||
$assert(size, 'size can not be null');
|
const strategy: ChildrenSorterStrategy =
|
||||||
$assert(position, 'position can not be null');
|
|
||||||
$assert(type, 'type can not be null');
|
|
||||||
|
|
||||||
const strategy =
|
|
||||||
type === 'root' ? OriginalLayout.BALANCED_SORTER : OriginalLayout.SYMMETRIC_SORTER;
|
type === 'root' ? OriginalLayout.BALANCED_SORTER : OriginalLayout.SYMMETRIC_SORTER;
|
||||||
return new Node(id, size, position, strategy);
|
return new Node(id, size, position, strategy);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** */
|
connectNode(parentId: number, childId: number, order: number): void {
|
||||||
connectNode(parentId, childId, order) {
|
|
||||||
const parent = this._treeSet.find(parentId);
|
const parent = this._treeSet.find(parentId);
|
||||||
const child = this._treeSet.find(childId);
|
const child = this._treeSet.find(childId);
|
||||||
|
|
||||||
@ -54,11 +53,12 @@ class OriginalLayout {
|
|||||||
sorter.verify(this._treeSet, parent);
|
sorter.verify(this._treeSet, parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** */
|
disconnectNode(nodeId: number): void {
|
||||||
disconnectNode(nodeId) {
|
|
||||||
const node = this._treeSet.find(nodeId);
|
const node = this._treeSet.find(nodeId);
|
||||||
const parent = this._treeSet.getParent(node);
|
const parent = this._treeSet.getParent(node);
|
||||||
$assert(parent, 'Node already disconnected');
|
if (!parent) {
|
||||||
|
throw new Error('Node already disconnected');
|
||||||
|
}
|
||||||
|
|
||||||
// Make it fixed
|
// Make it fixed
|
||||||
node.setFree(false);
|
node.setFree(false);
|
||||||
@ -75,21 +75,18 @@ class OriginalLayout {
|
|||||||
parent.getSorter().verify(this._treeSet, parent);
|
parent.getSorter().verify(this._treeSet, parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** */
|
layout(): void {
|
||||||
layout() {
|
|
||||||
const roots = this._treeSet.getTreeRoots();
|
const roots = this._treeSet.getTreeRoots();
|
||||||
roots.forEach((node) => {
|
roots.forEach((node) => {
|
||||||
// Calculate all node heights ...
|
// Calculate all node heights ...
|
||||||
const sorter = node.getSorter();
|
const sorter = node.getSorter();
|
||||||
|
|
||||||
const heightById = sorter.computeChildrenIdByHeights(this._treeSet, node);
|
const heightById = sorter.computeChildrenIdByHeights(this._treeSet, node);
|
||||||
this._layoutChildren(node, heightById);
|
this._layoutChildren(node, heightById);
|
||||||
|
|
||||||
this._fixOverlapping(node, heightById);
|
this._fixOverlapping(node, heightById);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
_layoutChildren(node, heightById) {
|
private _layoutChildren(node: Node, heightById: Map<number, number>): void {
|
||||||
const nodeId = node.getId();
|
const nodeId = node.getId();
|
||||||
const children = this._treeSet.getChildren(node);
|
const children = this._treeSet.getChildren(node);
|
||||||
const parent = this._treeSet.getParent(node);
|
const parent = this._treeSet.getParent(node);
|
||||||
@ -98,9 +95,9 @@ class OriginalLayout {
|
|||||||
|
|
||||||
// If ether any of the nodes has been changed of position or the height of the children is not
|
// If ether any of the nodes has been changed of position or the height of the children is not
|
||||||
// the same, children nodes must be repositioned ....
|
// the same, children nodes must be repositioned ....
|
||||||
const newBranchHeight = heightById[nodeId];
|
const newBranchHeight = heightById.get(nodeId)!;
|
||||||
|
|
||||||
const parentHeightChanged = $defined(parent) ? parent._heightChanged : false;
|
const parentHeightChanged = parent ? parent._heightChanged : false;
|
||||||
const heightChanged = node._branchHeight !== newBranchHeight;
|
const heightChanged = node._branchHeight !== newBranchHeight;
|
||||||
// eslint-disable-next-line no-param-reassign
|
// eslint-disable-next-line no-param-reassign
|
||||||
node._heightChanged = heightChanged || parentHeightChanged;
|
node._heightChanged = heightChanged || parentHeightChanged;
|
||||||
@ -141,7 +138,6 @@ class OriginalLayout {
|
|||||||
me._treeSet.updateBranchPosition(child, newPos);
|
me._treeSet.updateBranchPosition(child, newPos);
|
||||||
});
|
});
|
||||||
|
|
||||||
// eslint-disable-next-line no-param-reassign
|
|
||||||
node._branchHeight = newBranchHeight;
|
node._branchHeight = newBranchHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -151,7 +147,7 @@ class OriginalLayout {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
_calculateAlignOffset(node, child, heightById) {
|
private _calculateAlignOffset(node: Node, child: Node, heightById: Map<number, number>): number {
|
||||||
if (child.isFree()) {
|
if (child.isFree()) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -167,8 +163,8 @@ class OriginalLayout {
|
|||||||
) {
|
) {
|
||||||
if (this._treeSet.hasSinglePathToSingleLeaf(child)) {
|
if (this._treeSet.hasSinglePathToSingleLeaf(child)) {
|
||||||
offset =
|
offset =
|
||||||
heightById[child.getId()] / 2 -
|
heightById.get(child.getId())! / 2 -
|
||||||
(childHeight + child.getSorter()._getVerticalPadding() * 2) / 2;
|
(childHeight + child.getSorter().getVerticalPadding() * 2) / 2;
|
||||||
} else {
|
} else {
|
||||||
offset = this._treeSet.isLeaf(child) ? 0 : -(childHeight - nodeHeight) / 2;
|
offset = this._treeSet.isLeaf(child) ? 0 : -(childHeight - nodeHeight) / 2;
|
||||||
}
|
}
|
||||||
@ -189,13 +185,14 @@ class OriginalLayout {
|
|||||||
return offset;
|
return offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
static _branchIsTaller(node, heightById) {
|
static _branchIsTaller(node: Node, heightById: Map<number, number>): boolean {
|
||||||
return (
|
return (
|
||||||
heightById[node.getId()] > node.getSize().height + node.getSorter()._getVerticalPadding() * 2
|
heightById.get(node.getId())! >
|
||||||
|
node.getSize().height + node.getSorter().getVerticalPadding() * 2
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
_fixOverlapping(node, heightById) {
|
private _fixOverlapping(node: Node, heightById: Map<number, number>): void {
|
||||||
const children = this._treeSet.getChildren(node);
|
const children = this._treeSet.getChildren(node);
|
||||||
|
|
||||||
if (node.isFree()) {
|
if (node.isFree()) {
|
||||||
@ -206,7 +203,7 @@ class OriginalLayout {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
_shiftBranches(node, heightById) {
|
_shiftBranches(node: Node, heightById: Map<number, number>): void {
|
||||||
const shiftedBranches = [node];
|
const shiftedBranches = [node];
|
||||||
|
|
||||||
const siblingsToShift = this._treeSet.getSiblingsInVerticalDirection(
|
const siblingsToShift = this._treeSet.getSiblingsInVerticalDirection(
|
||||||
@ -238,28 +235,22 @@ class OriginalLayout {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static _branchesOverlap(branchA, branchB, heightById) {
|
static _branchesOverlap(branchA: Node, branchB: Node, heightById: Map<number, number>): boolean {
|
||||||
// a branch doesn't really overlap with itself
|
// a branch doesn't really overlap with itself
|
||||||
if (branchA === branchB) {
|
if (branchA === branchB) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const topA = branchA.getPosition().y - heightById[branchA.getId()] / 2;
|
const topA = branchA.getPosition().y - heightById.get(branchA.getId())! / 2;
|
||||||
const bottomA = branchA.getPosition().y + heightById[branchA.getId()] / 2;
|
const bottomA = branchA.getPosition().y + heightById.get(branchA.getId())! / 2;
|
||||||
const topB = branchB.getPosition().y - heightById[branchB.getId()] / 2;
|
const topB = branchB.getPosition().y - heightById.get(branchB.getId())! / 2;
|
||||||
const bottomB = branchB.getPosition().y + heightById[branchB.getId()] / 2;
|
const bottomB = branchB.getPosition().y + heightById.get(branchB.getId())! / 2;
|
||||||
|
|
||||||
return !(topA >= bottomB || bottomA <= topB);
|
return !(topA >= bottomB || bottomA <= topB);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static SYMMETRIC_SORTER: ChildrenSorterStrategy = new SymmetricSorter();
|
||||||
|
|
||||||
|
static BALANCED_SORTER: ChildrenSorterStrategy = new BalancedSorter();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @type {mindplot.layout.SymmetricSorter}
|
|
||||||
*/
|
|
||||||
OriginalLayout.SYMMETRIC_SORTER = new SymmetricSorter();
|
|
||||||
/**
|
|
||||||
* @type {mindplot.layout.BalancedSorter}
|
|
||||||
*/
|
|
||||||
OriginalLayout.BALANCED_SORTER = new BalancedSorter();
|
|
||||||
|
|
||||||
export default OriginalLayout;
|
export default OriginalLayout;
|
@ -25,7 +25,13 @@ class SymmetricSorter extends AbstractBasicSorter {
|
|||||||
/**
|
/**
|
||||||
* Predict the order and position of a dragged node.
|
* Predict the order and position of a dragged node.
|
||||||
*/
|
*/
|
||||||
predict(graph: RootedTreeSet, parent: Node, node: Node, position: PositionType, free?: boolean) {
|
predict(
|
||||||
|
graph: RootedTreeSet,
|
||||||
|
parent: Node,
|
||||||
|
node: Node,
|
||||||
|
position: PositionType,
|
||||||
|
free?: boolean,
|
||||||
|
): [number, PositionType] {
|
||||||
const self = this;
|
const self = this;
|
||||||
const rootNode = graph.getRootNode(parent);
|
const rootNode = graph.getRootNode(parent);
|
||||||
|
|
||||||
@ -97,7 +103,7 @@ class SymmetricSorter extends AbstractBasicSorter {
|
|||||||
const last = parentChildren[parentChildren.length - 1];
|
const last = parentChildren[parentChildren.length - 1];
|
||||||
for (let i = 0; i < parentChildren.length; i++) {
|
for (let i = 0; i < parentChildren.length; i++) {
|
||||||
const parentChild = parentChildren[i];
|
const parentChild = parentChildren[i];
|
||||||
const nodeAfter = i + 1 === parentChildren.length ? null : parentChildren[i + 1];
|
const nodeAfter = parentChildren[i + 1];
|
||||||
|
|
||||||
// Fit at the bottom
|
// Fit at the bottom
|
||||||
if (!nodeAfter && position.y > parentChild.getPosition().y) {
|
if (!nodeAfter && position.y > parentChild.getPosition().y) {
|
||||||
@ -291,7 +297,7 @@ class SymmetricSorter extends AbstractBasicSorter {
|
|||||||
return 'Symmetric Sorter';
|
return 'Symmetric Sorter';
|
||||||
}
|
}
|
||||||
|
|
||||||
protected _getVerticalPadding() {
|
getVerticalPadding() {
|
||||||
return SymmetricSorter.INTERNODE_VERTICAL_PADDING;
|
return SymmetricSorter.INTERNODE_VERTICAL_PADDING;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user