Crainiate Community

Support and discussion for users of Crainiate component software products
Welcome to Crainiate Community Sign in | Join | Help
in Search

Adding a curved recursive loop to a shape

Last post 07-30-2007 9:26 PM by James Westgate. 8 replies.
Page 1 of 1 (9 items)
Sort Posts: Previous Next
  • 09-01-2006 1:55 PM

    Adding a curved recursive loop to a shape

    A recursive line is a line whose Start and End is joined to the same shape. To display this kind of line correctly one should add a port to each side of the shape required so that the line is properly visible.

    The Model class has an existing method AddRecursive method which will add a connector to the ports, creating a recursive line made out of straight line segments. To add a curved loop instead of a connector, we need to create a subclass of the Curve class which is defined as follows

    Option Explicit On
    Imports System.Drawing
    Imports System.Drawing.Drawing2D
    Imports System.Runtime.Serialization

    Imports Crainiate.ERM4

    Public Class RecursiveLoop
        Inherits Curve

        Public Sub New(ByVal startPort As Port, ByVal endPort As Port)
            MyBase.new(startPort, endPort)

            CurveType = CurveType.Bezier
            AllowMove = False
            MyBase.Start.AllowMove = False
            MyBase.End.AllowMove = False
        End Sub

        Public Sub New(ByVal prototype As RecursiveLoop)
            MyBase.New(prototype)
        End Sub

        Public Overrides Sub DrawPath()

            If Container Is Nothing Then Return
            If MyBase.Start Is Nothing Then Return
            If MyBase.End Is Nothing Then Return

            Dim startLocation As PointF = GetOriginLocation(MyBase.Start, MyBase.End)
            Dim endLocation As PointF = GetOriginLocation(MyBase.End, MyBase.Start)

            Dim controlPoint1 As New PointF(startLocation.X + 60, startLocation.Y - 40)
            Dim controlPoint2 As New PointF(endLocation.X + 5, endLocation.Y - 80)

            SetControlPoints(New PointF() {controlPoint1, controlPoint2})

            MyBase.DrawPath()

        End Sub

        Public Overrides Function Handle(ByVal location As System.Drawing.PointF) As Handle
            Return New Handle(HandleType.Arrow)
        End Function

        Protected Overrides Sub RenderAction(ByVal graphics As System.Drawing.Graphics, ByVal render As IRender, ByVal renderDesign As IRenderDesign)
            DrawPath()
            MyBase.RenderAction(graphics, render, renderDesign)
        End Sub

        Public Overrides Function Clone() As Object
            Return New RecursiveLoop(Me)
        End Function
    End Class


    To use the RecursiveLoop class, add the following code to a project using the ERM4 Diagram control, version 4.2435 or later

        Private Sub frmDiagram_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load

            Dim shape As New shape

            Dim top As New Port(PortOrientation.Top)
            Dim right As New Port(PortOrientation.Right)

            top.Visible = False
            right.Visible = False
            top.Style = PortStyle.Simple
            right.Style = PortStyle.Simple

            shape.Ports.Add("top", top)
            shape.Ports.Add("right", right)

            shape.Location = New PointF(100, 100)

            model1.Shapes.Add("Shape1", shape)

            Dim curve As New RecursiveLoop(right, top)
            curve.End.Marker = New Arrow

            model1.Lines.Add("Line1", curve)

        End Sub



    • 81.132.167.247
  • 07-18-2007 11:08 AM In reply to

    • Mathy
    • Top 10 Contributor
    • Joined on 07-16-2007
    • Posts 19

    Re: Adding a curved recursive loop to a shape

     I used this as a base for my own curve class that automatically calculates it's control points. The curves display fine, but the arrow I use as a marker don't. They don't follow the flow of the curve, but instead they just point in the direction of the old curve. I tried things like invalidating the marker or replacing it by a new but that all results in stack overflows because it seems to recurse.

    Any pointers? 

    • 194.7.161.130
  • 07-18-2007 12:03 PM In reply to

    Re: Adding a curved recursive loop to a shape

    The problem here is that the marker rotation can only be calculated by drawing a straight line to the first control point and measuring the angle, for tight loops, especially with the Bezier algorithm, this can mean the angle difference is large enough to be noticiable.

     If you have a technique for calculating the correct orientation of the marker (such as the algorithm you are using to calcualte the control points), we can certianly help you with code that will position the marker correctly.

    • 194.29.65.17
  • 07-18-2007 12:30 PM In reply to

    • Mathy
    • Top 10 Contributor
    • Joined on 07-16-2007
    • Posts 19

    Re: Adding a curved recursive loop to a shape

    What I want to achieve is just nice, slightly curved lines between components. This is the code:

            const int width = 20;

            private void ResetControlPoints()
            {
                if (Start != null && End != null)
                {
                    PointF start = GetOriginLocation(Start, End);
                    PointF end = GetOriginLocation(End, Start);
                    PointF[] controlPoints = new PointF[2];

                    float a = end.X - start.X;
                    float b = end.Y - start.Y;

                    float distance = (float) System.Math.Sqrt(a * a + b * b);

                    float dx = -b * width / distance;
                    float dy = a * width / distance;

                    controlPoints[0] = new PointF(start.X + dx, start.Y + dy);
                    controlPoints[1] = new PointF(end.X + dx, end.Y + dy);
                   
                    SetControlPoints(controlPoints);
                }
            }

    Since you require bezier-curves to have at least two control points (don't know why, one is valid too and would be much easier for me) I need to do it this way with two points. I tried it with a spline with one control point in the middle and then the arrow indeed has the right rotation, but I don't like the shape of the spline as much as that of the bezier.

     

    • 194.7.161.130
  • 07-18-2007 12:50 PM In reply to

    • Mathy
    • Top 10 Contributor
    • Joined on 07-16-2007
    • Posts 19

    Re: Adding a curved recursive loop to a shape

    I think the easiest solution would be if I could tell the arrow to orientate itself to the other control point. Is there a way to achieve this? 

    • 194.7.161.130
  • 07-26-2007 9:26 AM In reply to

    • Mathy
    • Top 10 Contributor
    • Joined on 07-16-2007
    • Posts 19

    Re: Adding a curved recursive loop to a shape

    Hello? :) 

    • 194.7.161.130
  • 07-27-2007 9:57 AM In reply to

    Re: Adding a curved recursive loop to a shape

    The method to override would be the Line.GetMarkerTransform method. Unfortuneately this is currently not marked as virtual.

    Instead, you will have to override the entire render procedure and change the code accordingly.

    render.cs

    The attachment contains the default code to orientate the markers from the first control point. You can override this method and change the orientation point to any desired location.

    • 86.155.94.155
  • 07-30-2007 8:55 AM In reply to

    • Mathy
    • Top 10 Contributor
    • Joined on 07-16-2007
    • Posts 19

    Re: Adding a curved recursive loop to a shape

    I still have some questions:

    • I replaced GetPathInternal() with GetPath(), is there a difference?
    • I can't render the ports because SuspendValidation(), Render(...) and ResumeValidation() are protected. I'm not using ports atm, but I might consider it. Is there a way to do this?
    • I had to remove the marker from End.Marker and create it in the overridden Render because I need base.Render to draw the curve but it also draws the arrow in the normal position. Is there a way to just render the curve? Also, the arrow is not shadowed this way.
    • 194.7.161.130
  • 07-30-2007 9:26 PM In reply to

    Re: Adding a curved recursive loop to a shape

    • GetPathInternal is slightly quicker as it does not make a clone of the path, but returns a reference instead. You will be fine with GetPath().
    • You will need to subclass the Port class and override these methods so that they are internal to your assembly, or use reflection to call the methods - these method interfaces will not change as they are a contract to all sub classes so this approach is safe.
    • You will need to bypass base.Render as this will perform the code you are currently implementing. In essence, the code then only performs the following 3 lines of code in the base implementation:

    pen.Color = render.AdjustColor(BorderColor,BorderWidth,Opacity);
    graphics.SmoothingMode = SmoothingMode;
    graphics.DrawPath(pen, path);

    You will now have to override RenderShadow (do not call the base implementation) and perform the same rendering using a pen deried from the element.Layer.ShadowColor

    Let me know if you need futher help.

    Filed under:
    • 86.155.94.155
Page 1 of 1 (9 items)