-
Jun 25th, 2015, 06:57 AM
#1
Thread Starter
Lively Member
[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.
-
Jun 25th, 2015, 12:56 PM
#2
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.
-
Jun 25th, 2015, 01:51 PM
#3
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).
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.
-
Jun 25th, 2015, 03:30 PM
#4
Thread Starter
Lively Member
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.
-
Jun 25th, 2015, 05:22 PM
#5
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.
-
Jun 25th, 2015, 05:40 PM
#6
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
-
Jun 25th, 2015, 06:08 PM
#7
Thread Starter
Lively Member
Re: Generating parallel lines.
Originally Posted by jemidiah
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
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|