Equations of Lines in Space

In 2D coordinates, AX + BY + C = 0 is the equation of a line. In 3D coordinates, this equation represents a plane. In 3D coordinates its not possible to specify a line by a single equation. A 3D line is represented by 3 equations, one for each x,y and z.

A straight line in space is completely determined by two points. It is also completely determined with a single point and a set of direction numbers. The direction numbers are the difference of the coordinates over any segment. Direction numbers come in sets of three. Its the same idea as a vector. A line has an infinite amount of direction number sets, all of them proportional. Just as you can multiply a vector by a scalar, and obtain another vector parallel but with a different length, you can multiply direction numbers by a scalar, call it t, and obtain further points on the same line.

If two points are given, (x1,y1,z1) and (x2,y2,z2), direction numbers are (x2-x1, y2-y1, z2-z1). These are often referred to as (a,b,c).

Either point can be used for the given point

X = x1 + at
Y = y1 + bt
Z = z1 + ct

To create an unending line in both directions, t takes on values between negative and positive infinity.

if we take an example, let one point P1 be the origin and the other point be (2,3,4). use P1 for the given point and (2,3,4) for (a,b,c)

X = 2t
Y = 3t
Z = 4t

the length of a line segment is the sq root of the sum of the coordinate deltas squared, so Len (2,3,4) is 29^1/2

Len = \sqrt{x^2 + y^2 + z^2} = \sqrt{4 + 9 +16} = \sqrt{29}

when t = 0, the point on the line is the origin. When t=1, the point is (2,3,4).

when
Len = \sqrt{29}

t = 1

so if

Len = 1

t = \frac{1} {\sqrt{29}}

when the line segment is 1, the coordinates are the direction cosines. the angle between the line and each of the coordinate axes can be found by taking the ArcCosine.

to put an arrowhead on a 3D vector, i insert an arrowhead block. I wanted to draw and do a revolve, but i was not initially able to find the activex method for revolve, so insert a block is the standby. i drew the cone shape the same size as dimension arrowheads so the scale factor works in a similar way. the insertion point is the head of the vector. we need the angle. i pass a direction vector, which is a parallel vector any length. normally i will just pass the same vector, but sometimes like when constructing an XYZ axes it is just as convenient to pass a unit vector.

Sub arr(pt1() As Double, D() As Double)
‘3D arrow
‘pt1 is location of the arrow
‘D is direction vector

the direction cosines of the direction vector are the familiar array of 3 doubles.

Dim dir_cos() As Double ‘direction cosines
dir_cos = ret_3D_angle(D)

the function ret_3D_angle takes one parameter, the direction vector, calculates the length, divides the x,y,z values by the length, and returns all 3 together in an array, just like a point.

the direction cosine for the x axis – the x coordinate for the direction vector at the place where the length is one – is used and the angle found for autocad to use later.

Dim alpha As Double
alpha = WorksheetFunction.Acos(dir_cos(0))

we have not passed in the actual tail coordinate of the vector we are trying to arrow. we have passed in the head coordinate and a vector parallel. the direction vector is positionless. its just 3 numbers. if we subtract those from the head coordinates, we will have a second point on the vector.

when we do that we have enough coordinates to change the user coordinate system, ucs in autocad, to a plane defined by the two lines, the vector itself and a line from the tail point just calculated parallel to the x-axis.

First we insert the arrowhead at rotation zero at the world coordinate system. then change the user coordinate system to the one defined by our vector and a line parallel to the x-axis. when you rotate an object, it rotates around a line perpendicular to the user coordinate system. so in effect by changing the user coordinate system, we have already made one rotation, even though we have not applied it yet. now rotate the arrowhead by the insertion point through alpha radians that we previously calculated. that completes the 3D arrow rotation.

Even though there are 3 angles from a vector to each of the axes, any two of them determine the third.

here is a diagram i did a while back on the arrowhead rotation problem.

x1,y1, z1 is any valid point on the vector which we have found above. to create a new ucs, a new origin is located, then a point on the new x-axis, and a point on the new y-axis. they must form a right angle. the new origin can be assembled from the head and tail coordinates. the new x-axis point can just add one value for x, and the new y-axis can use the head coordinates. some special error checking has to occur when the arrow is on the x-axis.


Sub arr(pt1() As Double, D() As Double)
'3D arrow
'pt1 is location of the arrow
'D is direction vector
    Dim x1 As Double, y1 As Double, z1 As Double
    Dim x2 As Double, y2 As Double, z2 As Double
    Dim origin() As Double, xAxis() As Double, yAxis() As Double
        
    Dim dir_cos() As Double  'direction cosines
    dir_cos = ret_3D_angle(D)
    
    Dim alpha As Double
    alpha = WorksheetFunction.Acos(dir_cos(0))
    
   Dim blkref As AcadBlockReference
   Dim blkname As String
   blkname = "Ar_Head3D"
   If sc = 0 Then sc = 1
       
   'need an illustration
   'pt1 is the location for the arrowhead
   'D is the direction vector
   'the new origin is x from pt1 and y and z calculated from tail of D
   
   'transfer pt1 to x2,y2,z2
   x2 = pt1(0)
   y2 = pt1(1)
   z2 = pt1(2)
   
   ' direction vector is positionless
   ' so we in effect put it at head and find tail
   x1 = x2 - D(0)
   y1 = y2 - D(1)
   z1 = z2 - D(2)
   
   ' the new origin and new xaxis can be calculated
   ' the new yaxis is the tip of the arrow
   origin = pt(x2, y1, z1)
   xAxis = pt(x2 + 1, y1, z1)
   yAxis = pt1
   set_wcs  ' make sure we insert at world ucs


   Set blkref = acadDoc.ModelSpace.InsertBlock(pt1, blkname, sc, sc, sc, 0)
   
   'error when d(1) = 0 ucs yaxis is same as origin
   'when alpha = 0 dont need to rotate
   'when alpha = pi dont need to change ucs
   'On Error Resume Next
   
   If dir_cos(0) = 1 Then Exit Sub
      
   If dir_cos(0) <> -1 Then
        Call set_ucs(origin, xAxis, yAxis, "UCS_alpha")
        End If
         
        blkref.Rotate pt1, alpha
        set_wcs
 
End Sub


Function ret_3D_angle(D() As Double) As Double()
'D is direction vector
'returns an array of 3 doubles that contain the direction cosines
Dim vector_len As Double
Dim pt1() As Double
Dim A As Double, B As Double, C As Double

vector_len = leng(D)

If vector_len = 0 Then
MsgBox "zero vector in ret_3D_angle"
Exit Function
End If

A = D(0) / vector_len
B = D(1) / vector_len
C = D(2) / vector_len

pt1 = pt(A, B, C)
ret_3D_angle = pt1

End Function


Sub set_ucs(origin() As Double, xAxis() As Double, yAxis() As Double, strName As String)
    Dim ucsObj As AcadUCS
    
    Set ucsObj = acadDoc.UserCoordinateSystems.Add(origin, xAxis, yAxis, strName)
    acadDoc.ActiveUCS = ucsObj
 End Sub
 
 
    Sub set_wcs()
     ' Call Connect_Acad
    
    Dim ucsObj As AcadUCS
    Dim pt0() As Double, ptx() As Double, pty() As Double
 
    pt0 = pt(0, 0, 0)
    ptx = pt(1, 0, 0)
    pty = pt(0, 1, 0)

   Set ucsObj = acadDoc.UserCoordinateSystems.Add(pt0, ptx, pty, "World")
   acadDoc.ActiveUCS = ucsObj
 End Sub

Direction Cosines

Inclination – angle in radians or degrees a 2D line makes with the positive x-axis, measuring counter-clockwise. A line extends indefinitely in both directions.

In radians, 0 <= theta < pi
In degrees, 0 <= theta < 180

Slope – tangent of the inclination, y/x, rise over run.

Slope m = Tan alpha = y/x

Lines are perpendicular if their slopes are m1 = -1/m2

Tan alpha = – tan (180 – alpha)

A 3D line, a 3D vector, is determined by any two points. If one of these points is the origin, the coordinates of every point on the line form a set of direction numbers. Every line has an infinite set of direction numbers. The numbers always have the same proportions. You can get the coordinates of any point by multiplying the coordinates of some other point by some constant.

The direction angles of a 3D line are defined to be the angle the line makes directly with each of the positive axes. Knowing any two the third can be calculated. The angles are limited to 0 to 180, similar to the 2D inclination, but not limited to being counter-clockwise.

For an undirected line that extends indefinitely in both directions, there are two sets of direction angles,
theta and (180 – theta)

The basic calculation of 3D line direction is the division of the direction numbers by the length between the two points that generated that set. This gives the direction cosines, the x,y and z values for a line of length one. Multiplying the direction cosines by a constant gives a point on the same line at distance of the constant. The 3 direction angles of the line can be found with the Cos(-1) function.

For instance a line from the origin to (3,4,5) has a length of 7.07. The direction cosines are 3/7.07, 4/7.07, 5/7.07), the angle of the line to each of the axes, xyz, by calculator cos(-1), is approx 65, 55, and 45 degrees. Lets check that out with autocad and VBA.


Sub test24()
Call Connect_Acad

Dim x As Double, y As Double, z As Double
Dim pt0() As Double
pt0 = pt(0, 0, 0)

Dim ptX() As Double 'pt on x axis to make alpha ucs
Dim ptY() As Double 'pt on y axis to make beta ucs
Dim ptZ() As Double 'pt on z axis to make gamma ucs

'i could re-use these to only have one
Dim ptx2() As Double 'pt to define new x axis for alpha
Dim pty2() As Double 'pt to define new x axis for beta
Dim ptz2() As Double 'pt to define new x axis for gamma

Dim A() As Double, B() As Double, C() As Double
Dim dir_cos() As Double
Dim dir_ang() As Double

x = -3: y = -4: z = 5
A = pt(x, y, z)

'draw the autocad line
vec_draw1 A

ptX = pt(x, 0, 0)
ptx2 = pt(x + 1, 0, 0)
ucs ptX, ptx2, A, "ucs_alpha"
ptX = pt(Abs(x), 0, 0)
dim_ang pt0, ptX, A, midpt2(ptX, A)
label_pt A, "A"

ptY = pt(0, y, 0)
pty2 = pt(0, y + 1, 0)
ucs ptY, pty2, A, "ucs_beta"
ptY = pt(0, Abs(y), 0)
dim_ang pt0, ptY, A, midpt2(ptY, A)

ptZ = pt(0, 0, z)
ptz2 = pt(0, 0, z + 1)
ucs ptZ, ptz2, A, "ucs_gamma"
ptZ = pt(0, 0, Abs(z))
dim_ang pt0, ptZ, A, midpt2(ptZ, A)

'the box is a visual aid not always required
'Dim pt1() As Double
'pt1 = pt(x / 2, y / 2, z / 2)
'solidbox pt1, Abs(x), Abs(y), Abs(z)

'here are the calculations
dir_cos = dir_cos1(A)
Debug.Print "< " & x; ", " & y & ", " & z & " >"
Debug.Print "alpha_cos " & dir_cos(0)
Debug.Print "beta_cos " & dir_cos(1)
Debug.Print "gamma_cos " & dir_cos(2)
Debug.Print

'direction cosines are the xyz values where the line
'has a length of one
'put an autocad point object at those coordinates
draw_pt dir_cos, "L=1"

dir_ang = dir_ang1(A)
Debug.Print "Alpha " & rad2deg(dir_ang(0))
Debug.Print "Beta " & rad2deg(dir_ang(1))
Debug.Print "Gamma " & rad2deg(dir_ang(2))
Debug.Print

're-set ucs to world
set_wcs

acadApp.Update
End Sub

Call the autocad dimangular method. First the ucs is changed for each of the 3 angles. UCS requires 3 points. a new origin, a point on the new x-axis, and a point on the new y-axis. the origin is variously either (x,0,0), (0,y,0), or (0,0,z), the point on the new x-axis is variously (x+1,0,0), (0,y+1,0) or (0,0,z+1). the point on the new y-axis is always the point of the vector. that establishes a right triangle in the plane of the vector and the axis to which the angular dimension is drawn.


Sub dim_ang(pt1() As Double, pt2() As Double, pt3() As Double, pt4() As Double)
 Dim dimObj As AcadDimAngular
 'Set dimObj = acadDoc.ModelSpace.AddDimAngular(AngleVertex, firstendpoint, secondendpoint, textpoint)
 Set dimObj = acadDoc.ModelSpace.AddDimAngular(pt1, pt2, pt3, pt4)
End Sub

Independently of the autocad dimangular method, the direction cosines and direction angles are calculated. Since these are always triples, the most convenient way to store them is in a 3 place array of doubles, exactly as point coordinates are stored. In fact the direction cosines are the point coordinates for any line at the point where the length of the line is one. Both direction cosines and direction angles are calculated for a vector with tail at the origin. The version to calculate with tail not at the origin would simply do the subtraction of coordinates then call this version.

Function dir_cos1(pt1() As Double) As Double()
Dim cos_alpha As Double, cos_beta As Double, cos_gamma As Double
Dim x As Double, y As Double, z As Double
Dim D As Double

D = dist1(pt1)

If D = 0 Then
dir_cos1 = pt(0, 0, 0)
Exit Function
End If

x = pt1(0): y = pt1(1): z = pt1(2)

cos_alpha = x / D
cos_beta = y / D
cos_gamma = z / D

dir_cos1 = pt(cos_alpha, cos_beta, cos_gamma)
End Function

Function dir_ang1(pt1() As Double) As Double()
Dim alpha As Double, beta As Double, gamma As Double
Dim dir_cos() As Double
Dim pt2(0 To 2) As Double

dir_cos = dir_cos1(pt1)

alpha = WorksheetFunction.Acos(dir_cos(0))
beta = WorksheetFunction.Acos(dir_cos(1))
gamma = WorksheetFunction.Acos(dir_cos(2))

pt2(0) = alpha
pt2(1) = beta
pt2(2) = gamma

dir_ang1 = pt2

End Function

testing the subroutine for values in all 8 quadrants. the angles are always measured to the positive side of the axis. therefore they are always between 0 and 180, never negative. Getting the confidence that all quadrants return true values, the next step is to develop a sub-routine that returns the angle between two lines.

3D Line – part 2

The distance formula

Function dist1(pt1() As Double) As Double
Dim x As Double, y As Double, z As Double
x = pt1(0): y = pt1(1): z = pt1(2)
dist1 = (x ^ 2 + y ^ 2 + z ^ 2) ^ (1 / 2)
End Function

Function dist2(pt1() As Double, pt2() As Double) As Double
Dim x As Double, y As Double, z As Double
x = pt2(0) - pt1(0)
y = pt2(1) - pt1(1)
z = pt2(2) - pt1(2)
dist2 = (x ^ 2 + y ^ 2 + z ^ 2) ^ (1 / 2)
End Function

test sub

Sub test17()
 Call Connect_Acad
Dim A() As Double, B() As Double
Dim x As Double, y As Double, z As Double

x = 5
y = 4
z = 3

A = pt(x, y, z)
B = pt(x, y, 0)

line1 A
txt1 midpt1(A), dec2(dist1(A))

line1 B
txt1 midpt1(B), dec2(dist1(B))

line1 pt(x, 0, 0)
txt1 midpt1(pt(x, 0, 0)), dec2(x)

line1 pt(0, y, 0)
line1 pt(0, 0, z)

line2 A, B
txt1 midpt2(A, B), dec2(dist2(A, B))

line2 pt(x, 0, 0), B
txt1 midpt2(pt(x, 0, 0), B), dec2(y)

label_pt2 A, "A", 2
label_pt2 B, "B", 2

Dim pt1() As Double
pt1 = pt(x / 2, y / 2, z / 2)
solidbox pt1, x, y, z

End Sub

text and label subs

Sub txt1(pt1() As Double, str As String, Optional height As Variant, Optional rotation As Variant)
  Dim objtxt As AcadText
  
  If IsMissing(height) Then
      height = textheight  'global var
    End If
    
  If IsMissing(rotation) Then
      rotation = 0
    End If
 
  Set objtxt = acadDoc.ModelSpace.AddText(str, pt1, height)
  objtxt.Layer = "0"
  
  If rotation <> 0 Then
  objtxt.rotation = deg2rad(CDbl(rotation))
  End If
     
 End Sub
 
Sub label_pt2(pt1() As Double, Optional str_label As String = "none", Optional prec As Integer = 4)

If IsMissing(height) Then
      height = textheight  'global var
    End If
    
Dim str As String
Dim x As Double, y As Double, z As Double
x = Round(pt1(0), prec)
y = Round(pt1(1), prec)
z = Round(pt1(2), prec)

str = "<" & x & "," & y & "," & z & ">"

If str_label <> "none" Then
str = str_label & " " & str
End If

txt1 pt1, str, textheight
End Sub
 
 Function dec2(number As Double) As Double
   Dim dbl As Double
   dbl = Round(number, 2)
   dec2 = dbl
 End Function

Acad3DSolid box sub. Transparency is a percentage 1 to 90. Higher numbers are more transparent.

Sub solidbox(pt() As Double, leng As Double, wid As Double, height As Double)

Dim objbox As Acad3DSolid
Set objbox = acadDoc.ModelSpace.AddBox(pt, leng, wid, height)
objbox.EntityTransparency = 80

acadApp.Update

End Sub

midpoint

Function midpt1(pt1() As Double) As Double()
Dim x As Double, y As Double, z As Double
x = pt1(0): y = pt1(1): z = pt1(2)
midpt1 = pt(x / 2, y / 2, z / 2)
End Function

Function midpt2(pt1() As Double, pt2() As Double) As Double()
Dim x1 As Double, y1 As Double, z1 As Double
x = (pt1(0) + pt2(0)) / 2
y = (pt1(1) + pt2(1)) / 2
z = (pt1(2) + pt2(2)) / 2
midpt2 = pt(x, y, z)
End Function

test sub


Sub test18()
Call Connect_Acad
Dim A() As Double, B() As Double, C() As Double

A = pt(2, 1, 3)
B = pt(3, -1, -2)
C = pt(0, 2, -1)

line2 A, B
line2 B, C
line2 C, A

line2 midpt2(A, B), midpt2(B, C)
line2 midpt2(B, C), midpt2(C, A)
line2 midpt2(C, A), midpt2(A, B)

label_pt2 A, "A"
label_pt2 B, "B"
label_pt2 C, "C"

label_pt2 midpt2(A, B)
label_pt2 midpt2(B, C)
label_pt2 midpt2(C, A)

acadApp.Update
End Sub

the angles to 3 decimal places

the triangles are all congruent. the middle triangle has half the perimeter of the outer triangle and one fourth the area.

3D Line – part 1

A straight line is completely determined by the coordinates of its endpoints. A straight line having a definite length and direction but no definite location in space is a vector. In its most basic form its a single point. A vector and a point are both a 3-array of doubles. Make them a dynamic array.

The functions to calculate direction, midpt, cosines, addition and scalar multiplication of vectors all return an array of 3 doubles. The line subroutines work with arrays of 3 doubles. Where I have one point argument, assuming 0,0,0 for the other, I number the routine_1. Where I have two point arguments, I number the routine_2. Passing and assigning points cleans up the code to almost logo like clarity.

dim pt1() as double, pt2() as double
pt1 = pt(2, 4, 6)
pt2 = pt(1, 9, 10)
line1 pt1
line1 pt2
line2 pt1, pt2
line2 midpt1(pt1), midpt1(pt2)

Function pt(x As Double, y As Double, z As Double) As Double()
Dim pnt(0 To 2) As Double
pnt(0) = x: pnt(1) = y: pnt(2) = z
pt = pnt
End Function

Sub line1(pt1() As Double)
'line from origin to pt1
Dim lineobj As AcadLine
Set lineobj = acadDoc.ModelSpace.AddLine(pt(0, 0, 0), pt1)
End Sub

Sub line2(pt1() As Double, pt2() As Double)
'line from pt1 to pt2
Dim lineobj As AcadLine
Set lineobj = acadDoc.ModelSpace.AddLine(pt1, pt2)
End Sub

Sub line3(startpt1() As Double, vectorpt2() As Double)
'line from startpoint at vector distance and angle
Dim lineobj As AcadLine
Dim pt3() As Double
pt3 = add_vectors(startpt1, vectorpt2)
Set lineobj = acadDoc.ModelSpace.AddLine(startpt1, pt3)
End Sub
Function dist1(pt1() As Double) As Double
Dim x As Double, y As Double, z As Double
x = pt1(0): y = pt1(1): z = pt1(2)
dist1 = (x ^ 2 + y ^ 2 + z ^ 2) ^ (1 / 2)
End Function

Function dist2(pt1() As Double, pt2() As Double) As Double
Dim x As Double, y As Double, z As Double
x = pt2(0) - pt1(0)
y = pt2(1) - pt1(1)
z = pt2(2) - pt1(2)
dist2 = (x ^ 2 + y ^ 2 + z ^ 2) ^ (1 / 2)
End Function

Function midpt1(pt1() As Double) As Double()
Dim x As Double, y As Double, z As Double
x = pt1(0): y = pt1(1): z = pt1(2)
midpt1 = pt(x / 2, y / 2, z / 2)
End Function

Function midpt2(pt1() As Double, pt2() As Double) As Double()
Dim x1 As Double, y1 As Double, z1 As Double
x = (pt1(0) + pt2(0)) / 2
y = (pt1(1) + pt2(1)) / 2
z = (pt1(2) + pt2(2)) / 2
midpt2 = pt(x, y, z)
End Function


Function dir_cosines1(pt1() As Double) As Double()
Dim cos_alpha As Double, cos_beta As Double, cos_gamma As Double
Dim d As Double

d = dist1(pt1)

If d = 0 Then
dir_cosines1 = pt(0, 0, 0)
Exit Function
End If

cos_alpha = pt1(0) / d
cos_beta = pt1(1) / d
cos_gamma = pt1(2) / d

dir_cosines1 = pt(cos_alpha, cos_beta, cos_gamma)
End Function


Function dir_cosines2(pt1() As Double, pt2() As Double) As Double()
Dim cos_alpha As Double, cos_beta As Double, cos_gamma As Double
Dim d As Double

d = dist1(pt1)

If d = 0 Then
dir_cosines2 = pt(0, 0, 0)
Exit Function
End If

cos_alpha = (pt2(0) - pt1(0)) / d
cos_beta = (pt2(1) - pt1(1)) / d
cos_gamma = (pt2(2) - pt1(2)) / d

dir_cosines2 = pt(cos_alpha, cos_beta, cos_gamma)
End Function

Function dir_angle(dir_cosine As Double) As Double
dir_angle = WorksheetFunction.Acos(dir_cosine)
dir_angle = rad2deg(dir_angle)
End Function

Function add_vectors(pt1() As Double, pt2() As Double) As Double()
add_vectors = pt(pt1(0) + pt2(0), pt1(1) + pt2(1), pt1(2) + pt2(2))
End Function

Function mult_vector(n As Double, pt1() As Double) As Double()
mult_vector = pt(n * pt1(0), n * pt1(1), n * pt1(2))
End Function

Sub test5()
Call Connect_Acad

pt1 = pt(2, 4, 6)
pt2 = pt(1, 9, 10)
line1 pt1
line1 pt2
line2 midpt1(pt1), midpt1(pt2)

Dim cosines() As Double
cosines = dir_cosines1(pt1)
pt3 = mult_vector(10, cosines)

'makes a line 10 in length along same line as pt1
line1 pt3

'alpha angle
Debug.Print dir_angle(cosines(0))
End Sub

Sub test6()
Call Connect_Acad

pt1 = pt(5, 1, 2)
pt2 = pt(1, 2, 3)
pt3 = add_vectors(pt1, pt2)

line1 pt1
line3 pt1, pt2
line1 pt3

acadApp.Update
End Sub