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);
        }