Skip to content

Render class nodes with compartments and UML relationship styles#100

Merged
jongalloway merged 3 commits intomainfrom
copilot/render-class-nodes-uml-styles
Mar 14, 2026
Merged

Render class nodes with compartments and UML relationship styles#100
jongalloway merged 3 commits intomainfrom
copilot/render-class-nodes-uml-styles

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Mar 14, 2026

Teaches the SVG renderer to draw UML-style class boxes and the core relationship line/arrow variants required by Mermaid class diagrams. All changes are renderer + layout only — no parser dependency.

Model

  • Edge.SourceArrowHead (ArrowHeadStyle, default None) — source-end marker for composition/aggregation diamonds, symmetric with the existing ArrowHead (target end).

Layout

  • SizeClassNode — computes node dimensions from annotation lines, class name, and per-compartment line counts. Called instead of SizeStandardNode when Node.Compartments.Count > 0 || Node.Annotations.Count > 0.
  • AnnotationFontSizeRatio = 0.85 extracted as a named constant (shared between layout and renderer).

Rendering

New SVG markers in SvgRenderSupport.AppendDefs:

Marker ID Shape Used for
arrowhead-open hollow triangle inheritance, realization
arrowhead-filled-diamond filled diamond composition (source end)
arrowhead-open-diamond hollow diamond aggregation (source end)

SvgStructureWriter.AppendEdge now emits the correct marker-end / marker-start based on ArrowHead and SourceArrowHead values.

SvgNodeWriter.AppendClassNode — new method rendering UML class boxes:

  • rx=0 outer rect (sharp UML corners)
  • Italic «stereotype» annotations above the class name
  • Bold centered class name
  • Horizontal <line> dividers above each compartment
  • Left-aligned compartment content (attributes, methods, etc.)
var node = new Node("Animal", "Animal");
node.Annotations.Add(new Label("abstract"));
node.Compartments.Add(new NodeCompartment("attributes", [new Label("-name: String")]));
node.Compartments.Add(new NodeCompartment("methods", [new Label("+makeSound(): void")]));

var inheritance = new Edge("Dog", "Animal") { ArrowHead = ArrowHeadStyle.OpenArrow };
var composition = new Edge("Dog", "Tail") { SourceArrowHead = ArrowHeadStyle.Diamond, ArrowHead = ArrowHeadStyle.None };
var aggregation = new Edge("Pack", "Dog")  { SourceArrowHead = ArrowHeadStyle.Circle,  ArrowHead = ArrowHeadStyle.None };

Tests

  • 26 new rendering tests in SvgClassDiagramRendererTests — compartment structure, stereotype rendering, sizing, and all 8 UML relationship types (inheritance, realization, composition, aggregation, association, link, dependency, dashed link).
  • Edge_SourceArrowHead_DefaultsToNone added to model tests.
  • All 61 E2E snapshot fixtures updated — only the <defs> section changes (three new marker definitions); no existing node/edge output affected.

Co-authored-by: jongalloway <68539+jongalloway@users.noreply.github.com>
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds UML class diagram rendering capabilities to DiagramForge's SVG renderer, including compartmented class boxes and UML relationship arrow/marker variants. All changes are renderer and layout only — no parser dependency.

Changes:

  • Adds Edge.SourceArrowHead property and three new SVG marker definitions (arrowhead-open, arrowhead-filled-diamond, arrowhead-open-diamond) for UML relationships.
  • Implements SizeClassNode layout sizing and AppendClassNode rendering for UML-style class boxes with annotations, bold class names, divider lines, and left-aligned compartments.
  • Adds 26 new unit tests for class node rendering and relationship styles, plus a model test, and updates all 61 E2E snapshot fixtures with the new <defs> markers.

Reviewed changes

Copilot reviewed 7 out of 68 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/DiagramForge/Models/Edge.cs Adds SourceArrowHead property for source-end markers
src/DiagramForge/Layout/DefaultLayoutEngine.cs Adds SizeClassNode method and AnnotationFontSizeRatio constant; routes class nodes to new sizing logic
src/DiagramForge/Rendering/SvgRenderSupport.cs Adds three new SVG marker definitions in AppendDefs
src/DiagramForge/Rendering/SvgStructureWriter.cs Expands AppendEdge to emit marker-start/marker-end based on SourceArrowHead/ArrowHead values
src/DiagramForge/Rendering/SvgNodeWriter.cs Adds AppendClassNode method and AnnotationFontSizeRatio constant; branches to class rendering when compartments/annotations are present; suppresses standard label for class nodes
tests/.../SvgClassDiagramRendererTests.cs New test file with 26 tests for class nodes and UML relationship rendering
tests/.../DiagramModelTests.cs Adds Edge_SourceArrowHead_DefaultsToNone test
tests/.../Fixtures/*.expected.svg (61 files) Updates <defs> with new markers; minor whitespace change in <path> elements

You can also share your feedback on Copilot code review. Take the survey.

// Width: widest of class name, annotations, or any compartment line
double maxTextWidth = EstimateTextWidth(node.Label.Text, fontSize);
foreach (var ann in node.Annotations)
maxTextWidth = Math.Max(maxTextWidth, EstimateTextWidth(ann.Text, annFontSize));
Comment on lines +120 to +125
string markerStart = edge.SourceArrowHead switch
{
ArrowHeadStyle.Diamond => """ marker-start="url(#arrowhead-filled-diamond)" """,
ArrowHeadStyle.Circle => """ marker-start="url(#arrowhead-open-diamond)" """,
_ => " ",
};
@jongalloway
Copy link
Copy Markdown
Owner

@copilot apply changes based on the comments in this thread

Co-authored-by: jongalloway <68539+jongalloway@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Mar 14, 2026

@copilot apply changes based on the comments in this thread

Both fixes applied in a6e6215:

  1. Annotation widthSizeClassNode now estimates width as «{ann.Text}» (matching the guillemet wrapping in AppendClassNode) so the node is never narrower than the rendered stereotype text.

  2. markerStart completeness — added Arrow and OpenArrow cases to mirror the markerEnd switch, so all four ArrowHeadStyle values are handled symmetrically at both ends.

@jongalloway jongalloway merged commit 540d65a into main Mar 14, 2026
5 checks passed
@jongalloway jongalloway deleted the copilot/render-class-nodes-uml-styles branch March 14, 2026 22:19
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.

Render class nodes with compartments and UML relationship styles

3 participants