Looking for a Python example of VB's redim preserve

I’ve been working on this for a while and I’m kind of stuck. I realize that in Python all lists are dynamic, but I can’t seem to make that knowledge work for me. Here is what I’m trying to do:

My script does a bunch of stuff to come up with a list of X, Y, Z coordinates like this:

[‘155.309’, ‘103.433’, ‘98.602’]

More calculations happen, and I get the next point:

[‘152.315’, ‘104.100’, ‘98.602’]

Now what I want to do is create a polyline between these points and however many come after it. In VBscript I just used the redim preserve functionality to make this work. In Python I’ve tried appending the list, but it’s not working for me.

I welcome any suggestions!

Thanks,

Dan

How about this:

a = [10, 20, 30]
print a
a.append(40)
print a

Hi Dan,

Either what you’re looking for is list.extend() which makes one big list…

list1=[a,b,c]
list2=[d,e,f]

list1.append(list2)
>>> [a,b,c,[d,e,f]]  #not what you want

list1.extend(list2)
>>> [a,b,c,d,e,f]

OR, start with an empty list and start appending your point lists one by one…

mylist=[]  #empty list
mylist.append(list1)
>>>[[a,b,c]]
mylist.append(list2)
>>>[[a,b,c],[d,e,f]]

You also might want to look into using Rhino Point3d objects rather than lists of coordinates…

–Mitch

And…

list0 = ['155.309', '103.433', '98.602']
list1 = ['152.315', '104.100', '98.602']
for str in list1:
    list0.append(str)
print list0

or

list0 = ['155.309', '103.433', '98.602']
list1 = ['152.315', '104.100', '98.602']
list0.append(list1)
print list0

Thanks for the quick responses.

What makes this tricky (for me) is that I don’t have 2 lists at the same time other than the very first iteration. I have a start point, then I calculate the next point. When the third point is calculated, it replaces the second, and so on. That’s where the Redim Preserve came in handy for me.

Maybe I need to find another way to come up with these points so that I can use append or extend?

FYI, it works great if I add points instead of a polyline. The AddPoint method only needs one 3D point at a time.

Do you have a small sample of what you are trying to do? Maybe that can help us point you in the right direction.

Yes, I will attach my project here. In the zip file you will find the Rhinoscript version as well, along with two G-code samples produced in madCAM. You can use these samples to test the results.

The purpose of this script is to read G-code into Rhino. All CAM programs show you where the tool is supposed to go, assuming you have a good post-processor. However, if the machine operator edits the G-code manually, there is no way to graphically see that change in your CAM software. This script allows you to “backplot” the G-code so that you can see the changes.

I’ve dumbed it down to a single machine just for clarity. I’m 90% of the way there, I’m just stuck on the actual production of the polylines.

Thanks in advance to anyone who takes the time to offer suggestions.

Dan

Backplotter.zip (98.4 KB)

Hi Dan,

You can have a look at a couple of additions I made to the code - hope it’s readable… At the beginning of your string search I added an empty list (pointList) to store the future 3D points.

In the loop where it finds X,Y, and Z, I converted those to Rhino 3DPoint objects (you need to import Rhino at the top). Note this step is not strictly necessary , as rs.AddPolyline is set up to take either coordinate lists or 3DPoint lists. After that each point or coordinate is appended to the empty list.

In the end, with your 1000.cnc file, there were 6440 points in pointList, I fed those to rs.AddPolyline… Bingo…

Let me know if this is clear enough…

–Mitch

(with 3DPoints)
CNCBackplotForDan.py (6.8 KB)

(with point coordinates)
CNCBackplotForDan2.py (6.8 KB)

Thanks Mitch! I really appreciate your help.

Dan

Hi Mitch,

It seems like I have a problem with the Z values. If you test 1001.cnc it ends up flat. Compare it to the Rhinoscript method and you will see a dramatic difference. I’ll have to investigate that a bit further.

Dan

What I did was to put a couple of print statements in there and then run it.

arrXYZ = [X,Y,Z]
if arrXYZ: pointList.append(arrXYZ)
    print strLine
    print Z

What that shows me in comparison to the original G-Code file is that some lines - the ones with the lower Z’s - are not making it down into the part of the loop that extracts the X, Y and Z and appends it to the list, so there is something wrong with the detection method perhaps… Didn’t have time to test more.

I also see an offset of one line between the G-Code Z values and what print Z actually printed… don’t know what’s gong on there…

N6653 X349.165Y89.147Z76.303
76.286
N6654 X349.285Y89.278Z76.317
76.303
N6655 X349.387Y89.429Z76.328
76.317
N6656 X349.470Y89.597Z76.338
76.328
N6657 X349.534Y89.779Z76.346
76.338
N6658 X349.581Y89.970Z76.351
76.346
N6659 X349.608Y90.168Z76.354
76.351
N6660 X349.617Y90.370Z76.355
76.354

–Mitch

I suspected there might be a problem further upstream. I’ll keep looking into.

This is a very good educational experience. By the time I’m done re-writing all my scripts into Python, I should have a pretty good knowledge of it! (I hope).

Thanks Mitch,

Dan

It looks like the issue is with only creating polylines when there are all three coordinates on a line of G-code. This is the problem:

if X != None and Y != None and Z != None:

Some of the lines only have one or two values:

N11 X9.080Z92.687
N12 X9.906Z92.421
N13 X10.731Z92.031
N14 X11.144Z91.781
N15 X11.557Z91.489
N16 X11.969Z91.147
N17 X12.382Z90.745
N18 X12.795Z90.264
N19 X13.208Z89.691
N20 X14.033Z88.514
N21 X14.858Z87.370
N22 X15.684Z86.264
N23 X16.509Z85.203
N24 X17.335Z84.164
N25 X18.986Z82.201
N26 X20.637Z80.360

In these cases, which in the 1001.cnc example are most of the lines, they are being skipped.

What I need to figure out (if possible) is how to rebuild each list to contain an x, y, and z value even if they are not present in each line. In the Rhinoscript version, the above code creates an array which contains the previous values if absent on the newest line:

9.080,0.005,92.687
9.906,0.005,92.421
10.731,0.005,92.031
11.144,0.005,91.781
11.557,0.005,91.489
11.969,0.005,91.147
12.382,0.005,90.745
12.795,0.005,90.264
13.208,0.005,89.691
14.033,0.005,88.514
14.858,0.005,87.370
15.684,0.005,86.264
16.509,0.005,85.203
17.335,0.005,84.164
18.986,0.005,82.201
20.637,0.005,80.360

I think if I can get this part figured out I will have this solved. I’m not even sure if that is realistic with Python, but, from what I’ve seen so far, there is probably a way to do it.

Dan

Hey Dan,

Yeah, that is what I figured… It’s not all that easy to account for all possible varieties of G-code… Out of curiosity, since I know G-Code and all the little traps that can happen with it, I tried to write my own extractor along the following lines:

  • ignore comments “()”
  • need to find all the letter codes in a line and split into chunks at those locations
  • parse the list of chunks to find the letter codes we want
  • we know that lines can contain multiple G addresses G40G90G00…
  • codes can be written G00 or G0, means the same
  • plan for future extraction of G02 and G03 if you really want to draw circles
  • could also distinguish G00 and G01 by color if desired (future)
  • codes are modal, so need to keep track of previous G, X, Y, Z values
  • return current G movement code plus X, Y, Z values if present

I ended up using regular expressions anyway as a way to separate the line into chunks. I had to look all that up on various sites as I’ve never really used re, so it was also a learning experience for me…

Below is a sample, for the moment it only does lines for G0/G1, the idea is that you could add sections to draw circles (for that you would need to extract I,J,K for the centers as well as X,Y,Z) at some point if you want. If not, the code could be simplified into creating just a point list and then a polyline instead of adding individual line segments…

Anyway, FWIW…

–Mitch

import rhinoscriptsyntax as rs
import Rhino, re
import scriptcontext as sc

def GCodeMovementExtractor(codeLine):
    """attempts to extract the movement code (G0, G1, G2, G3) 
    as well as the X, Y, Z coordinates from a line of G-Code. 
    Returns a numerical movement code (integer) if present 
    as well as X, Y, Z coordinates (floats) in a list.
    Can be extended to find circle center coordinates (I, J, K)"""
    
    iterLine=re.finditer('[A-Z]',codeLine)
    indices=[n.start() for n in iterLine]
    #list of all indices where a letter occurs
    #print indices #test
    codeBlocks=[]
    for i in range(len(indices)-1):
        tempSlice=codeLine[indices[i]:indices[i+1]]
        codeBlocks.append(tempSlice.strip())
    if len(indices)>1:
        #gets last slice
        tempSlice=codeLine[indices[-1]:]
        codeBlocks.append(tempSlice.strip())
    #codeBlocks is a list of individual code blocks in the line
    #Now we can test for what we want...
    #print codeBlocks #test
    gc=None ; X=None ; Y=None; Z=None
    for cb in codeBlocks:
        #get movement G-Code prefix if possible
        if cb[0]=="G":
            a=int(cb[-1])
            if a>=0 and a<=3: gc=a
        #Get X, Y, Z coords
        elif cb[0]=="X": X=float(cb[1:])
        elif cb[0]=="Y": Y=float(cb[1:])
        elif cb[0]=="Z": Z=float(cb[1:])
    return [gc, X, Y, Z]
    
def TestGCodeExtraction():
    rs.EnableRedraw(False)
    segments=[]
    currG=0
    currX=None
    currY=None
    currZ=None
    prevPt=None
    #this is just a test file
    file=open("D:\\DropZone\\1001.txt",'r')
    for line in file:
        #ignore comments
        if "(" in line: continue
        #extract the current line
        exRes=GCodeMovementExtractor(line)
        if exRes[0] != None: currG=exRes[0]
        if exRes[1] != None: currX=exRes[1]
        if exRes[2] != None: currY=exRes[2]
        if exRes[3] != None: currZ=exRes[3]
        if currX !=None and currY !=None and currZ !=None:
            #valid point found
            currPt=Rhino.Geometry.Point3d(currX,currY,currZ)
            if prevPt==None:
                #currPt is start point
                prevPt=Rhino.Geometry.Point3d(currX,currY,currZ)
            else:
                try:
                    seg=rs.AddLine(prevPt,currPt)
                    if seg: segments.append(seg)
                except:
                    continue
            prevPt=currPt
    if len(segments)>1:
        rs.JoinCurves(segments,True)
    file.close()
    
TestGCodeExtraction()

Hi Mitch,

Sorry for the delayed response. I was on a road trip away from the office all day. First thing tomorrow morning I will take a good look at your version.

Thanks,

Dan

OK, no problem…

Attached is a pointlist/polyline creating version - it’s much faster if that’s all you want to do.

–Mitch

GCodeParser-Polyline.py (3.0 KB)

1 Like

Hi Mitch,

I will study that one too. I should learn a lot from both of these examples.

Thanks for your efforts,

Dan

Yeah, there’s some nice work there Mitch! I’ve hooked a few bells and whistles to it (file selection, layer control), and it’s working pretty well. I just need to wrap my head around it a little more to see if I can get the first and last polyline not to go to Z0. This would be deceptive to the user as he’s going to think his tool is plunging into the material.

I’m also going to see if I can adapt it to Heidenhain code. It might be as simple as looking for “L” instead of “G”.

0 BEGIN PGM 6000.h MM
; (This code is from madCAM for roughing on the Awea.)
1 CYCL DEF 247 DATUM SETTING~
Q339=0
2 CYCL DEF 32.0 TOLERANCE
3 CYCL DEF 32.1 T0.1
4 CYCL DEF 32.2 HSC-MODE:1
5 TOOL CALL 18 Z S2000 ;TOOL CHANGE (01_T18_Ø50.8r6_rough_bullnose_alum)
6 M3
7 L X342.499Y86.853FMAX
8 L Z128.250FMAX M8
9 L Z127.002FMAX
10 L Z101.602F2300
11 L X343.615Y85.143Z101.495F2500
12 L X344.991Y83.634Z101.388
13 L X346.591Y82.364Z101.281
14 L X348.374Y81.368Z101.174
15 L X350.293Y80.670Z101.067
16 L X352.299Y80.289Z100.959
17 L X354.341Y80.234Z100.852
18 L X356.364Y80.507Z100.745
19 L X358.318Y81.102Z100.638
20 L X360.152Y82.001Z100.531

and so on…

I’m impressed with how fast this script runs. Much faster than the VB based version.

Thanks again for you efforts,

Dan

Yeah I saw that, it’s due to the H0Z0 at the beginning and the end… You could probably just parse the raw line string (before you send it to be split into bits) for that particular chunk (maybe also with re but also just

if "H0Z0" in line : continue

to just skip the line - as you don’t find that string combo anywhere else… H0 is turning off tool length compensation, no? What does the Z0 do in this case? Is that like “go to machine Z reference” (usually near max machine Z) ? I usually use G53 for “machine coordinates”.

Heidenhain substitution shouldn’t be too hard.

Good luck,

–Mitch

The H0Z0 brings the machine to it’s home position from what I recall. It will be easy to skip that.

I think it’s smooth sailing from here. I just need to get time to play with this!

Thanks again,

Dan