Skip to content

feat: add @joint/layout-msagl package #2898

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 26 commits into
base: master
Choose a base branch
from

Conversation

MartinKanera
Copy link
Contributor

@MartinKanera MartinKanera commented Mar 7, 2025

Description

Add a new layout package utilizing the Microsoft Automatic Graph Layout (MSAGL).

The layout function arranges either an entire JointJS graph or a specified subset of cells using MSAGL. It converts the cells into MSAGL geometry and applies the Sugiyama layout algorithm with Rectilinear or SplineBundling edge routing.

Key Features

  • Hierarchical Layout: Uses the Sugiyama algorithm for layered graph layout
  • Nested Subgraphs: Supports elements with embedded children as subgraphs
  • Edge Routing: Rectilinear or SplineBundling edge routing modes
  • Customizable Callbacks: Control how layout results are applied to elements

Caveats

  • Elements containing embedded cells are treated as subgraphs. As a result, subgraphs are resized to tightly pack their child elements, which may cause parent elements to have dimensions different from their original size.
  • Layout inside subgraphs is always set to TB (Top-to-Bottom) direction, as other directions can cause layout issues.
  • When edgeRoutingMode is set to Rectilinear, we currently apply a static offset to self‑edges. The resulting loop can look artificially large or misplaced. This limitation originates in the upstream msagljs library and will disappear once it is addressed there.

API

// Callback type definitions
type GetSizeCallback = (element: dia.Element) => dia.Size;
type GetLabelSizeCallback = (cell: dia.Link | dia.Element) => dia.Size | undefined;
type SetPositionCallback = (element: dia.Element, position: dia.Point) => void;
type SetVerticesCallback = (link: dia.Link, vertices: dia.Point[]) => void;
type SetLabelsCallback = (link: dia.Link, labelPosition: dia.Point, points: dia.Point[]) => void;
type SetAnchorCallback = (link: dia.Link, linkEndPoint: dia.Point, bbox: dia.BBox, endType: 'source' | 'target') => void;

interface Options {
    // Layout direction (default: LayerDirectionEnum.TB)
    layerDirection?: LayerDirectionEnum;
    
    // Spacing (defaults: layerSeparation: 40, nodeSeparation: 20)
    layerSeparation?: number;
    nodeSeparation?: number;
    
    // Edge routing (defaults: edgeRoutingMode: EdgeRoutingMode.Rectilinear, polylinePadding: 1)
    edgeRoutingMode?: EdgeRoutingMode;
    polylinePadding?: number;
    
    // Grid and margins (defaults: gridSize: 10, margins: {left: 10, right: 10, top: 10, bottom: 10})
    gridSize?: number;
    margins?: {
        left: number;
        right: number;
        top: number;
        bottom: number;
    };
    
    // Element sizing callbacks (defaults: element.size(), element.get('labelSize'))
    getSize?: GetSizeCallback;
    getLabelSize?: GetLabelSizeCallback;
    
    // Layout application callbacks (defaults: all true with built-in implementations)
    setPosition?: SetPositionCallback;
    setVertices?: boolean | SetVerticesCallback;
    setLabels?: boolean | SetLabelsCallback;
    setAnchor?: boolean | SetAnchorCallback;
}

// Enums
enum LayerDirectionEnum {
    TB, // Top to Bottom
    BT, // Bottom to Top  
    LR, // Left to Right
    RL  // Right to Left
}

enum EdgeRoutingMode {
    SplineBundling = 1, // Smooth curved edges
    Rectilinear = 4     // Orthogonal edges with right angles
}
function layout(graphOrCells: dia.Graph | dia.Cell[], options?: Options): g.Rect

@MartinKanera MartinKanera self-assigned this Mar 7, 2025
@MartinKanera MartinKanera requested a review from kumilingus March 22, 2025 00:49
@MartinKanera MartinKanera marked this pull request as ready for review August 7, 2025 14:17
@MartinKanera MartinKanera changed the title (WIP) feat: add @joint/layout-msagl package feat: add @joint/layout-msagl package Aug 7, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant