Namespace: Beep.Skia.Connections — Key Files: ConnectionLine.cs (1145 lines), ConnectionManager.cs, ConnectionPoint.cs

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.

PropertyTypeDescription
StartIConnectionPointThe source port on the origin component.
EndIConnectionPointThe target port on the destination component. Null while a line is being dragged; EndPoint holds the preview coordinates.
LineColorSKColorLine color. Defaults to SKColors.Black.
PaintSKPaintStroke styling; set Paint.StrokeWidth for line thickness and Paint.PathEffect for dashes.
RoutingModeLineRoutingModeStraight, Orthogonal, or Curved. See Routing Modes.
ShowStartArrow / ShowEndArrowboolArrowheads at each end. ArrowSize controls their size.
DashPatternfloat[]Optional dash pattern applied to the stroke.
FlowDirectionDataFlowDirectionNone, Forward, Backward, or Bidirectional. Drives data-flow animation with IsDataFlowAnimated and DataFlowColor.
Label1, Label2, Label3stringUp to three labels positioned along the line; each has its own LabelPlacement.
StartMultiplicity / EndMultiplicityERDMultiplicityERD crow's-foot markers at each end.
IsSelectedboolSelection 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:

StyleEnum ValueVisual Effect
NoneAnimationStyle.NoneStatic line. No animation overhead.
DotsAnimationStyle.DotsSmall circles travel along the line from start to end, resembling data packets.
DashesAnimationStyle.DashesDashed segments scroll along the line, creating a marquee-style flow indicator. Phase offset animates continuously.
WaveAnimationStyle.WaveA sinusoidal displacement is applied perpendicular to the line path, creating a snake-like wave motion.
GradientAnimationStyle.GradientA linear gradient shifts along the line, cycling through two colors to indicate active flow.
ParticlesAnimationStyle.ParticlesMultiple small particles emanate from the start point and travel toward the end point at varying speeds.
PulseAnimationStyle.PulseThe entire line pulses between two opacity levels with a smooth ease-in-out curve. Good for "active connection" state.
ArrowsAnimationStyle.ArrowsSmall 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.Timer drives 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:

ModeEnum ValuePathUse 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:

  1. Determines the exit direction from the start port (North, South, East, West) based on port placement.
  2. Determines the entry direction into the end port.
  3. Computes intermediate bend points that produce a clean, axis-aligned path with minimal crossings.
  4. Applies a configurable CornerRadius to round the 90-degree bends for a polished appearance.
  5. 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:

MultiplicityStart MarkerEnd MarkerMeaning
One-to-One (1:1)Single vertical barSingle vertical barEach instance of entity A relates to exactly one instance of entity B.
One-to-Many (1:N)Single vertical barCrow'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)CircleSingle vertical barOptional participation: an instance of A may or may not relate to an instance of B.
Zero-or-Many (0..N)CircleCrow'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 RuleDescriptionViolation Behavior
Type CompatibilityThe 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 AvailabilityIf 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-LoopsA 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 PreventionTwo components cannot have more than one connection between the same pair of ports.Connection rejected. Existing line flashes to indicate the duplicate attempt.
Direction ValidationAn 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 PropertyTypeDescription
TextstringDisplay text. Supports newline characters for multi-line labels.
PositionLabelPositionStart, Middle, or End — the position along the line as a proportion (0.0 to 1.0).
OffsetSKPointPixel offset from the computed line position. Positive Y offsets push the label above the line.
FontSizefloatFont size in logical units. Default: 12f.
FontColorSKColorText color. Default: SKColors.Black or theme-dependent.
BackgroundColorSKColorOptional 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
});