Results 1 to 7 of 7

Thread: [RESOLVED] Generating parallel lines.

  1. #1

    Thread Starter
    Lively Member
    Join Date
    Sep 2011
    Posts
    121

    Resolved [RESOLVED] Generating parallel lines.

    I have a line defined by coordinates (x1,y1,x2,y2) and pairs of circles that are centered on (x1,y1). What I am looking for is a way to calculate the line coords for a connection between circles that are parallel to the original line but offset on either side by some fixed distance d.

    In the picture that should (I hope) show up below, the green line is the original, what I want are the two red ones.

    Name:  ShowCircle.png
Views: 300
Size:  759 Bytes

  2. #2
    Sinecure devotee
    Join Date
    Aug 2013
    Location
    Southern Tier NY
    Posts
    6,582

    Re: Generating parallel lines.

    I think if the two side lines were offset by angle, i.e. a rotation it would be simpler, but to keep them parallel to the center line is a bit of a different requirement.

    There are probably quite a few ways to go about it, but here is what I came up with.
    Determine the normalized (both values will be in the range 0 to 1) change in X and Y for the (x1,y1,x2,y2) line. (basically the slope)
    circ1Rad is the radius of the inner circle and circ2Rad is the radius of the outer circle, which is also our x1,y1 to x2,y2 distance.
    Code:
        Dim mx As Double, my As Double
        
        If x1 <> x2 Then
          mx = (x2 - x1) / circ2Rad
          my = (y2 - y1) / circ2Rad
        Else
          If y2 < y1 Then
            my = -1
          Else
            my = 1
          End If
        End If
    Next, figure out how long a line parallel to the line and "d" distance from it would be to intercept the inner radius and the outer radius.
    circ1Rad would be the hypotenuse and d the base leg of a right triangle, whose other side would be the length of the line parallel to the center line that intersects the radius of the circle.
    Calculate that length using the c^2 = a^2 + b^2 rule. Do this for both circles.
    Code:
        Dim d1 As Double, d2 As Double 'd1 is the distance to the inner circle from the parallel point, d2 to the outer
        d1 = Sqr((circ1Rad * circ1Rad) - (d * d))
        d2 = Sqr((circ2Rad * circ2Rad) - (d * d))
    Using the slope values determined earlier, use the y value to calulate x, and x value to calculate y, which will give you a relative offset perpendicular to the original line.
    Code:
        Dim xd As Single, yd As Single
        xd = d * my  '90 deg
        yd = d * mx
    Use the two lengths of the parallel lines to the circles to compute where two points at those distances fall on the existing center line.
    Code:
        Dim x1r As Single, x2r As Single, y1r As Single, y2r As Single
        x1r = x1 + d1 * mx
        y1r = y1 + d1 * my
        
        x2r = x1 + d2 * mx
        y2r = y1 + d2 * my
    Now you can offset those two points (x1r, y1r , x2r, y2r) by the 90 degree d offsets to get your two line segments.
    You didn't specify what language you're using, or what you wanted those points called, but for testing I just did a quick program in VB6 to verify it worked.
    I just offset the points directly in the Line drawing statement, rather than create four more variables.
    the signs of the xd and yd offsets are just reversed in the one case, to offset from the center point in both directions.
    Code:
         Line (x1r + xd, y1r - yd)-(x2r + xd, y2r - yd)
         Line (x1r - xd, y1r + yd)-(x2r - xd, y2r + yd)
    Last edited by passel; Jun 25th, 2015 at 02:08 PM.

  3. #3
    Sinecure devotee
    Join Date
    Aug 2013
    Location
    Southern Tier NY
    Posts
    6,582

    Re: Generating parallel lines.

    You didn't indicate what language you're using, but I'll post the VB6 code I used to test it, and a quick ported to VB.Net version of the test code as well.
    The VB.Net code is pretty much a straight translation of the VB6 code, so is not expected to be representative of good VB.Net code.

    For the VB6 version, add a couple of mostly horizontal pictureboxes (like a horizontal scrollbar in height,width). Dragging on the first will change the angle of the line, and dragging on the second will change the "d" value (the offset of the parallel lines).
    Name:  parallelRadius.png
Views: 170
Size:  6.7 KB
    Code:
    Option Explicit
    Dim ang As Double
    Dim circ1Rad As Single, circ2Rad As Single
    Dim x1 As Single, x2 As Single, y1 As Single, y2 As Single
    Dim d As Single
    Dim PI As Double
    Dim DEG2RAD As Double
    
    Private Sub Form_Load()
      PI = Atn(1) * 4
      DEG2RAD = PI / 180
      
      AutoRedraw = True
      ScaleMode = vbPixels
      Picture1.ScaleMode = vbPixels
      Picture2.ScaleMode = vbPixels
      circ1Rad = 60
      circ2Rad = 80
      x1 = 200: y1 = 200
      d = 10
    End Sub
    
    Private Sub Picture1_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)
      Static lx As Single
      
      If Button = vbLeftButton Then
        ang = ang + (X - lx)
        If ang > 360 Then ang = ang - 360
        If ang < 0 Then ang = ang + 360
        Redraw
      End If
      
      lx = X
    End Sub
    
    Private Sub Picture2_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)
      Static lx As Single
      
      If Button = vbLeftButton Then
        d = d + X - lx
        If d > circ1Rad Then d = circ1Rad
        If d < 2 Then d = 2
        Redraw
      End If
      lx = X
    End Sub
    
    Private Sub Redraw()
      Dim angR As Single
      Dim SinA As Double, CosA As Double
      
      Cls
      Circle (x1, y1), circ1Rad
      Circle (x1, y1), circ2Rad
        
      angR = ang * DEG2RAD
      SinA = Sin(angR)
      CosA = Cos(angR)
      
      x2 = x1 + circ2Rad * Sin(angR)
      y2 = y1 - circ2Rad * Cos(angR)
      Dim mx As Double, my As Double
      
      Line (x1, y1)-(x2, y2)
      If x1 <> x2 Then
        mx = (x2 - x1) / circ2Rad
        my = (y2 - y1) / circ2Rad
      Else
        If y2 < y1 Then
          my = -1
        Else
          my = 1
        End If
      End If
      
      Dim d1 As Double, d2 As Double
      d1 = Sqr((circ1Rad * circ1Rad) - (d * d))
      d2 = Sqr((circ2Rad * circ2Rad) - (d * d))
      Dim xd As Single, yd As Single
      xd = d * my  '90 deg
      yd = d * mx
      Dim x1r As Single, x2r As Single, y1r As Single, y2r As Single
      x1r = x1 + d1 * mx
      y1r = y1 + d1 * my
      
      x2r = x1 + d2 * mx
      y2r = y1 + d2 * my
      
      Line (x1r + xd, y1r - yd)-(x2r + xd, y2r - yd)
      Line (x1r - xd, y1r + yd)-(x2r - xd, y2r + yd)
    End Sub
    For the VB.Net version, I'll use labels instead of pictureboxes, and have the code add them so you should only have to paste the code into a new project and not need to add any controls.
    Dragging on the Angle label will change the angle, and dragging on the Width label will change "d".
    Code:
    Imports System.Math
    
    Public Class Form1
      Private ang As Double
      Private circ1Rad As Integer = 60
      Private circ2Rad As Integer = 80
      Private x1 As Integer = 200
      Private y1 As Integer = 200
      Private x2 As Integer = x1
      Private y2 As Integer = y1 - circ2Rad
      Private d As Single = 10
      Const DEG2RAD = PI / 180
    
      Private WithEvents labelAngle As New Label
      Private WithEvents labelDist As New Label
    
      Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
        DoubleBuffered = True
        labelAngle.BorderStyle = BorderStyle.FixedSingle
        labelAngle.AutoSize = False
        labelAngle.Bounds = New Rectangle(0, 0, 300, 20)
        labelAngle.Visible = True
        labelAngle.Text = "Angle"
        Controls.Add(labelAngle)
    
        labelDist.BorderStyle = BorderStyle.FixedSingle
        labelDist.AutoSize = False
        labelDist.Bounds = New Rectangle(0, 25, 200, 20)
        labelDist.Visible = True
        labelDist.Text = "Width"
        Controls.Add(labelDist)
      End Sub
    
      Private Sub Form1_Paint(sender As Object, e As System.Windows.Forms.PaintEventArgs) Handles Me.Paint
        Dim angR As Double
        Dim SinA As Double, CosA As Double
        Dim g As Graphics = e.Graphics
    
        g.DrawEllipse(Pens.Black, New Rectangle(x1 - circ1Rad, y1 - circ1Rad, 2 * circ1Rad, 2 * circ1Rad))
        g.DrawEllipse(Pens.Black, New Rectangle(x1 - circ2Rad, y1 - circ2Rad, 2 * circ2Rad, 2 * circ2Rad))
    
        angR = ang * DEG2RAD
        SinA = Sin(angR)
        CosA = Cos(angR)
    
        x2 = CInt(x1 + circ2Rad * Sin(angR))
        y2 = CInt(y1 - circ2Rad * Cos(angR))
        Dim mx As Double, my As Double
    
        g.DrawLine(Pens.Black, x1, y1, x2, y2)
        If x1 <> x2 Then
          mx = (x2 - x1) / circ2Rad
          my = (y2 - y1) / circ2Rad
        Else
          If y2 < y1 Then
            my = -1
          Else
            my = 1
          End If
        End If
    
        Dim d1 As Double, d2 As Double
        d1 = Sqrt((circ1Rad * circ1Rad) - (d * d))
        d2 = Sqrt((circ2Rad * circ2Rad) - (d * d))
        Dim xd As Integer, yd As Integer
        xd = CInt(d * my)  '90 deg
        yd = CInt(d * mx)
        Dim x1r As Single, x2r As Single, y1r As Single, y2r As Single
        x1r = CInt(x1 + d1 * mx)
        y1r = CInt(y1 + d1 * my)
    
        x2r = CInt(x1 + d2 * mx)
        y2r = CInt(y1 + d2 * my)
    
        g.DrawLine(Pens.Red, x1r + xd, y1r - yd, x2r + xd, y2r - yd)
        g.DrawLine(Pens.Red, x1r - xd, y1r + yd, x2r - xd, y2r + yd)
    
      End Sub
    
      Private Sub labelAng_MouseMove(sender As Object, e As System.Windows.Forms.MouseEventArgs) Handles labelAngle.MouseMove
        Static lx As Single
    
        If e.Button = Windows.Forms.MouseButtons.Left Then
          ang = ang + (e.X - lx)
          If ang > 360 Then ang = ang - 360
          If ang < 0 Then ang = ang + 360
          Invalidate()
        End If
    
        lx = e.X
      End Sub
    
      Private Sub labelDist_MouseMove(sender As Object, e As System.Windows.Forms.MouseEventArgs) Handles labelDist.MouseMove
        Static lx As Single
    
        If e.Button = Windows.Forms.MouseButtons.Left Then
          d = d + e.X - lx
          If d > circ1Rad Then d = circ1Rad
          If d < 2 Then d = 2
          Invalidate()
        End If
    
        lx = e.X
      End Sub
    End Class
    Last edited by passel; Jun 25th, 2015 at 02:10 PM.

  4. #4

    Thread Starter
    Lively Member
    Join Date
    Sep 2011
    Posts
    121

    Re: Generating parallel lines.

    Okay, I've tried the program and it mostly works but I have a very weird error... (using VB 2008)

    Code:
    Imports System.Math
    
    Public Class Form1
        Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button3.Click
            Dim bmp As New Bitmap(1700, 1700)
            Dim g As Graphics = Graphics.FromImage(bmp)
            Dim ang As Double
            Dim circ1Rad As Integer = 60
            Dim circ2Rad As Integer = 80
            Dim x1 As Integer = 850
            Dim y1 As Integer = 850
            Dim x2 As Integer = x1
            Dim y2 As Integer = y1 - circ2Rad
            Dim d As Single = 10
            Const DEG2RAD = PI / 180
            Dim angR As Double
            Dim SinA As Double, CosA As Double
            Dim d1 As Double, d2 As Double
            Dim xd As Integer, yd As Integer
            Dim x1r As Single, x2r As Single, y1r As Single, y2r As Single
            Dim mx As Double, my As Double
            For t = 850 To 100 Step -50
                circ1Rad = t - 10
                circ2Rad = t - 40
                g.DrawEllipse(Pens.Black, New Rectangle(x1 - circ1Rad, y1 - circ1Rad, 2 * circ1Rad, 2 * circ1Rad))
                g.DrawEllipse(Pens.Black, New Rectangle(x1 - circ2Rad, y1 - circ2Rad, 2 * circ2Rad, 2 * circ2Rad))
    
                ' WEIRDNESS STARTS HERE!
    
                For a = 0 To 4 ' This is the line I change
    
                    ang = 72 * a
                    angR = ang * DEG2RAD
                    SinA = Sin(angR)
                    CosA = Cos(angR)
                    x2 = CInt(x1 + circ2Rad * Sin(angR))
                    y2 = CInt(y1 - circ2Rad * Cos(angR))
                    If x1 <> x2 Then
                        mx = (x2 - x1) / circ2Rad
                        my = (y2 - y1) / circ2Rad
                    Else
                        If y2 < y1 Then
                            my = -1
                        Else
                            my = 1
                        End If
                    End If
                    d1 = Sqrt((circ1Rad * circ1Rad) - (d * d))
                    d2 = Sqrt((circ2Rad * circ2Rad) - (d * d))
                    xd = CInt(d * my)  '90 deg
                    yd = CInt(d * mx)
                    x1r = CInt(x1 + d1 * mx)
                    y1r = CInt(y1 + d1 * my)
                    x2r = CInt(x1 + d2 * mx)
                    y2r = CInt(y1 + d2 * my)
                    g.DrawLine(Pens.Black, x1r + xd, y1r - yd, x2r + xd, y2r - yd)
                    g.DrawLine(Pens.Black, x1r - xd, y1r + yd, x2r - xd, y2r + yd)
                Next
            Next
            bmp.Save("D:\Games\TestPic.bmp")
            End
        End Sub
    End Class
    If I try to do just the 0 degree angle (For a = 0 to 0) you get it working just fine with concentric circles marked out correctly. If I do it with a = 1 to 4, that also works just fine with four radius lines coming in. But if I try to get all five at once, a = 0 to 4... it messes up with the four coming in just fine but the first one showing up correctly only on the outer ring and then diagonal parallel lines coming in from the top left. ... Any ideas? I can get around it, but still it's annoying. In any case, thanks for this! This made life much simpler.
    Last edited by OddGamer; Jun 25th, 2015 at 03:52 PM.

  5. #5
    Only Slightly Obsessive jemidiah's Avatar
    Join Date
    Apr 2002
    Posts
    2,431

    Re: Generating parallel lines.

    Minor mistake: "mx = (x2 - x1) / circ2Rad" should be pulled out of the If statement and put right before it. mx is not getting properly reset in the x1=x2 case, which only shows up in the a=0 (or multiple of 5, I suppose) case, hence your bizarre behavior. Your code could be cleaned up a fair amount, for instance by putting the stuff inside the For a loop into a separate function. This error would have been (harmlessly) masked by this reorganization.
    The time you enjoy wasting is not wasted time.
    Bertrand Russell

    <- Remember to rate posts you find helpful.

  6. #6
    Sinecure devotee
    Join Date
    Aug 2013
    Location
    Southern Tier NY
    Posts
    6,582

    Re: Generating parallel lines.

    Or set mx to 0 in the else case.
    Code:
                    If x1 <> x2 Then
                        mx = (x2 - x1) / circ2Rad
                        my = (y2 - y1) / circ2Rad
                    Else
                        mx = 0
                        If y2 < y1 Then
                            my = -1
                        Else
                            my = 1
                        End If
                    End If
    I was setting mx to 0 in the else case initially (but only had one "my = -1" also initially), i.e.
    Code:
                   If x1 <> x2 Then
                        mx = (x2 - x1) / circ2Rad
                        my = (y2 - y1) / circ2Rad
                    Else
                        my = -1: mx = 0
                    End If
    When I realized I needed to have my = 1 at the 180 degree angle (x1 = x2 again), I added the additional check for the sign of y2 - y1, but removed the mx = 0 since it is initialized to 0 by VB when memory allocated.

    But I wasn't doing repeated passes.
    As jemidiah says, if that code was put in a sub or function, then it would also be relying on mx being initialized to 0 at the start of the routine.
    I got a little too aggressive removing "unneeded code", which because of your loop, became needed.

    =====================================
    p.s. DON'T NEED THE ELSE
    =====================================
    Actually, looking back at this, the else case isn't even needed.
    At some early stage, I was doing a DeltaY / DeltaX type operation and I wanted to make sure I didn't divide by 0, so put in the check for the two X values being the same.

    But then I changed it so the DeltaY wasn't divided by DeltaX, but both DeltaY and DeltaX were divided individually by the Line distance to Normalize the deltas (put in the range of 0 to 1).
    So, we don't have to protect against dividing by 0 (unless the programmer does something dumb by providing a circle with a radius of zero).
    So, remove the If else condition block, which gets rid of nine lines of code (8 if you didn't put the "mx = 0" in yet).
    Code:
    '... This
                    If x1 <> x2 Then
                        mx = (x2 - x1) / circ2Rad
                        my = (y2 - y1) / circ2Rad
                    Else
                        mx = 0
                        If y2 < y1 Then
                            my = -1
                        Else
                            my = 1
                        End If
                    End If
    
    '... becomes this
            mx = (x2 - x1) / circ2Rad
            my = (y2 - y1) / circ2Rad
    Last edited by passel; Jun 25th, 2015 at 06:19 PM. Reason: Clarify

  7. #7

    Thread Starter
    Lively Member
    Join Date
    Sep 2011
    Posts
    121

    Re: Generating parallel lines.

    Quote Originally Posted by jemidiah View Post
    Minor mistake: "mx = (x2 - x1) / circ2Rad" should be pulled out of the If statement and put right before it. mx is not getting properly reset in the x1=x2 case, which only shows up in the a=0 (or multiple of 5, I suppose) case, hence your bizarre behavior. Your code could be cleaned up a fair amount, for instance by putting the stuff inside the For a loop into a separate function. This error would have been (harmlessly) masked by this reorganization.
    Gah! Yes. Thanks. Works perfectly now. Sometimes I forget to break down my code. Thanks again! Works awesomely!

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width