Five documentation deliverables for the ElkSharp rendering improvements: 1. docs/workflow/engine/16-elksharp-rendering-architecture.md (453 lines) Full pipeline: Sugiyama stages, edge routing strategies, hybrid deterministic mode, gateway geometry, 18-category scoring system, corridor routing, Y-gutter expansion, diagnostics. 2. docs/workflow/engine/17-elksharp-architectural-decisions.md (259 lines) Six ADRs: short-stub normalization, gateway vertex entries, Y-gutter expansion, corridor rerouting, FinalScore adjustment, alongside detection. 3. docs/workflow/tutorials/10-rendering/README.md (234 lines) Practical tutorial: setup, layout options, SVG/PNG rendering, diagnostics capture, violation reports, full end-to-end example. 4. src/__Libraries/StellaOps.ElkSharp/AGENTS.md — 7 new local rules for Y-gutter, corridor reroute, gateway vertices, FinalScore adjustments, short-stub normalization, alongside detection, target-join spread. 5. docs/workflow/ENGINE.md — replaced monolithic ElkSharp paragraph with structured pipeline overview, effort-level table, and links to the new architecture docs. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
6.5 KiB
Tutorial 10: Rendering Workflow Diagrams
This tutorial shows how to use the Stella Ops workflow rendering system to produce visual diagrams from workflow definitions.
Prerequisites
- A workflow canonical definition (from the compiler or imported JSON).
- Reference to
StellaOps.Workflow.RendererandStellaOps.ElkSharpassemblies.
Basic Usage
1. Create the Layout Engine
var engine = new ElkSharpWorkflowRenderLayoutEngine();
2. Configure Layout Options
var request = new WorkflowRenderLayoutRequest
{
Direction = WorkflowRenderLayoutDirection.LeftToRight,
Effort = WorkflowRenderLayoutEffort.Best,
NodeSpacing = 40,
LayerSpacing = 60,
};
3. Compute the Layout
var layout = await engine.LayoutAsync(graph, request);
The graph parameter is a WorkflowRenderGraph produced by the
WorkflowRenderGraphCompiler from a canonical workflow definition.
4. Render to SVG
var svgRenderer = new WorkflowRenderSvgRenderer();
var svgDoc = svgRenderer.Render(layout, "My Workflow");
await File.WriteAllTextAsync("workflow.svg", svgDoc.Svg);
5. Export to PNG
var pngExporter = new WorkflowRenderPngExporter();
await pngExporter.ExportAsync(svgDoc, "workflow.png", scale: 2f);
The scale parameter controls the pixel density (2f = 2x resolution for HiDPI).
Layout Options Reference
Direction
| Value | Description |
|---|---|
LeftToRight |
Nodes flow left to right (default for workflows). |
TopToBottom |
Nodes flow top to bottom. Uses the legacy iterative path. |
Effort Levels
| Level | Speed | Quality | Use Case |
|---|---|---|---|
Draft |
Fast (~1s) | Basic | Interactive editing, previews |
Balanced |
Medium (~3-5s) | Good | Medium graphs, dev-time rendering |
Best |
Slow (~12-15s) | Production | Final artifacts, export, CI rendering |
Draft uses 8 ordering iterations, 3 placement iterations, and baseline routing only. No iterative optimization is performed.
Balanced uses 14 ordering iterations, 6 placement iterations, and light repair that fixes the worst violations without full A* search.
Best uses 24 ordering iterations, 10 placement iterations, and the hybrid deterministic optimization pipeline with full-core parallel repair candidates.
Spacing
- NodeSpacing (default 40): Vertical gap between nodes in pixels. The engine may scale this up to 1.8x when edge density is high.
- LayerSpacing (default 60): Horizontal gap between layers in pixels.
Reading the Layout Result
The LayoutAsync result contains positioned nodes and routed edges.
Nodes
foreach (var node in layout.Nodes)
{
Console.WriteLine($"Node {node.Id}: ({node.X}, {node.Y}) " +
$"size {node.Width}x{node.Height} " +
$"shape={node.Shape}");
}
Node shapes include Rectangle (service tasks), Diamond (decision gateways),
Hexagon (fork/join gateways), Circle (start/end events), and others.
Edges
foreach (var edge in layout.Edges)
{
Console.WriteLine($"Edge {edge.SourceId} -> {edge.TargetId}");
foreach (var point in edge.BendPoints)
{
Console.WriteLine($" bend: ({point.X}, {point.Y})");
}
}
Bend points define the orthogonal path from source to target. Two consecutive bend points with the same Y form a horizontal segment; two with the same X form a vertical segment.
Diagnostics
When using Best effort, the engine captures detailed diagnostics about the
optimization process.
Enabling Diagnostics
Diagnostics are captured automatically in Best mode. Access them through
the layout result.
Violation Report
The violation report lists each edge's violations with category, severity, and geometric details.
if (layout.Diagnostics?.ViolationReport != null)
{
foreach (var entry in layout.Diagnostics.ViolationReport)
{
Console.WriteLine($"Edge {entry.EdgeId}: " +
$"{entry.Category} (penalty {entry.Penalty})");
}
}
Violation Categories
The scoring system uses 18 categories. Hard violations (100K penalty) include node crossings, under-node routing, shared lanes, and boundary slot conflicts. Medium violations (50K) include backtracking and detours. Soft violations (200-650) include edge crossings, proximity, and excessive bends.
A FinalScore of 0 for hard violations indicates a clean layout with no visual defects. See the Rendering Architecture for the full violation taxonomy.
Phase Timings
if (layout.Diagnostics?.PhaseTimings != null)
{
foreach (var phase in layout.Diagnostics.PhaseTimings)
{
Console.WriteLine($"{phase.Name}: {phase.Duration.TotalMilliseconds}ms");
}
}
Phase timings cover ordering, placement, gutter expansion, base routing, iterative optimization, and post-processing.
End-to-End Example
// Compile a workflow definition to a render graph
var compiler = new WorkflowRenderGraphCompiler();
var graph = compiler.Compile(workflowDefinition);
// Configure and run layout
var engine = new ElkSharpWorkflowRenderLayoutEngine();
var request = new WorkflowRenderLayoutRequest
{
Direction = WorkflowRenderLayoutDirection.LeftToRight,
Effort = WorkflowRenderLayoutEffort.Best,
NodeSpacing = 40,
LayerSpacing = 60,
};
var layout = await engine.LayoutAsync(graph, request);
// Render to SVG
var svgRenderer = new WorkflowRenderSvgRenderer();
var svgDoc = svgRenderer.Render(layout, workflowDefinition.Name);
await File.WriteAllTextAsync($"{workflowDefinition.Name}.svg", svgDoc.Svg);
// Export to PNG at 2x resolution
var pngExporter = new WorkflowRenderPngExporter();
await pngExporter.ExportAsync(svgDoc, $"{workflowDefinition.Name}.png", scale: 2f);
// Check for violations
var hardViolations = layout.Diagnostics?.ViolationReport?
.Where(v => v.Penalty >= 100_000)
.ToList();
if (hardViolations?.Any() == true)
{
Console.WriteLine($"WARNING: {hardViolations.Count} hard violations detected");
}
Further Reading
- ElkSharp Rendering Architecture -- Full technical details of the Sugiyama pipeline, edge routing, and iterative optimization.
- Architectural Decisions -- ADR records for key design choices.
- ENGINE.md -- Workflow engine overview including layout engine configuration and render pipeline.