private void DrawSelfSignal(IDrawingCanvas drawingCanvas, SignalVisualElement signal) { // Actor line position double aX = GetCenterX(signal.ActorA) + signal.ActorA.OffsetX; // Left and right edges of self-signal line double x1 = aX; double x2 = x1 + s_selfSignalWidth; // Top and bottom of self-signal line double y1 = signal.OffsetY + s_signalMargin.Top; double y2 = signal.OffsetY + signal.TextBB.Height - s_signalMargin.Bottom; // Position of signal text double x = x2 + s_signalPadding.Left; double y = y1 + s_signalPadding.Top; // Draw the text so that its left edge is along the vertical segment of the self-signal signal.Text.TextAlignment = TextAlignment.Left; signal.Text.SetForegroundBrush(SignalForeground); drawingCanvas.DrawText(x, y, signal.Text); // Draw three lines, the last one with an arrow drawingCanvas.DrawLine(x1, y1, x2, y1, signal.LineType, SequenceArrowType.None); drawingCanvas.DrawLine(x2, y1, x2, y2, signal.LineType, SequenceArrowType.None); drawingCanvas.DrawLine(x2, y2, x1, y2, signal.LineType, signal.ArrowType); }
private void DrawSignal(IDrawingCanvas drawingCanvas, SignalVisualElement signal) { double aX = GetCenterX(signal.ActorA) + signal.ActorA.OffsetX; double bX = GetCenterX(signal.ActorB) + signal.ActorB.OffsetX; // Mid-point between actors double x = (bX + aX) / 2; double y = signal.OffsetY + s_signalMargin.Top + s_signalPadding.Top; // Draw the text in the middle of the signal signal.Text.TextAlignment = TextAlignment.Center; signal.Text.SetForegroundBrush(SignalForeground); drawingCanvas.DrawText(x, y, signal.Text); // Draw the line along the bottom of the signal y = signal.OffsetY + signal.TextBB.Height - s_signalMargin.Bottom; drawingCanvas.DrawLine(aX, y, bX, y, signal.LineType, signal.ArrowType); }
private Size?MeasureAndArrangeVisualElements() { // Early out if there sequence diagram view model available var vm = ViewModel; if (vm == null) { return(null); } // Reset visual elements var actors = vm.Actors; var annotations = vm.Annotations; m_actorVisualElements = new List <ActorVisualElement>(actors.Count); m_signalVisualElements = new List <SignalVisualElement>(); m_noteVisualElements = new List <NoteVisualElement>(); m_actorsWidth = 0; m_actorsHeight = 0; m_annotationsBottom = 0; double offsetY = Margin.Top; // Capture typeface which is used to measure various textboxes m_typeface = new Typeface(FontFamily, FontStyle, FontWeight, FontStretch); // Layout actors foreach (var actor in actors) { var text = GetFormattedText(actor.Name, ActorForeground); var textBB = GetTextBB(text, s_actorMargin, s_actorPadding); var actorVisualElement = new ActorVisualElement { Text = text, TextBB = textBB, Distances = new double?[actors.Count] }; m_actorVisualElements.Add(actorVisualElement); m_actorsWidth = Math.Max(textBB.Width, m_actorsWidth); m_actorsHeight = Math.Max(textBB.Height, m_actorsHeight); } // Position at bottom edge of top actor text boxes offsetY += m_actorsHeight; // Layout all signal types foreach (var annotation in annotations) { double extraWidth = 0; bool skipFinalSignalLayout = false; // Indexes of the left and right actors involved int a = 0, b = 0; // Visual element AnnotationVisualElement annotationVisualElement = null; // Handle signals var signal = annotation as SignalViewModel; if (signal != null) { var text = GetFormattedText(annotation.Message, SignalForeground); var textBB = GetTextBB(text, s_signalMargin, s_signalPadding); var signalVisualElement = new SignalVisualElement { Text = text, TextBB = textBB, OffsetY = offsetY, IsSelfSignal = signal.IsSelf(), ActorA = m_actorVisualElements[signal.ActorA.Index], ActorB = m_actorVisualElements[signal.ActorB.Index], LineType = signal.LineType, ArrowType = signal.ArrowType, }; annotationVisualElement = signalVisualElement; m_signalVisualElements.Add(signalVisualElement); if (signal.IsSelf()) { // Self-signals need a minimum height a = signal.ActorA.Index; b = a + 1; // Self-signals take up extra horizontal space extraWidth += s_selfSignalWidth; } else { a = Math.Min(signal.ActorA.Index, signal.ActorB.Index); b = Math.Max(signal.ActorA.Index, signal.ActorB.Index); } } // Handle notes var note = annotation as NoteViewModel; if (note != null) { var text = GetFormattedText(annotation.Message, NoteForeground); var textBB = GetTextBB(text, s_noteMargin, s_notePadding); var noteVisualElement = new NoteVisualElement { Text = text, TextBB = textBB, OffsetY = offsetY, Placement = note.Placement, Message = note.Message, Actors = note.Actors.Select(actor => m_actorVisualElements[actor.Index]).ToArray(), }; annotationVisualElement = noteVisualElement; m_noteVisualElements.Add(noteVisualElement); if (note.Placement == SequenceNotePlacement.LeftOf) { b = note.Actors[0].Index; a = b - 1; } else if (note.Placement == SequenceNotePlacement.RightOf) { a = note.Actors[0].Index; b = a + 1; } else if (note.Placement == SequenceNotePlacement.Over) { if (note.Actors.Length > 1) { // Over multiple actors a = Math.Min(note.Actors[0].Index, note.Actors[1].Index); b = Math.Max(note.Actors[0].Index, note.Actors[1].Index); // We don't need our padding, and we want to overlap extraWidth = -(s_noteMargin.Left + s_noteMargin.Right + s_noteOverlap.Left + s_noteOverlap.Right); } else { // Over single actor a = note.Actors[0].Index; ActorEnsureDistance(a - 1, a, noteVisualElement.TextBB.Width / 2); ActorEnsureDistance(a, a + 1, noteVisualElement.TextBB.Width / 2); // Skip calculation of the signal height in this case skipFinalSignalLayout = true; } } } // Ensure proper spacing between actors if (!skipFinalSignalLayout) { ActorEnsureDistance(a, b, annotationVisualElement.TextBB.Width + extraWidth); } // Adjust offset by by annotiation height offsetY += annotationVisualElement.TextBB.Height; } m_annotationsBottom = offsetY; // Compute the overall size of the diagram double actorsX = 0; foreach (var actor in m_actorVisualElements) { actor.OffsetX = Math.Max(actorsX, actor.OffsetX); // This requires that we enumerate distances in order for (int i = 0; i < actor.Distances.Length; ++i) { double?distance = actor.Distances[i]; // Only process well-defined distances if (distance.HasValue) { ActorVisualElement b = m_actorVisualElements[i]; double d = Math.Max(distance.Value, Math.Max(m_actorsWidth / 2, b.TextBB.Width / 2)); b.OffsetX = Math.Max(b.OffsetX, actor.OffsetX + m_actorsWidth / 2 + d - b.TextBB.Width / 2); } } actorsX = actor.OffsetX + m_actorsWidth + actor.PaddingRight; } // Determine total size Size size = new Size(); size.Width = actorsX + Margin.Left + Margin.Right; size.Height = Margin.Top + Margin.Bottom + m_annotationsBottom + m_actorsHeight; return(size); }