Ports

Ports are connection points on items where connectors attach. The Port Manager handles port creation, positioning, and visual display.

Overview

Ports are stored as lightweight data on items (item.data.ports) rather than as separate objects. Visual indicators are created on-demand only when needed (during connect mode).

Adding Ports

Standard Ports (4-way)

Most shapes use standard ports at top, bottom, left, and right:

// Add standard 4-port configuration
app.diagramSystem.addPorts(item, 'standard');

// Or explicitly
app.diagramSystem.portManager.addStandardPorts(item);

Line Ports (Start/End)

For paths and lines, use start/end ports:

app.diagramSystem.portManager.addLinePorts(item);
// Creates ports at: 'start' and 'end' positions

Path Ports

For closed freehand paths, distribute ports along the path:

app.diagramSystem.portManager.addPathPorts(item, 4);
// Creates 4 ports evenly distributed along the path

Custom Ports

Add ports at specific positions:

app.diagramSystem.portManager.addPorts(item, [
  { position: 'top', type: 'output' },
  { position: 'bottom', type: 'input' },
  { position: 'left', type: 'both' },
  { position: 'right', type: 'both' },
  { position: 'center', type: 'both' }
]);

Port Positions

Position Description
top Center of top edge
bottom Center of bottom edge
left Center of left edge
right Center of right edge
top-left Top-left corner
top-right Top-right corner
bottom-left Bottom-left corner
bottom-right Bottom-right corner
center Center of bounds
start First point (for paths)
end Last point (for paths)
custom Custom position with offset

Port Types

Control which direction connections can flow:

Type Description
input Only accepts incoming connections
output Only provides outgoing connections
both Accepts both directions (default)
app.diagramSystem.portManager.addPorts(item, [
  { position: 'top', type: 'input' },     // Only incoming
  { position: 'bottom', type: 'output' }  // Only outgoing
]);

Getting Ports

// Get all ports for an item
const ports = app.diagramSystem.getPorts(item);

// Get a specific port by position
const topPort = app.diagramSystem.portManager.getPort(item, 'top');

// Get port position (absolute coordinates)
const position = app.diagramSystem.portManager.getPortPosition(item, topPort);

Port Detection

Find ports near a point (for snapping):

// Find nearest port within snap distance
const nearest = app.diagramSystem.portManager.findNearestPort(point, {
  snapDistance: 25,           // Default: 25px
  excludeItem: sourceItem,    // Exclude an item
  excludePort: sourcePort,    // Exclude a specific port
  connectionType: 'output'    // Filter by type
});

if (nearest) {
  console.log('Found port:', nearest.portData);
  console.log('On item:', nearest.item);
  console.log('At position:', nearest.position);
  console.log('Distance:', nearest.distance);
}

// Find port exactly at a point (for click detection)
const port = app.diagramSystem.portManager.findPortAt(point, 12);

Port Visuals

Port visuals are temporary circles shown during connect mode:

// Show port visuals for all items (called when entering connect mode)
app.diagramSystem.portManager.showAllPortVisuals();

// Hide all port visuals
app.diagramSystem.portManager.hideAllPortVisuals();

// Update visual positions (after items move)
app.diagramSystem.portManager.updateVisualPositions();

Visual Configuration

// Default visual config
{
  radius: 8,                              // Port circle size
  strokeColor: '#3b82f6',                 // Blue outline
  strokeWidth: 2,
  dashArray: [3, 3],                      // Dotted outline
  fillColor: 'transparent',               // Default fill
  hoverFill: 'rgba(59, 130, 246, 0.2)',  // On hover
  highlightFill: 'rgba(251, 191, 36, 0.3)', // When snapped
  connectedFill: 'rgba(16, 185, 129, 0.2)', // When connected
  snapDistance: 25                        // Snap detection radius
}

Port Highlighting

Visual feedback during connection:

// Highlight a port (when snapping to it)
app.diagramSystem.portManager.highlightPort(portData, item, true);

// Remove highlight
app.diagramSystem.portManager.highlightPort(portData, item, false);

// Clear all highlights
app.diagramSystem.portManager.clearHighlights();

Removing Ports

// Remove all ports from an item
app.diagramSystem.portManager.removePorts(item);

Port Data Structure

Ports are stored on item.data.ports as an array:

item.data.ports = [
  {
    id: 'item_123_top_0',     // Unique ID
    position: 'top',           // Position type
    type: 'both',              // Connection type
    maxConnections: Infinity,  // Max connections allowed
    offset: { x: 0, y: 0 },    // Custom offset
    param: undefined           // For path ports: t value (0-1)
  },
  // ... more ports
];

item.data.hasPorts = true;  // Flag indicating ports exist

Automatic Port Assignment

When diagram mode is active, ports are automatically added:

  1. Diagram shapes - Standard 4-port on creation
  2. Open paths - Start/end ports
  3. Closed paths - Path-distributed ports
  4. Other items - Standard 4-port when entering connect mode
// Auto-assignment happens when:
// 1. Creating a shape via app.diagramSystem.createShape()
// 2. Entering connect mode with items that don't have ports

Direction Vectors

Get the outward direction for routing:

const direction = app.diagramSystem.portManager.getPortDirection('top');
// Returns: paper.Point(0, -1)  // Pointing up

// Direction vectors:
// top:          (0, -1)
// bottom:       (0, 1)
// left:         (-1, 0)
// right:        (1, 0)
// top-left:     (-0.707, -0.707)
// top-right:    (0.707, -0.707)
// bottom-left:  (-0.707, 0.707)
// bottom-right: (0.707, 0.707)

Integration with Connectors

When creating connectors, ports are automatically used:

// Automatic port selection
app.diagramSystem.connect(itemA, itemB);
// Finds best ports based on relative positions

// Manual port selection
const portA = app.diagramSystem.portManager.getPort(itemA, 'bottom');
const portB = app.diagramSystem.portManager.getPort(itemB, 'top');
app.diagramSystem.connectPorts(portA, portB);