Connection System
ConnectionLine (1145 lines), ConnectionPoint, routing, animation, and ERD multiplicity
Namespace: Beep.Skia.Connections — Key Files: ConnectionLine.cs (1145 lines), ConnectionManager.cs, ConnectionPoint.cs
Table of Contents
ConnectionLine
ConnectionLine is a 1145-line class that renders a visual link between two IConnectionPoint instances. It manages the full lifecycle of a connection: path computation, visual styling, hit testing, label placement, and animation. Connection lines are the primary mechanism for expressing relationships in Beep.Skia diagrams.
| Property | Type | Description |
|---|---|---|
Start | IConnectionPoint | The source port on the origin component. |
End | IConnectionPoint | The target port on the destination component. Null while a line is being dragged; EndPoint holds the preview coordinates. |
LineColor | SKColor | Line color. Defaults to SKColors.Black. |
Paint | SKPaint | Stroke styling; set Paint.StrokeWidth for line thickness and Paint.PathEffect for dashes. |
RoutingMode | LineRoutingMode | Straight, Orthogonal, or Curved. See Routing Modes. |
ShowStartArrow / ShowEndArrow | bool | Arrowheads at each end. ArrowSize controls their size. |
DashPattern | float[] | Optional dash pattern applied to the stroke. |
FlowDirection | DataFlowDirection | None, Forward, Backward, or Bidirectional. Drives data-flow animation with IsDataFlowAnimated and DataFlowColor. |
Label1, Label2, Label3 | string | Up to three labels positioned along the line; each has its own LabelPlacement. |
StartMultiplicity / EndMultiplicity | ERDMultiplicity | ERD crow's-foot markers at each end. |
IsSelected | bool | Selection state. Selected lines render with a highlight color. |
Animation Styles
Connection lines support configurable animations that provide visual feedback about flow direction and connection state. Animations are rendered on a separate frame timer and composited onto the line:
| Style | Enum Value | Visual Effect |
|---|---|---|
| None | AnimationStyle.None | Static line. No animation overhead. |
| Dots | AnimationStyle.Dots | Small circles travel along the line from start to end, resembling data packets. |
| Dashes | AnimationStyle.Dashes | Dashed segments scroll along the line, creating a marquee-style flow indicator. Phase offset animates continuously. |
| Wave | AnimationStyle.Wave | A sinusoidal displacement is applied perpendicular to the line path, creating a snake-like wave motion. |
| Gradient | AnimationStyle.Gradient | A linear gradient shifts along the line, cycling through two colors to indicate active flow. |
| Particles | AnimationStyle.Particles | Multiple small particles emanate from the start point and travel toward the end point at varying speeds. |
| Pulse | AnimationStyle.Pulse | The entire line pulses between two opacity levels with a smooth ease-in-out curve. Good for "active connection" state. |
| Arrows | AnimationStyle.Arrows | Small arrow shapes travel along the line, explicitly showing the flow direction. Most commonly used style. |
AnimatedFlowLines
The AnimatedFlowLines feature is a higher-level abstraction that batches multiple animated connections and manages their shared animation timer. When enabled:
- A single
System.Timers.Timerdrives all animated connections at a configurable frame rate (default: 30 FPS). - Each connection tracks its own phase offset for independent animation timing.
- The timer is automatically paused when the diagram is not visible (tab switch, minimize) to conserve CPU.
- Animation speed is controlled per-connection via
AnimationSpeed(range: 0.25x to 4.0x).
Enabling Connection Animation
var connection = drawingManager.ConnectComponents(sourcePort, targetPort);
connection.AnimationStyle = AnimationStyle.Arrows;
connection.AnimationSpeed = 2.0f;
connection.AnimatedFlowLines = true;
drawingManager.EnableFlowAnimation();
Routing Modes
The connection system supports three routing modes that determine how the line path is computed between the start and end ports:
| Mode | Enum Value | Path | Use Case |
|---|---|---|---|
| Straight | RoutingMode.Straight |
Direct line segment from start to end. No intermediate points. | Simple diagrams, quick prototyping, mind maps. |
| Orthogonal | RoutingMode.Orthogonal |
Axis-aligned path with 90-degree bends. Automatically computed bend points. Honors obstacle avoidance. | Flowcharts, network diagrams, ERD — most professional diagram types. |
| Curved | RoutingMode.Curved |
Smooth cubic Bezier curve between endpoints. Control points automatically positioned for natural curvature. | Mind maps, concept diagrams, UML associations, organic layouts. |
Orthogonal Routing Details
Orthogonal routing is the most sophisticated mode. The algorithm:
- Determines the exit direction from the start port (North, South, East, West) based on port placement.
- Determines the entry direction into the end port.
- Computes intermediate bend points that produce a clean, axis-aligned path with minimal crossings.
- Applies a configurable
CornerRadiusto round the 90-degree bends for a polished appearance. - Automatically adjusts when components are moved, calling
RecalculateRoute()on the next render cycle.
Configuring Connection Routing
connection.RoutingMode = RoutingMode.Orthogonal;
connection.CornerRadius = 6;
connection.RecomputeOnMove = true;
connection.RoutingMode = RoutingMode.Curved;
connection.CurveTension = 0.5f;
connection.RoutingMode = RoutingMode.Straight;
ERD Multiplicity
The connection system includes specialized support for Entity-Relationship Diagram (ERD) semantics, including multiplicity markers and row-level connections:
Endpoint Markers
Multiplicity is expressed as visual markers at each end of the connection line. The marker set follows standard ERD notation:
| Multiplicity | Start Marker | End Marker | Meaning |
|---|---|---|---|
| One-to-One (1:1) | Single vertical bar | Single vertical bar | Each instance of entity A relates to exactly one instance of entity B. |
| One-to-Many (1:N) | Single vertical bar | Crow's foot (three lines) | Each instance of entity A relates to zero or more instances of entity B. |
| Many-to-Many (M:N) | Crow's foot (three lines) | Crow's foot (three lines) | Instances of A and B have a many-to-many relationship. Typically resolved via a junction table. |
| Zero-or-One (0..1) | Circle | Single vertical bar | Optional participation: an instance of A may or may not relate to an instance of B. |
| Zero-or-Many (0..N) | Circle | Crow's foot (three lines) | Optional participation with multiple possible related instances. |
| Exactly-One (1) | Double vertical bar | — | Mandatory participation: every instance of A must relate to exactly one instance of B. |
Row-Level Connection Semantics
For ERD diagrams, connections can be established at the column level rather than the entity level. When a ConnectionPoint has a RowId set, the connection targets a specific field (row) within an entity. This enables:
- Foreign key relationships that link a specific column in one entity to a primary key column in another.
- Visual connection lines that attach to individual row entries within an ERD entity box.
- Schema generation that correctly emits
FOREIGN KEY (column) REFERENCES table(column)constraints.
Row-Level Connection Setup
// Create the relationship, then apply ERD multiplicity markers
drawingManager.ConnectComponents(ordersEntity, customersEntity);
var fkConnection = drawingManager.GetLines().Last();
fkConnection.StartMultiplicity = ERDMultiplicity.ZeroOrMany;
fkConnection.EndMultiplicity = ERDMultiplicity.OneOnly;
Connection Validation
Before a connection is established, the ConnectionManager runs a validation pipeline to ensure diagram integrity:
| Validation Rule | Description | Violation Behavior |
|---|---|---|
| Type Compatibility | The DataType of the start and end ports must be compatible. Exact match or assignable via inheritance. Null-type ports accept any type. | Connection rejected. Status indicator shows red "type mismatch" icon on hover. |
| Port Availability | If a port has MaxConnections = 1 and is already connected, additional connections are rejected. | Connection rejected. Port renders with a distinct "occupied" visual state. |
| No Self-Loops | A component cannot connect to itself unless AllowSelfLoops is explicitly set on the port. | Connection rejected. Visual feedback snaps the drag line back to the start port. |
| Duplicate Prevention | Two components cannot have more than one connection between the same pair of ports. | Connection rejected. Existing line flashes to indicate the duplicate attempt. |
| Direction Validation | An OutPoint can only connect to an InPoint (or vice versa). Out-to-out and in-to-in connections are rejected. | Connection rejected. Port changes cursor to a "not allowed" icon during drag. |
Custom Connection Validation
public static bool CanConnect(IConnectionPoint source, IConnectionPoint target)
{
if (source == null || target == null)
return false;
if (source.Type == target.Type)
return false; // cannot connect out-to-out or in-to-in
if (ReferenceEquals(source.Component, target.Component))
return false; // cannot self-connect
if (target.Connection != null)
return false; // target input already has a connection
// Optional: compare the declared data types
if (!string.IsNullOrEmpty(target.DataType) &&
!string.Equals(target.DataType, source.DataType, StringComparison.Ordinal))
return false;
return true;
}
Labels
Connection lines support multiple text labels positioned at configurable locations along the line path. Labels are stored in the Labels collection and rendered during the line's draw cycle:
| Label Property | Type | Description |
|---|---|---|
Text | string | Display text. Supports newline characters for multi-line labels. |
Position | LabelPosition | Start, Middle, or End — the position along the line as a proportion (0.0 to 1.0). |
Offset | SKPoint | Pixel offset from the computed line position. Positive Y offsets push the label above the line. |
FontSize | float | Font size in logical units. Default: 12f. |
FontColor | SKColor | Text color. Default: SKColors.Black or theme-dependent. |
BackgroundColor | SKColor | Optional background fill behind the text to improve readability over the line. |
Adding Labels to a Connection
connection.Labels.Add(new ConnectionLabel
{
Text = "has many",
Position = LabelPosition.Middle,
Offset = new SKPoint(0, -12),
FontColor = SKColors.DarkSlateGray
});
connection.Labels.Add(new ConnectionLabel
{
Text = "1",
Position = LabelPosition.Start,
Offset = new SKPoint(8, -8),
FontSize = 10f
});
connection.Labels.Add(new ConnectionLabel
{
Text = "N",
Position = LabelPosition.End,
Offset = new SKPoint(-8, -8),
FontSize = 10f
});