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

View File

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