diagram add references positioning

This commit is contained in:
Jan Prochazka 2022-01-16 17:07:27 +01:00
parent 22054cad12
commit 6e50979045
3 changed files with 48 additions and 13 deletions

View File

@ -305,12 +305,13 @@
...newTables.map(x => ({
...x,
designerId: uuidv1(),
needsArrange: true,
})),
],
};
};
const handleAddTableReferences = table => {
const handleAddTableReferences = async table => {
if (!dbInfo) return;
const db = $dbInfo;
if (!db) return;
@ -318,6 +319,10 @@
return getTablesWithReferences(db, table, current);
});
updateFromDbInfo();
await tick();
const rect = (domTables[table.designerId] as any)?.getRect();
arrange(true, false, rect ? { x: (rect.left + rect.right) / 2, y: (rect.top + rect.bottom) / 2 } : null);
};
const performAutoActions = async db => {
@ -466,13 +471,18 @@
return settings?.canArrange;
}
export function arrange(skipUndoChain = false) {
export function arrange(skipUndoChain = false, arrangeAll = true, circleMiddle = { x: 0, y: 0 }) {
const graph = new GraphDefinition();
for (const table of value?.tables || []) {
const domTable = domTables[table.designerId] as any;
if (!domTable) continue;
const rect = domTable.getRect();
graph.addNode(table.designerId, rect.right - rect.left, rect.bottom - rect.top);
graph.addNode(
table.designerId,
rect.right - rect.left,
rect.bottom - rect.top,
arrangeAll || table.needsArrange ? null : { x: rect.left + rect.right / 2, y: rect.top + rect.bottom / 2 }
);
}
for (const reference of value?.references) {
@ -481,7 +491,7 @@
graph.initialize();
const layout = GraphLayout.createCircle(graph).springyAlg().doMoveSteps().fixViewBox();
const layout = GraphLayout.createCircle(graph, circleMiddle).springyAlg().doMoveSteps().fixViewBox();
callChange(current => {
return {
@ -492,10 +502,14 @@
return node
? {
...table,
needsArrange: false,
left: node.x - node.node.width / 2,
top: node.y - node.node.height / 2,
}
: table;
: {
...table,
needsArrange: false,
};
}),
};
}, skipUndoChain);

View File

@ -1,5 +1,5 @@
import _ from 'lodash';
import { IBoxBounds, rectangleDistance, rectangleIntersectArea, Vector2D } from './designerMath';
import { IBoxBounds, IPoint, rectangleDistance, rectangleIntersectArea, Vector2D } from './designerMath';
const MIN_NODE_DISTANCE = 50;
const SPRING_LENGTH = 100;
@ -18,7 +18,13 @@ const SCORE_ASPECT_RATIO = 1.6;
class GraphNode {
neightboors: GraphNode[] = [];
radius: number;
constructor(public graph: GraphDefinition, public designerId: string, public width: number, public height: number) {}
constructor(
public graph: GraphDefinition,
public designerId: string,
public width: number,
public height: number,
public fixedPosition: IPoint
) {}
initialize() {
this.radius = Math.sqrt((this.width * this.width) / 4 + (this.height * this.height) / 4);
@ -43,8 +49,8 @@ export class GraphDefinition {
nodes: { [designerId: string]: GraphNode } = {};
edges: GraphEdge[] = [];
addNode(designerId: string, width: number, height: number) {
this.nodes[designerId] = new GraphNode(this, designerId, width, height);
addNode(designerId: string, width: number, height: number, fixedPosition: IPoint) {
this.nodes[designerId] = new GraphNode(this, designerId, width, height, fixedPosition);
}
addEdge(sourceId: string, targetId: string) {
@ -94,6 +100,7 @@ class LayoutNode {
}
translate(dx: number, dy: number) {
if (this.node.fixedPosition) return this;
return new LayoutNode(this.node, this.x + dx, this.y + dy);
}
@ -199,14 +206,18 @@ export class GraphLayout {
constructor(public graph: GraphDefinition) {}
static createCircle(graph: GraphDefinition): GraphLayout {
static createCircle(graph: GraphDefinition, middle: IPoint = { x: 0, y: 0 }): GraphLayout {
const res = new GraphLayout(graph);
if (_.isEmpty(graph.nodes)) return res;
const addedNodes = new Set<string>();
const circleSortedNodes: GraphNode[] = [];
addNodeNeighboors(_.values(graph.nodes), circleSortedNodes, addedNodes);
addNodeNeighboors(
_.values(graph.nodes).filter(x => !x.fixedPosition),
circleSortedNodes,
addedNodes
);
const nodeRadius = _.max(circleSortedNodes.map(x => x.radius));
const nodeCount = circleSortedNodes.length;
const radius = (nodeCount * nodeRadius) / (2 * Math.PI) + nodeRadius;
@ -214,9 +225,18 @@ export class GraphLayout {
let angle = 0;
const dangle = (2 * Math.PI) / circleSortedNodes.length;
for (const node of circleSortedNodes) {
res.nodes[node.designerId] = new LayoutNode(node, Math.sin(angle) * radius, Math.cos(angle) * radius);
res.nodes[node.designerId] = new LayoutNode(
node,
middle.x + Math.sin(angle) * radius,
middle.y + Math.cos(angle) * radius
);
angle += dangle;
}
for (const node of _.values(graph.nodes).filter(x => x.fixedPosition)) {
res.nodes[node.designerId] = new LayoutNode(node, node.fixedPosition.x, node.fixedPosition.y);
}
res.fillEdges();
return res;
@ -289,6 +309,7 @@ export class GraphLayout {
}
tryMoveNode(node: LayoutNode): GraphLayout[] {
if (node.node.fixedPosition) return [];
return [
this.changePositions(x => (x == node ? node.translate(MOVE_STEP, 0) : x)),
this.changePositions(x => (x == node ? node.translate(-MOVE_STEP, 0) : x)),

View File

@ -1,4 +1,4 @@
interface IPoint {
export interface IPoint {
x: number;
y: number;
}