More Turtle Graphics

A basic class implementation for turtle graphics in autocad –

The basic idea of turtle graphics is that the pen has a location and a direction. The instruction to draw a line only needs distance. The ending point is the new current location. To translate distance at angle from a given point to coordinates the Sin and Cosine are used.

The public class variables are x1, y1 – the current location, the beginning of the line to be drawn, and x2, y2, the calculated end points. Heading is a private double. It is private so that the class can always keep the angle heading between 0 <= heading < 360 no matter how many cumulative turns. A boolean PEN variable allows a PENUP or PENDOWN state.

Turtle1.FD 6 draws a line 6 units (assuming PEN is DOWN). Turtle.Left 45 and Turtle.Right 45 turn heading to the left or right 45 degrees or any number. Input to the user is in degrees. The class module converts to radians in private.

some example code using the turtle class –

Sub turtle_demo_A()
connect_acad

Dim turtle1 As CTurtle
Set turtle1 = New CTurtle

Debug.Print turtle1.x1
Debug.Print turtle1.y1
Debug.Print turtle1.pen
'x1 and y1 are zero
'pen is true
'these are class defaults

Debug.Print turtle1.heading
'access to private variable pheading is thru LET and GET HEADING
'class_initialize default is 90

turtle1.heading = 540
Debug.Print turtle1.heading
'property Let heading (double)
'seems like an argument but you call LET with assignment
'property let heading sets and controls value between 0 <= pheading < 360
'heading is 180

turtle1.heading = turtle1.heading + 180
Debug.Print turtle1.heading
'heading is 0
'just like A = A + 3
'right side of equation is GET and left side is LET

turtle1.heading = turtle1.heading - 45
Debug.Print turtle1.heading
'heading is 315

turtle1.left 90
Debug.Print turtle1.heading
'heading is 45

turtle1.right 90
Debug.Print turtle1.heading
'heading is 315

turtle1.heading = turtle1.heading + 180
Debug.Print turtle1.heading
'heading is 135

turtle1.fd 12
'line is 45 to left

End Sub

all this boils down to –


Sub turtle_demo_B()
connect_acad

Dim turtle1 As CTurtle
Set turtle1 = New CTurtle

turtle1.heading = 45
turtle1.x1 = 2
turtle1.y1 = 3

turtle1.fd 12
End Sub

The Class Module code for the turtle to draw a line contains the trigonometry to calculate the point at dist and angle. It draws the line using the familiar AddLine method with EndPoints as an array of 3 doubles, xyz. This is a 2D implementation, Z is always zero for now but does not have to be.

Public Sub fd(dist As Double)
'assumes x1 y1 and heading
x2 = x1 + dist * Cos(ang2rad(pheading))
y2 = y1 + dist * Sin(ang2rad(pheading))

If pen Then
Call drawline(x1, y1, x2, y2)
End If

'updates to new position
x1 = x2
y1 = y2
End Sub

Sub drawline(x1 As Double, y1 As Double, x2 As Double, y2 As Double)
'internal sub to draw line
Dim acadline As acadline
Dim pt1(0 To 2) As Double
Dim pt2(0 To 2) As Double
pt1(0) = x1: pt1(1) = y1: pt1(2) = 0
pt2(0) = x2: pt2(1) = y2: pt2(2) = 0

Set acadline = acadDoc.ModelSpace.AddLine(pt1, pt2)
Update
End Sub

Drawing the same line in code with no class implementation –

Sub turtle_demo_C()
connect_acad

Dim acadline As acadline
Dim pt1(0 To 2) As Double
Dim pt2(0 To 2) As Double

Dim x1 As Double, y1 As Double
Dim x2 As Double, y2 As Double

Dim ang As Double
Dim dist As Double

x1 = 2
y1 = 3
ang = 45
dist = 12

x2 = x1 + dist * Cos(ang2rad(ang))
y2 = y1 + dist * Sin(ang2rad(ang))

pt1(0) = x1: pt1(1) = y1: pt1(2) = 0
pt2(0) = x2: pt2(1) = y2: pt2(2) = 0

Set acadline = acadDoc.ModelSpace.AddLine(pt1, pt2)
acadApp.Update
End Sub

Instead of doing the calculations, Autocad provides the Utility PolarPoint to do the trig. Polarpoint returns an array. Autodesk help uses a Variant to capture it, but a dynamic array works fine (see pt2).

Sub turtle_demo_D()
connect_acad

Dim acadline As acadline
Dim pt1(0 To 2) As Double
Dim pt2() As Double

Dim x1 As Double, y1 As Double

Dim ang As Double
Dim dist As Double

x1 = 0
y1 = 0
ang = 45
dist = 12

'these are commented out
'x2 = x1 + dist * Cos(ang2rad(ang))
'y2 = y1 + dist * Sin(ang2rad(ang))

pt1(0) = x1: pt1(1) = y1: pt1(2) = 0
pt2 = acadDoc.Utility.PolarPoint(pt1, ang2rad(ang), dist)

Set acadline = acadDoc.ModelSpace.AddLine(pt1, pt2)
acadApp.Update
End Sub

We can further simplify this process with a function to populate point arrays. Every Point can be declared as a dynamic array.

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 turtle_demo_E()
connect_acad

Dim acadline As acadline
Dim pt1() As Double
Dim pt2() As Double

Dim ang As Double
Dim dist As Double

ang = 45
dist = 12

pt1 = Pt(2, 3, 0)
pt2 = acadDoc.Utility.PolarPoint(pt1, ang2rad(ang), dist)

Set acadline = acadDoc.ModelSpace.AddLine(pt1, pt2)
acadApp.Update
End Sub

we can streamline a little bit more with a dedicated Line sub-routine. now lets compare the turtle and more conventional autocad methods. Each will draw a line and turn before drawing the next. Using dynamic arrays, what was pt2 can become pt1 with a simple assignment. That is not possible with the conventional static array where points are declared as – Dim PT1 (0 to 2) as Double.

Sub turtle_demo_F()
connect_acad

Dim turtle1 As CTurtle
Set turtle1 = New CTurtle

turtle1.heading = 30
turtle1.x1 = 1
turtle1.y1 = 2

turtle1.fd 12
turtle1.left 30
turtle1.fd 12

Dim pt1() As Double
Dim pt2() As Double

Dim ang As Double
Dim dist As Double

ang = 45
dist = 12

pt1 = Pt(1, 2, 0)
pt2 = acadDoc.Utility.PolarPoint(pt1, ang2rad(ang), dist)
line1 pt1, pt2

pt1 = pt2
pt2 = acadDoc.Utility.PolarPoint(pt1, ang2rad(ang + 30), dist)
line1 pt1, pt2

acadApp.Update
End Sub

Turtle graphics has geometry implications, start here, go forward, turn and repeat. Turtle graphics is local with a simple interface and limited command set. Coordinate graphics is a global grid, but its interface can also be simplified. The two approaches might be able to work together.

A random star generator –

Sub turtle_demo_6()
init_turtle
Dim dblsize As Double
Dim inc As Integer

For inc = 1 To 480

dblsize = rnddbl(0, 360)
turtle1.heading = dblsize

dblsize = rnddbl(0, 1024)
turtle1.x1 = dblsize

dblsize = rnddbl(512, 1024)
turtle1.y1 = dblsize

dblsize = rnddbl(2, 17)
star_5 dblsize

Next inc

End Sub

Sub star_5(dblsize As Double)

For i = 1 To 5
turtle1.fd dblsize
turtle1.right 144
Next i

End Sub

Function rnddbl(upr As Double, lwr As Double) As Double
' Randomize
' better results without Randomize

rnddbl = CDbl((upr - lwr + 1) * Rnd + lwr)
End Function

any comment in the code causes wordpress to substitute the html equivalent, even the code tag vb with quotations gets corrupted. use wordpress code tag and it moves by itself above an empty line. I really struggle sometimes with wordpress and its code behavior. The only way i even get it to work is to load the old editor. There is an easier way to edit posts, they helpfully remind me. I tried that a few times.

Advertisements

If Euclid had Autocad

The 2300 year old geometry primer begins with definitions for point, line and plane surface establishing these concepts which will be used virtually unchanged even in modern cad applications. A point has no parts, it says. Later geometers will add it only has position but no dimension. A line has only length, no width, only one dimension. A plane surface has both length and width, two dimensions. 17th century mathematicians added XYZ variables to locate position. Euclid constructed, stated (“I say that…”), and proved elementary facts of geometry by starting with the most basic usable definitions and with virtual or real tools of straightedge and compass, that only allowed lines and circles to be constructed, made a textbook of geometry theorems that all depend on previous constructions for the proof of their canonic accuracy. His very first theorem constructs an equilateral triangle, a triangle with all sides and all angles equal, from a single random line.

Euclid has been criticized by moderns because he included no previous definition, postulate or axiom that two circles overlapping intersect in a single point. I thought finding that intersection point would be the hardest part of programming it, but there is a dedicated method, Intersectwith, for nearly every autocad object which returns a single dimension array of x, y and z values for all intersections. Here I just choose the first one. I use a random number generator to create the seed line.


Sub prime_pr1()
'given lineAB call proposition1
Connect_Acad

Dim ptA(0 To 2) As Double
Dim ptB(0 To 2) As Double
Dim Ax As Double, Ay As Double
Dim Bx As Double, By As Double

Ax = rnddbl(0, 10)
Ay = rnddbl(0, 10)
Bx = rnddbl(11, 20)
By = rnddbl(0, 10)

Call pt(ptA, Ax, Ay, 0)
Call pt(ptB, Bx, By, 0)
   
   Call pr1(ptA, ptB)
acadApp.Update
End Sub


Sub pr1(ptA() As Double, ptB() As Double)
Dim lineAB As AcadLine, lineAC As AcadLine, lineBC As AcadLine
Dim circD As AcadCircle, circE As AcadCircle
Dim ptC(0 To 2) As Double

Dim r As Double
Dim intpts As Variant

Set lineAB = acadDoc.ModelSpace.AddLine(ptA, ptB)

r = distance(ptA, ptB)
Set circD = acadDoc.ModelSpace.AddCircle(ptA, r)

r = distance(ptB, ptA)
Set circE = acadDoc.ModelSpace.AddCircle(ptB, r)

intpts = circD.IntersectWith(circE, acExtendNone)
Call intpts_eval(intpts)

'going to take positive y value
'keeps traditional illustrations upright
    If ptG1(1) > ptG2(1) Then
ptC(0) = ptG1(0)
ptC(1) = ptG1(1)
ptC(2) = ptG1(2)
    Else
ptC(0) = ptG2(0)
ptC(1) = ptG2(1)
ptC(2) = ptG2(2)
    End If

Set lineAC = acadDoc.ModelSpace.AddLine(ptA, ptC)
Set lineBC = acadDoc.ModelSpace.AddLine(ptB, ptC)

End Sub



helper functions


Option Explicit

Public num_int_pts As Integer

Public ptG1(0 To 2) As Double
Public ptG2(0 To 2) As Double

'to evaluate the output array of the Intersectwith method
'loads up to two points in a global variable
'the calling program has to decide which one to use
'the core loop here taken directly out of autocad vba help for Intersectwith method
Sub intpts_eval(intpts As Variant)
 Dim i As Integer, j As Integer, k As Integer
    Dim str As String

If VarType(intpts) <> vbEmpty Then
        For i = LBound(intpts) To UBound(intpts)
            str = "Intersection Point[" & k & "] is: " & intpts(j) & "," & intpts(j + 1) & "," & intpts(j + 2)
            Debug.Print str
            str = ""
            i = i + 2
            j = j + 3
            k = k + 1
        Next
    End If
    
    Debug.Print LBound(intpts)
    Debug.Print UBound(intpts)
    
  'global var
  num_int_pts = k
  
  Select Case k
  Case Is = 0
  ptG1(0) = 0: ptG1(1) = 0: ptG1(2) = 0
  ptG2(0) = 0: ptG2(1) = 0: ptG2(2) = 0
  
  Case Is = 1
  Call pt(ptG1, (intpts(0)), (intpts(1)), (intpts(2)))
  ptG2(0) = 0: ptG2(1) = 0: ptG2(2) = 0
    
  Case Is = 2
  Call pt(ptG1, (intpts(0)), (intpts(1)), (intpts(2)))
  Call pt(ptG2, (intpts(3)), (intpts(4)), (intpts(5)))
 
  Case Is > 2
    MsgBox "thats a lot of points"
 End Select
End Sub


Sub pt(ByRef ptn() As Double, x As Double, y As Double, z As Double)
ptn(0) = x: ptn(1) = y: ptn(2) = z
End Sub

Function rnddbl(upr As Double, lwr As Double) As Double
Randomize
rnddbl = CDbl((upr - lwr + 1) * Rnd + lwr)
End Function

' straight out of autocad vba help
' Calculate distance between two points
Function distance(sp As Variant, ep As Variant) As Double
  Dim x As Double
  Dim y As Double
  Dim z As Double
  x = sp(0) - ep(0)
  y = sp(1) - ep(1)
  z = sp(2) - ep(2)

  distance = Sqr((Sqr((x ^ 2) + (y ^ 2)) ^ 2) + (z ^ 2))
End Function