1#!/usr/local/bin/python3.8
2"""
3    g_code_library
4    Copyright (C) <2017>  <Scorch>
5    This program is free software: you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation, either version 3 of the License, or
8    (at your option) any later version.
9
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program.  If not, see <http://www.gnu.org/licenses/>.
17"""
18import sys
19from math import *
20import os
21import re
22import binascii
23import getopt
24import webbrowser
25
26
27#################
28### START LIB ###
29#################
30############################################################################
31class G_Code_Rip:
32    def __init__(self):
33        self.Zero      = 0.0000001
34        self.g_code_data  = []
35        self.scaled_trans = []
36        self.right_side   = []
37        self.left_side    = []
38        self.probe_gcode  = []
39        self.probe_coords = []
40        self.arc_angle    = 2
41        self.accuracy     = .001
42        self.units        = "in"
43
44    ################################################################################
45    #             Function for outputting messages to different locations          #
46    #            depending on what options are enabled                             #
47    ################################################################################
48    def fmessage(self,text,newline=True):
49        if newline==True:
50            try:
51                sys.stdout.write(text)
52                sys.stdout.write("\n")
53            except:
54                pass
55        else:
56            try:
57                sys.stdout.write(text)
58            except:
59                pass
60
61    def Read_G_Code(self,filename, XYarc2line = False, arc_angle=2, units="in", Accuracy=""):
62        self.g_code_data  = []
63        self.scaled_trans = []
64        self.right_side   = []
65        self.left_side    = []
66        self.probe_gcode  = []
67        self.probe_coords = []
68        self.arc_angle    = arc_angle
69        self.units        = units
70        if Accuracy == "":
71            if units == "in":
72                self.accuracy = .001
73            else:
74                self.accuracy = .025
75        else:
76            self.accuracy = float(Accuracy)
77
78        READ_MSG = []
79
80        # Try to open file for reading
81        try:
82            fin = open(filename,'r')
83        except:
84            READ_MSG.append("Unable to open file: %s" %(filename))
85            return READ_MSG
86
87        scale = 1
88        variables = []
89        line_number = 0
90
91        xind=0
92        yind=1
93        zind=2
94
95        mode_arc  = "incremental" # "absolute"
96        mode_pos = "absolute"    # "incremental"
97
98        mvtype = 1  # G0 (Rapid), G1 (linear), G2 (clockwise arc) or G3 (counterclockwise arc).
99        plane  = "17" # G17 (Z-axis, XY-plane), G18 (Y-axis, XZ-plane), or G19 (X-axis, YZ-plane)
100        pos     =['','','']
101        pos_last=['','','']
102        POS     =[complex(0,1),complex(0,1),complex(0,1)]
103        feed = 0
104        spindle = 0
105
106        #########################
107        for line in fin:
108            line_number = line_number + 1
109            #print line_number
110            line = line.replace("\n","")
111            line = line.replace("\r","")
112            code_line=[]
113
114            #####################
115            ### FIND COMMENTS ###
116            #####################
117            if line.find("(") != -1:
118                s = line.find("(")
119                while s != -1:
120                    e = line.find(")")
121                    code_line.append([ ";", line[s:e+1] ])
122                    line = self.rm_text(line,s,e)
123                    s = line.find("(")
124
125            if line.find(";") != -1:
126                s = line.find(";")
127                e = len(line)
128                code_line.append([ ";", line[s:e] ])
129                line = self.rm_text(line,s,e)
130            # If comment exists write it to output
131            if code_line!= []:
132                for comment in code_line:
133                    self.g_code_data.append(comment)
134                code_line=[]
135
136            # Switch remaining non comment data to upper case
137            # and remove spaces
138            line = line.upper()
139            line = line.replace(" ","")
140
141
142            #####################################################
143            # Find # chars and check for a variable definition  #
144            #####################################################
145            if line.find("#") != -1:
146                s = line.rfind("#")
147                while s != -1:
148                    if line[s+1] == '<':
149                        e = s+2
150                        while line[e] != '>' and e <= len(line):
151                            e = e+1
152                        e = e+1
153                        vname = line[s:e].lower()
154                    else:
155                        vname = re.findall(r'[-+]?\d+',line[s:])[0]
156                        e = s + 1 + len(vname)
157                        vname = line[s:e]
158
159                    DEFINE = False
160                    if e < len(line):
161                        if line[e]=="=":
162                            DEFINE = True
163                    if DEFINE:
164                        try:
165                            vval = "%.4f" %(float(line[e+1:]))
166                            line = ''
167                        except:
168                            try:
169                                vval = self.EXPRESSION_EVAL(line[e+1:])
170                                line = ''
171                            except:
172                                READ_MSG.append(str(sys.exc_info()[1]))
173                                return READ_MSG
174
175                        variables.append([vname,vval])
176                        line  = self.rm_text(line,s,e-1)
177                    else:
178                        line = self.rm_text(line,s,e-1)
179                        VALUE = ''
180                        for V in variables:
181                            if V[0] == vname:
182                                VALUE = V[1]
183
184                        line = self.insert_text(line,VALUE,s)
185
186                    s = line.rfind("#")
187
188            #########################
189            ### FIND MATH REGIONS ###
190            #########################
191            if line.find("[") != -1 and line.find("[") != 0:
192                ############################
193                s = line.find("[")
194                while s != -1:
195                    e = s + 1
196                    val = 1
197                    while val > 0:
198                        if e >= len(line):
199                            MSG = "ERROR: Unable to evaluate expression: G-Code Line %d" %(line_number)
200                            raise ValueError(MSG)
201                        if line[e]=="[":
202                            val = val + 1
203                        elif line[e] == "]":
204                            val = val - 1
205                        e = e + 1
206
207                    new_val = self.EXPRESSION_EVAL(line[s:e])
208
209                    line = self.rm_text(line,s,e-1)
210                    line = self.insert_text(line,new_val,s)
211                    s = line.find("[")
212                #############################
213
214
215            ####################################
216            ### FIND FULLY UNSUPPORTED CODES ###
217            ####################################
218            # D Tool radius compensation number
219            # E ...
220            # L ...
221            # O ... Subroutines
222            # Q Feed increment in G73, G83 canned cycles
223            # A A axis of machine
224            # B B axis of machine
225            # C C axis of machine
226            # U U axis of machine
227            # V V axis of machine
228            # W W axis of machine
229
230            UCODES = ("A","B","C","D","E","L","O","Q","U","V","W")
231            skip = False
232            for code in UCODES:
233                if line.find(code) != -1:
234                    READ_MSG.append("Warning: %s Codes are not supported ( G-Code File Line: %d )" %(code,line_number))
235                    skip = True
236            if skip:
237                continue
238
239
240            ##############################
241            ###    FIND ALL CODES      ###
242            ##############################
243            # F Feed rate
244            # G General function
245            # I X offset for arcs and G87 canned cycles
246            # J Y offset for arcs and G87 canned cycles
247            # K Z offset for arcs and G87 canned cycles. Spindle-Motion Ratio for G33 synchronized movements.
248            # M Miscellaneous function (See table Modal Groups)
249            # P Dwell time in canned cycles and with G4. Key used with G10. Used with G2/G3.
250            # R Arc radius or canned cycle plane
251            # S Spindle speed
252            # T Tool selection
253            # X X axis of machine
254            # Y Y axis of machine
255            # Z Z axis of machine
256
257            ALL = ("A","B","C","D","E","F","G","H","I","J",\
258                   "K","L","M","N","O","P","Q","R","S","T",\
259                   "U","V","W","X","Y","Z","#","=")
260            temp = []
261            line = line.replace(" ","")
262            for code in ALL:
263                index=-1
264                index = line.find(code,index+1)
265                while index != -1:
266                    temp.append([code,index])
267                    index = line.find(code,index+1)
268            temp.sort(key=lambda a:a[1])
269
270            code_line=[]
271            if temp != []:
272                x = 0
273                while x <= len(temp)-1:
274                    s = temp[x][1]+1
275                    if x == len(temp)-1:
276                        e = len(line)
277                    else:
278                        e = temp[x+1][1]
279
280                    CODE  = temp[x][0]
281                    VALUE = line[s:e]
282                    code_line.append([ CODE, VALUE ])
283                    x = x + 1
284
285            #################################
286
287            mv_flag   = 0
288            POS_LAST = POS[:]
289            #CENTER  = ['','','']
290            CENTER   = POS_LAST[:]
291            passthru = ""
292            for com in code_line:
293                if com[0] == "G":
294                    Gnum = "%g" %(float(com[1]))
295                    if Gnum == "0" or Gnum == "1":
296                        mvtype = int(Gnum)
297                    elif Gnum == "2" or Gnum == "3":
298                        mvtype = int(Gnum)
299                        #CENTER = POS_LAST[:]
300                    elif Gnum == "17":
301                        plane = Gnum
302                    elif Gnum == "18":
303                        plane = Gnum
304                    elif Gnum == "19":
305                        plane = Gnum
306                    elif Gnum == "20":
307                        if units == "in":
308                            scale = 1
309                        else:
310                            scale = 25.4
311                    elif Gnum == "21":
312                        if units == "mm":
313                            scale = 1
314                        else:
315                            scale = 1.0/25.4
316                    elif Gnum == "81":
317                        READ_MSG.append("Warning: G%s Codes are not supported ( G-Code File Line: %d )" %(Gnum,line_number))
318                    elif Gnum == "90.1":
319                        mode_arc = "absolute"
320
321                    elif Gnum == "90":
322                        mode_pos = "absolute"
323
324                    elif Gnum == "91":
325                        mode_pos = "incremental"
326
327                    elif Gnum == "91.1":
328                        mode_arc = "incremental"
329
330                    elif Gnum == "92":
331                        #READ_MSG.append("Aborting G-Code Reading: G%s Codes are not supported" %(Gnum))
332                        READ_MSG.append("Warning: G%s Codes are not supported ( G-Code File Line: %d )" %(Gnum,line_number))
333                        #return READ_MSG
334
335                    elif Gnum == "38.2":
336                        READ_MSG.append("Warning: G%s Codes are not supported ( G-Code File Line: %d )" %(Gnum,line_number))
337                        #READ_MSG.append("Aborting G-Code Reading: G%s Codes are not supported" %(Gnum))
338                        #return READ_MSG
339
340                    else:
341                        passthru = passthru +  "%s%s " %(com[0],com[1])
342
343                elif com[0] == "X":
344                    if mode_pos == "absolute":
345                        POS[xind] = float(com[1])*scale
346                    else:
347                        POS[xind] = float(com[1])*scale + POS_LAST[xind]
348                    mv_flag = 1
349
350                elif com[0] == "Y":
351                    if mode_pos == "absolute":
352                        POS[yind] = float(com[1])*scale
353                    else:
354                        POS[yind] = float(com[1])*scale + POS_LAST[yind]
355                    mv_flag = 1
356
357                elif com[0] == "Z":
358                    if mode_pos == "absolute":
359                        POS[zind] = float(com[1])*scale
360                    else:
361                        POS[zind] = float(com[1])*scale + POS_LAST[zind]
362                    mv_flag = 1
363
364                ###################
365                elif com[0] == "I":
366                    if mode_arc == "absolute":
367                        CENTER[xind] = float(com[1])*scale
368                    else:
369                        CENTER[xind] = float(com[1])*scale + POS_LAST[xind]
370                    if (mvtype==2 or mvtype==3):
371                        mv_flag = 1
372
373                elif com[0] == "J":
374                    if mode_arc == "absolute":
375                        CENTER[yind] = float(com[1])*scale
376                    else:
377                        CENTER[yind] = float(com[1])*scale + POS_LAST[yind]
378                    if (mvtype==2 or mvtype==3):
379                        mv_flag = 1
380                elif com[0] == "K":
381                    if mode_arc == "absolute":
382                        CENTER[zind] = float(com[1])*scale
383                    else:
384                        CENTER[zind] = float(com[1])*scale + POS_LAST[zind]
385                    if (mvtype==2 or mvtype==3):
386                        mv_flag = 1
387
388                elif com[0] == "R":
389                    Rin= float(com[1])*scale
390                    CENTER = self.get_center(POS,POS_LAST,Rin,mvtype,plane)
391
392                ###################
393                elif com[0] == "F":
394                    feed = float(com[1]) * scale
395
396                elif com[0] == "S":
397                    spindle = float(com[1])
398
399                elif com[0] == ";":
400                    passthru = passthru + "%s " %(com[1])
401
402                elif com[0] == "P" and mv_flag == 1 and mvtype > 1:
403                    READ_MSG.append("Aborting G-Code Reading: P word specifying the number of full or partial turns of arc are not supported")
404                    return READ_MSG
405
406                elif com[0] == "M":
407                    Mnum = "%g" %(float(com[1]))
408                    if Mnum == "2":
409                        self.g_code_data.append([ "M2", "(END PROGRAM)" ])
410                    passthru = passthru + "%s%s " %(com[0],com[1])
411
412                elif com[0] == "N":
413                    pass
414                    #print "Ignoring Line Number %g" %(float(com[1]))
415
416                else:
417                    passthru = passthru + "%s%s " %(com[0],com[1])
418
419            pos      = POS[:]
420            pos_last = POS_LAST[:]
421            center = CENTER[:]
422
423            # Most command on a line are executed prior to a move so
424            # we will write the passthru commands on the line before we found them
425            # only "M0, M1, M2, M30 and M60" are executed after the move commands
426            # there is a risk that one of these commands could stop the program before
427            # the move is completed
428
429            if passthru != '':
430                self.g_code_data.append("%s" %(passthru))
431
432
433            ###############################################################################
434            if mv_flag == 1:
435                if mvtype == 0:
436                    self.g_code_data.append([mvtype,pos_last[:],pos[:]])
437                if mvtype == 1:
438                    self.g_code_data.append([mvtype,pos_last[:],pos[:],feed,spindle])
439                if mvtype == 2 or mvtype == 3:
440                    if plane == "17":
441                        if XYarc2line == False:
442                            self.g_code_data.append([mvtype,pos_last[:],pos[:],center[:],feed,spindle])
443                        else:
444                            data = self.arc2lines(pos_last[:],pos[:],center[:], mvtype, plane)
445
446                            for line in data:
447                                XY=line
448                                self.g_code_data.append([1,XY[:3],XY[3:],feed,spindle])
449
450                    elif plane == "18":
451                        data = self.arc2lines(pos_last[:],pos[:],center[:], mvtype, plane)
452                        for line in data:
453                            XY=line
454                            self.g_code_data.append([1,XY[:3],XY[3:],feed,spindle])
455
456                    elif plane == "19":
457                        data = self.arc2lines(pos_last[:],pos[:],center[:], mvtype, plane)
458                        for line in data:
459                            XY=line
460                            self.g_code_data.append([1,XY[:3],XY[3:],feed,spindle])
461            ###############################################################################
462            #################################
463        fin.close()
464
465        ## Post process the g-code data to remove complex numbers
466        cnt = 0
467        firstx = complex(0,1)
468        firsty = complex(0,1)
469        firstz = complex(0,1)
470        first_sum = firstx + firsty + firstz
471        while ((cnt < len(self.g_code_data)) and (isinstance(first_sum, complex))):
472            line = self.g_code_data[cnt]
473            if line[0] == 0 or line[0] == 1 or line[0] == 2 or line[0] == 3:
474                if (isinstance(firstx, complex)): firstx = line[2][0]
475                if (isinstance(firsty, complex)): firsty = line[2][1]
476                if (isinstance(firstz, complex)): firstz = line[2][2]
477            cnt=cnt+1
478            first_sum = firstx + firsty + firstz
479        max_cnt = cnt
480        cnt = 0
481        ambiguousX = False
482        ambiguousY = False
483        ambiguousZ = False
484        while (cnt < max_cnt):
485            line = self.g_code_data[cnt]
486            if line[0] == 1 or line[0] == 2 or line[0] == 3:
487                # X Values
488                if (isinstance(line[1][0], complex)):
489                    line[1][0] = firstx
490                    ambiguousX = True
491                if (isinstance(line[2][0], complex)):
492                    line[2][0] = firstx
493                    ambiguousX = True
494                # Y values
495                if (isinstance(line[1][1], complex)):
496                    line[1][1] = firsty
497                    ambiguousY = True
498                if (isinstance(line[2][1], complex)):
499                    line[2][1] = firsty
500                    ambiguousY = True
501                # Z values
502                if (isinstance(line[1][2], complex)):
503                    line[1][2] = firstz
504                    ambiguousZ = True
505                if (isinstance(line[2][2], complex)):
506                    line[2][2] = firstz
507                    ambiguousZ = True
508            cnt=cnt+1
509        #if (ambiguousX or ambiguousY or ambiguousZ):
510        #    MSG = "Ambiguous G-Code start location:\n"
511        #    if (ambiguousX):  MSG = MSG + "X position is not set by a G0(rapid) move prior to a G1,G2 or G3 move.\n"
512        #    if (ambiguousY):  MSG = MSG + "Y position is not set by a G0(rapid) move prior to a G1,G2 or G3 move.\n"
513        #    if (ambiguousZ):  MSG = MSG + "Z position is not set by a G0(rapid) move prior to a G1,G2 or G3 move.\n"
514        #    MSG = MSG + "!! Review output files carefully !!"
515        #    READ_MSG.append(MSG)
516
517        return READ_MSG
518
519    def get_center(self,POS,POS_LAST,Rin,mvtype,plane="17"):
520        if   plane == "18":
521            xind=2
522            yind=0
523            zind=1
524        elif plane == "19":
525            xind=1
526            yind=2
527            zind=0
528        elif plane == "17":
529            xind=0
530            yind=1
531            zind=2
532
533        CENTER=["","",""]
534        cord = sqrt( (POS[xind]-POS_LAST[xind])**2 + (POS[yind]-POS_LAST[yind])**2 )
535        v1 = cord/2.0
536
537        #print "rin=%f v1=%f (Rin**2 - v1**2)=%f" %(Rin,v1,(Rin**2 - v1**2))
538        v2_sq = Rin**2 - v1**2
539        if v2_sq<0.0:
540            v2_sq = 0.0
541        v2 = sqrt( v2_sq )
542
543        theta = self.Get_Angle2(POS[xind]-POS_LAST[xind],POS[yind]-POS_LAST[yind])
544
545        if mvtype == 3:
546            dxc,dyc = self.Transform(-v2,v1,radians(theta-90))
547        elif mvtype == 2:
548            dxc,dyc = self.Transform(v2,v1,radians(theta-90))
549        else:
550            return "Center Error"
551
552        xcenter = POS_LAST[xind] + dxc
553        ycenter = POS_LAST[yind] + dyc
554
555        CENTER[xind] = xcenter
556        CENTER[yind] = ycenter
557        CENTER[zind] = POS_LAST[zind]
558
559        return CENTER
560
561    #######################################
562    def split_code(self,code2split,shift=[0,0,0],angle=0.0):
563        xsplit=0.0
564        mvtype = -1  # G0 (Rapid), G1 (linear), G2 (clockwise arc) or G3 (counterclockwise arc).
565
566        passthru = ""
567        POS     =[0,0,0]
568        feed = 0
569        spindle = 0
570        self.right_side = []
571        self.left_side  = []
572
573        L = 0
574        R = 1
575        for line in code2split:
576            if line[0] == 1:
577                mvtype   = line[0]
578                POS_LAST = line[1][:]
579                POS      = line[2][:]
580                CENTER   = ['','','']
581                feed     = line[3]
582                spindle  = line[4]
583
584            elif line[0] == 3 or line[0] == 2:
585                mvtype   = line[0]
586                POS_LAST = line[1][:]
587                POS      = line[2][:]
588                CENTER   = line[3][:]
589                feed     = line[4]
590                spindle  = line[5]
591
592            else:
593                mvtype  = -1
594                passthru = line
595
596            ###############################################################################
597            if mvtype >= 1 and mvtype <= 3:
598                pos      = self.coordop(POS,shift,angle)
599                pos_last = self.coordop(POS_LAST,shift,angle)
600
601                if CENTER[0]!='' and CENTER[1]!='':
602                    center = self.coordop(CENTER,shift,angle)
603                else:
604                    center = CENTER
605
606                this=""
607                other=""
608
609                if pos_last[0] > xsplit+self.Zero:
610                    flag_side = R
611                elif pos_last[0] < xsplit-self.Zero:
612                    flag_side = L
613                else:
614                    if mvtype == 1:
615                        if pos[0] >= xsplit:
616                            flag_side = R
617                        else:
618                            flag_side = L
619
620                    elif mvtype == 2:
621
622                        if abs(pos_last[1]-center[1]) < self.Zero:
623                            if center[0] > xsplit:
624                                flag_side = R
625                            else:
626                                flag_side = L
627                        else:
628                            if   pos_last[1] >= center[1]:
629                                flag_side = R
630                            else:
631                                flag_side = L
632
633                    else: #(mvtype == 3)
634                        if abs(pos_last[1]-center[1]) < self.Zero:
635                            if center[0] > xsplit:
636                                flag_side = R
637                            else:
638                                flag_side = L
639                        else:
640                            if   pos_last[1] >= center[1]:
641                                flag_side = L
642                            else:
643                                flag_side = R
644
645                if flag_side == R:
646                    this  = 1
647                    other = 0
648                else:
649                    this  = 0
650                    other = 1
651
652                app=[self.apright, self.apleft]
653
654                #############################
655                if mvtype == 0:
656                    pass
657
658                if mvtype == 1:
659                    A  = self.coordunop(pos_last[:],shift,angle)
660                    C  = self.coordunop(pos[:]     ,shift,angle)
661                    cross = self.get_line_intersect(pos_last, pos, xsplit)
662
663                    if len(cross) > 0: ### Line crosses boundary ###
664                        B  = self.coordunop(cross[0]   ,shift,angle)
665                        app[this] ( [mvtype,A,B,feed,spindle] )
666                        app[other]( [mvtype,B,C,feed,spindle] )
667                    else:
668                        app[this] ( [mvtype,A,C,feed,spindle] )
669
670                if mvtype == 2 or mvtype == 3:
671                    A  = self.coordunop(pos_last[:],shift,angle)
672                    C  = self.coordunop(pos[:]     ,shift,angle)
673                    D  = self.coordunop(center     ,shift,angle)
674                    cross = self.get_arc_intersects(pos_last[:], pos[:], xsplit, center[:], "G%d" %(mvtype))
675
676                    if len(cross) > 0: ### Arc crosses boundary at least once ###
677                        B  = self.coordunop(cross[0]   ,shift,angle)
678                        #Check length of arc before writing
679                        if sqrt((A[0]-B[0])**2 + (A[1]-B[1])**2) > self.accuracy:
680                            app[this]( [mvtype,A,B,D,feed,spindle])
681
682                        if len(cross) == 1: ### Arc crosses boundary only once ###
683                            #Check length of arc before writing
684                            if sqrt((B[0]-C[0])**2 + (B[1]-C[1])**2) > self.accuracy:
685                                app[other]([ mvtype,B,C,D, feed,spindle] )
686                        if len(cross) == 2: ### Arc crosses boundary twice ###
687                            E  = self.coordunop(cross[1],shift,angle)
688                            #Check length of arc before writing
689                            if sqrt((B[0]-E[0])**2 + (B[1]-E[1])**2) > self.accuracy:
690                                app[other]([ mvtype,B,E,D, feed,spindle] )
691                            #Check length of arc before writing
692                            if sqrt((E[0]-C[0])**2 + (E[1]-C[1])**2) > self.accuracy:
693                                app[this] ([ mvtype,E,C,D, feed,spindle] )
694                    else: ### Arc does not cross boundary ###
695                        app[this]([ mvtype,A,C,D, feed,spindle])
696
697            ###############################################################################
698            else:
699                if passthru != '':
700                    self.apboth(passthru)
701
702    #######################################
703    def probe_code(self,code2probe,nX,nY,probe_istep,minx,miny,xPartitionLength,yPartitionLength): #,Xoffset,Yoffset):
704    #def probe_code(self,code2probe,nX,nY,probe_istep,minx,miny,xPartitionLength,yPartitionLength,Xoffset,Yoffset,Zoffset):
705        #print "nX,nY =",nX,nY
706        probe_coords = []
707        BPN=500
708        POINT_LIST = [False for i in range(int((nY)*(nX)))]
709
710        if code2probe == []:
711            return
712
713        mvtype = -1  # G0 (Rapid), G1 (linear), G2 (clockwise arc) or G3 (counterclockwise arc).
714        passthru = ""
715        POS      = [0,0,0]
716        feed     = 0
717        spindle  = 0
718        out      = []
719
720        min_length = min(xPartitionLength,yPartitionLength) / probe_istep
721        if (min_length < self.Zero):
722            min_length = max(xPartitionLength,yPartitionLength) / probe_istep
723        if (min_length < self.Zero):
724            min_length = 1
725
726        for line in code2probe:
727            if line[0] == 0  or line[0] == 1:
728                mvtype   = line[0]
729                POS_LAST = line[1][:]
730                POS      = line[2][:]
731                CENTER   = ['','','']
732                if line[0] == 1:
733                    feed     = line[3]
734                    spindle  = line[4]
735
736            elif line[0] == 3 or line[0] == 2:
737                mvtype   = line[0]
738                POS_LAST = line[1][:]
739                POS      = line[2][:]
740                CENTER   = line[3][:]
741                feed     = line[4]
742                spindle  = line[5]
743            else:
744                mvtype  = -1
745                passthru = line
746
747            ###############################################################################
748            if mvtype >= 0 and mvtype <=3:
749                pos = POS[:]
750                pos_last = POS_LAST[:]
751                center = CENTER[:]
752
753                #############################
754                if mvtype == 0:
755                    out.append( [mvtype,pos_last,pos] )
756
757                if mvtype == 1:
758                    dx = pos[0]-pos_last[0]
759                    dy = pos[1]-pos_last[1]
760                    dz = pos[2]-pos_last[2]
761                    length = sqrt(dx*dx + dy*dy)
762                    if (length <= min_length):
763                        out.append( [mvtype,pos_last,pos,feed,spindle] )
764                    else:
765                        Lsteps = max(2,int(ceil(length / min_length)))
766                        xstp0 = float(pos_last[0])
767                        ystp0 = float(pos_last[1])
768                        zstp0 = float(pos_last[2])
769                        for n in range(1,Lsteps+1):
770                            xstp1 = n/float(Lsteps)*dx + pos_last[0]
771                            ystp1 = n/float(Lsteps)*dy + pos_last[1]
772                            zstp1 = n/float(Lsteps)*dz + pos_last[2]
773                            out.append( [mvtype,[xstp0,ystp0,zstp0],[xstp1,ystp1,zstp1],feed,spindle] )
774                            xstp0 = float(xstp1)
775                            ystp0 = float(ystp1)
776                            zstp0 = float(zstp1)
777
778                if mvtype == 2 or mvtype == 3:
779                    out.append( [ mvtype,pos_last,pos,center, feed,spindle] )
780            ###############################################################################
781            else:
782                if passthru != '':
783                    out.append(passthru)
784
785        ################################
786        ##  Loop through output to    ##
787        ##  find needed probe points  ##
788        ################################
789        for i in range(len(out)):
790            line = out[i]
791            if line[0] == 0  or line[0] == 1:
792                mvtype   = line[0]
793                POS_LAST = line[1][:]
794                POS      = line[2][:]
795                CENTER   = ['','','']
796                if line[0] == 1:
797                    feed = line[3]
798                    spindle = line[4]
799
800            elif line[0] == 3 or line[0] == 2:
801                mvtype   = line[0]
802                POS_LAST = line[1][:]
803                POS      = line[2][:]
804                CENTER   = line[3][:]
805                feed     = line[4]
806                spindle  = line[5]
807            else:
808                mvtype  = -1
809                passthru = line
810
811            if mvtype >= 1 and mvtype <=3:
812                pos = POS[:]
813                pos_last = POS_LAST[:]
814                center = CENTER[:]
815
816
817                #### ADD ADDITIONAL DATA TO POS_LAST DATA ####
818                i_x,i_y = self.get_ix_iy((pos_last[0]-minx),(pos_last[1]-miny),xPartitionLength,yPartitionLength)
819                #i_x = i_x+Xoffset
820                #i_y = i_y+Yoffset
821                if i_x < 0:
822                    i_x=0
823                if i_y < 0:
824                    i_y=0
825                if (i_x+1 >= nX):
826                    i_x = nX-2
827                    #i_x = i_x-1 #commented 02/22
828                    #print "adjust i_x POS_LAST"
829                i_x2 = i_x+1
830                if (i_y+1 >= nY):
831                    i_y = nY-2
832                    #i_y = i_y-1  #commented 02/22
833                    #print "adjust i_y POS_LAST"
834                i_y2 = i_y+1
835
836                p_index_A =  int(i_y* nX + i_x )
837                p_index_B =  int(i_y2*nX + i_x )
838                p_index_C =  int(i_y *nX + i_x2)
839                p_index_D =  int(i_y2*nX + i_x2)
840
841                Xfraction=((pos_last[0]-minx)-(i_x*xPartitionLength))/xPartitionLength
842                Yfraction=((pos_last[1]-miny)-(i_y*yPartitionLength))/yPartitionLength
843
844                if Xfraction>1.0:
845                    #print "ERROR POS_LAST: Xfraction = ", Xfraction
846                    Xfraction = 1.0
847                if Xfraction <0.0:
848                    #print "ERROR POS_LAST: Xfraction = ", Xfraction
849                    Xfraction = 0.0
850                if Yfraction > 1.0:
851                    #print "ERROR POS_LAST: Yfraction = ", Yfraction
852                    Yfraction = 1.0
853                if Yfraction<0.0:
854                    #print "ERROR POS_LAST: Yfraction = ", Yfraction
855                    Yfraction = 0.0
856
857                BPN=500
858                out[i][1].append(p_index_A+BPN)
859                out[i][1].append(p_index_B+BPN)
860                out[i][1].append(p_index_C+BPN)
861                out[i][1].append(p_index_D+BPN)
862                out[i][1].append(Xfraction)
863                out[i][1].append(Yfraction)
864
865                try:
866                    POINT_LIST[p_index_A ] = True
867                    POINT_LIST[p_index_B ] = True
868                    POINT_LIST[p_index_C ] = True
869                    POINT_LIST[p_index_D ] = True
870                except:
871                    pass
872                #### ADD ADDITIONAL DATA TO POS_LAST DATA ####
873                i_x,i_y = self.get_ix_iy((pos[0]-minx),(pos[1]-miny),xPartitionLength,yPartitionLength)
874                #i_x = i_x+Xoffset
875                #i_y = i_y+Yoffset
876                if i_x < 0:
877                    i_x=0
878                if i_y < 0:
879                    i_y=0
880                if (i_x+1 >= nX):
881                    i_x = nX-2
882                    #i_x = i_x-1 #commented 02/22
883                    #print "adjust i_x POS"
884                i_x2 = i_x+1
885                if (i_y+1 >= nY):
886                    i_y = nY-2
887                    #i_y = i_y-1#commented 02/22
888                    #print "adjust i_y POS"
889                i_y2 = i_y+1
890
891                p_index_A =  int(i_y* nX + i_x )
892                p_index_B =  int(i_y2*nX + i_x )
893                p_index_C =  int(i_y *nX + i_x2)
894                p_index_D =  int(i_y2*nX + i_x2)
895                Xfraction=((pos[0]-minx)-(i_x*xPartitionLength))/xPartitionLength
896                Yfraction=((pos[1]-miny)-(i_y*yPartitionLength))/yPartitionLength
897
898                if Xfraction>1.0:
899                    Xfraction = 1.0
900                    #print "ERROR POS: Xfraction = ", Xfraction
901                if Xfraction <0.0:
902                    Xfraction = 0.0
903                    #print "ERROR POS: Xfraction = ", Xfraction
904                if Yfraction > 1.0:
905                    Yfraction = 1.0
906                    #print "ERROR POS: Yfraction = ", Yfraction
907                if Yfraction<0.0:
908                    Yfraction = 0.0
909                    #print "ERROR POS: Yfraction = ", Yfraction
910
911                out[i][2].append(p_index_A+BPN)
912                out[i][2].append(p_index_B+BPN)
913                out[i][2].append(p_index_C+BPN)
914                out[i][2].append(p_index_D+BPN)
915                out[i][2].append(Xfraction)
916                out[i][2].append(Yfraction)
917                try:
918                    POINT_LIST[p_index_A ] = True
919                    POINT_LIST[p_index_B ] = True
920                    POINT_LIST[p_index_C ] = True
921                    POINT_LIST[p_index_D ] = True
922                except:
923                    pass
924        self.probe_gcode = out
925        #for line in out:
926        #    print line
927
928        ################################
929        ##  Generate Probing Code     ##
930        ##  For needed points         ##
931        ################################
932
933        for i in range(len(POINT_LIST)):
934            i_x = i % nX
935            i_y = int(i / nX)
936            xp  = i_x * xPartitionLength + minx
937            yp  = i_y * yPartitionLength + miny
938            probe_coords.append([POINT_LIST[i],i+BPN,xp,yp])
939
940        self.probe_coords = probe_coords
941        return
942
943    def get_ix_iy(self,x,y,xPartitionLength,yPartitionLength):
944        i_x=int(x/xPartitionLength)
945        i_y=int(y/yPartitionLength)
946        return i_x,i_y
947
948    #######################################
949    def scale_rotate_code(self,code2scale,scale=[1.0,1.0,1.0,1.0],angle=0.0):
950        if code2scale == []:
951            return code2scale,0,0,0,0,0,0
952        minx =  99999
953        maxx = -99999
954        miny =  99999
955        maxy = -99999
956        minz =  99999
957        maxz = -99999
958        mvtype = -1  # G0 (Rapid), G1 (linear), G2 (clockwise arc) or G3 (counterclockwise arc).
959
960        passthru = ""
961        POS     =[0,0,0]
962        feed = 0
963        spindle = 0
964        out = []
965
966        L = 0
967        R = 1
968        flag_side = 1
969
970        for line in code2scale:
971            if line[0] == 0  or line[0] == 1:
972                mvtype   = line[0]
973                POS_LAST = line[1][:]
974                POS      = line[2][:]
975                CENTER   = ['','','']
976                if line[0] == 1:
977                    feed     = line[3] * scale[3]
978                    spindle   = line[4]
979
980            elif line[0] == 3 or line[0] == 2:
981                mvtype   = line[0]
982                POS_LAST = line[1][:]
983                POS      = line[2][:]
984                CENTER   = line[3][:]
985                feed     = line[4] * scale[3]
986                spindle   = line[5]
987            else:
988                mvtype  = -1
989                passthru = line
990
991            ###############################################################################
992            if mvtype >= 0 and mvtype <=3:
993
994                pos      = self.scale_rot_coords(POS,scale,angle)
995                pos_last = self.scale_rot_coords(POS_LAST,scale,angle)
996
997
998                if CENTER[0]!='' and CENTER[1]!='':
999                    center = self.scale_rot_coords(CENTER,scale,angle)
1000                else:
1001                    center = CENTER
1002
1003                #############################
1004                try:
1005                    minx = min( minx, min(pos[0],pos_last[0]) )
1006                    maxx = max( maxx, max(pos[0],pos_last[0]) )
1007                except:
1008                    pass
1009                try:
1010                    miny = min( miny, min(pos[1],pos_last[1]) )
1011                    maxy = max( maxy, max(pos[1],pos_last[1]) )
1012                except:
1013                    pass
1014                try:
1015                    minz = min( minz, min(pos[2],pos_last[2]) )
1016                    maxz = max( maxz, max(pos[2],pos_last[2]) )
1017                except:
1018                    pass
1019
1020                if mvtype == 0:
1021                    out.append( [mvtype,pos_last,pos] )
1022
1023                if mvtype == 1:
1024                    out.append( [mvtype,pos_last,pos,feed, spindle] )
1025
1026                if mvtype == 2 or mvtype == 3:
1027                    out.append( [ mvtype,pos_last,pos,center, feed, spindle] )
1028
1029                    if mvtype == 3:
1030                        ang1 = self.Get_Angle2(pos_last[0]-center[0],pos_last[1]-center[1])
1031                        xtmp,ytmp = self.Transform(pos[0]-center[0],pos[1]-center[1],radians(-ang1))
1032                        ang2 = self.Get_Angle2(xtmp,ytmp)
1033
1034                    else:
1035                        ang1 = self.Get_Angle2(pos[0]-center[0],pos[1]-center[1])
1036                        xtmp,ytmp = self.Transform(pos_last[0]-center[0],pos_last[1]-center[1],radians(-ang1))
1037                        ang2 = self.Get_Angle2(xtmp,ytmp)
1038
1039                    if ang2 == 0:
1040                        ang2=359.999
1041
1042                    Radius = sqrt( (pos[0]-center[0])**2 +(pos[1]-center[1])**2 )
1043
1044                    if ang1 > 270:
1045                        da = 270
1046                    elif ang1 > 180:
1047                        da = 180
1048                    elif ang1 > 90:
1049                        da = 90
1050                    else:
1051                        da = 0
1052                    for side in [90,180,270,360]:
1053                        spd = side + da
1054                        if ang2 > (spd-ang1):
1055                            if spd > 360:
1056                                spd=spd-360
1057                            if spd==90:
1058                                maxy = max( maxy, center[1]+Radius )
1059                            if spd==180:
1060                                minx = min( minx, center[0]-Radius)
1061                            if spd==270:
1062                                miny = min( miny, center[1]-Radius )
1063                            if spd==360:
1064                                maxx = max( maxx, center[0]+Radius )
1065            ###############################################################################
1066            else:
1067                if passthru != '':
1068                    out.append(passthru)
1069
1070        return out,minx,maxx,miny,maxy,minz,maxz
1071
1072
1073    #######################################
1074    def scale_translate(self,code2translate,translate=[0.0,0.0,0.0]):
1075
1076        if translate[0]==0 and translate[1]==0 and translate[2]==0:
1077            return code2translate
1078
1079        mvtype = -1  # G0 (Rapid), G1 (linear), G2 (clockwise arc) or G3 (counterclockwise arc).
1080        passthru = ""
1081        POS     =[0,0,0]
1082        pos     =[0,0,0]
1083        pos_last=[0,0,0]
1084        feed = 0
1085        spindle = 0
1086        out = []
1087
1088        L = 0
1089        R = 1
1090        flag_side = 1
1091
1092        for line in code2translate:
1093            if line[0] == 1 or line[0] == 0:
1094                mvtype   = line[0]
1095                POS_LAST = line[1][:]
1096                POS      = line[2][:]
1097                CENTER   = ['','','']
1098                if line[0] == 1:
1099                    feed     = line[3]
1100                    spindle  = line[4]
1101
1102            elif line[0] == 3 or line[0] == 2:
1103                mvtype   = line[0]
1104                POS_LAST = line[1][:]
1105                POS      = line[2][:]
1106                CENTER   = line[3][:]
1107                feed     = line[4]
1108                spindle  = line[5]
1109            else:
1110                mvtype  = -1
1111                passthru = line
1112
1113            ###############################################################################
1114            if mvtype >= 0 and mvtype <=3:
1115                pos      = self.scale_trans_coords(POS,translate)
1116                pos_last      = self.scale_trans_coords(POS_LAST,translate)
1117                if CENTER[0]!='' and CENTER[1]!='':
1118                    center      = self.scale_trans_coords(CENTER,translate)
1119                else:
1120                    center = CENTER[:]
1121
1122                #############################
1123                if mvtype == 0:
1124                    out.append( [mvtype,pos_last,pos] )
1125
1126                if mvtype == 1:
1127                    out.append( [mvtype,pos_last,pos,feed,spindle] )
1128
1129                if mvtype == 2 or mvtype == 3:
1130                    out.append( [ mvtype,pos_last,pos,center, feed,spindle] )
1131            ###############################################################################
1132            else:
1133                if passthru != '':
1134                    out.append(passthru)
1135        return out
1136
1137    def scale_trans_coords(self,coords,trans):
1138        x = coords[0] - trans[0]
1139        y = coords[1] - trans[1]
1140        z = coords[2] - trans[2]
1141        return [x,y,z]
1142
1143    def scale_rot_coords(self,coords,scale,rot):
1144        x = coords[0] * scale[0]
1145        y = coords[1] * scale[1]
1146        z = coords[2] * scale[2]
1147
1148        x,y = self.Transform(x,y, radians(rot) )
1149        return [x,y,z]
1150
1151    def generategcode(self,side,z_safe=.5,
1152                      plunge_feed=10.0,
1153                      no_variables=False,
1154                      Rstock=0.0,
1155                      Wrap="XYZ",
1156                      preamble="",
1157                      postamble="",
1158                      gen_rapids=False,
1159                      PLACES_L=4,
1160                      PLACES_R=3,
1161                      PLACES_F=1,
1162                      WriteAll=False,
1163                      FSCALE="Scale-Rotary",
1164                      Reverse_Rotary = False,
1165                      NoComments=False):
1166
1167        g_code = []
1168
1169        sign = 1
1170        if Reverse_Rotary:
1171            sign = -1
1172
1173        self.MODAL_VAL={'X':" ", 'Y':" ", 'Z':" ", 'F':" ", 'A':" ", 'B':" ", 'I':" ", 'J':" "}
1174        LASTX = 0
1175        LASTY = 0
1176        LASTZ = z_safe
1177
1178        g_code.append("( G-Code Modified by G-Code Ripper                        )")
1179        g_code.append("( by Scorch - 2017 www.scorchworks.com                    )")
1180        if Wrap == "XYZ":
1181            AXIS=["X"     , "Y"     , "Z"     ]
1182            DECP=[PLACES_L, PLACES_L, PLACES_L]
1183        elif Wrap == "Y2A":
1184            AXIS=["X"     , "A"     , "Z"     ]
1185            DECP=[PLACES_L, PLACES_R, PLACES_L]
1186            WriteAll=False
1187            g_code.append("(G-Code Ripper has mapped the Y-Axis to the A-Axis      )")
1188        elif Wrap == "X2B":
1189            AXIS=["B"     , "Y"     , "Z"     ]
1190            DECP=[PLACES_R, PLACES_L, PLACES_L]
1191            WriteAll=False
1192            g_code.append("(G-Code Ripper has mapped the X-Axis to the B-Axis      )")
1193        elif Wrap == "Y2B":
1194            AXIS=["X"     , "B"     , "Z"     ]
1195            DECP=[PLACES_L, PLACES_R, PLACES_L]
1196            WriteAll=False
1197            g_code.append("(G-Code Ripper has mapped the Y-Axis to the B-Axis      )")
1198        elif Wrap == "X2A":
1199            AXIS=["A"     , "Y"     , "Z"     ]
1200            DECP=[PLACES_R, PLACES_L, PLACES_L]
1201            WriteAll=False
1202            g_code.append("(G-Code Ripper has mapped the X-Axis to the A-Axis      )")
1203
1204        if Wrap != "XYZ":
1205            g_code.append("(A nominal stock radius of %f was used.             )" %(Rstock))
1206            g_code.append("(Z-axis zero position is the surface of the round stock.  )")
1207            g_code.append("(---------------------------------------------------------)")
1208
1209        g_code.append("G90   (set absolute distance mode)")
1210        g_code.append("G90.1 (set absolute distance mode for arc centers)")
1211        g_code.append("G17   (set active plane to XY)")
1212
1213        if self.units == "in":
1214            g_code.append("G20   (set units to inches)")
1215        else:
1216            g_code.append("G21   (set units to mm)")
1217
1218        if no_variables==False:
1219            g_code.append("#<z_safe> = % 5.3f " %(z_safe))
1220            g_code.append("#<plunge_feed> = % 5.0f " %(plunge_feed))
1221
1222        for line in preamble.split('|'):
1223            g_code.append(line)
1224
1225        g_code.append("(---------------------------------------------------------)")
1226        ###################
1227        ## GCODE WRITING ##
1228        ###################
1229        for line in side:
1230            if line[0] == 1 or line[0] == 2 or line[0] == 3 or (line[0] == 0 and gen_rapids == False):
1231                D0 = line[2][0]-line[1][0]
1232                D1 = line[2][1]-line[1][1]
1233                D2 = line[2][2]-line[1][2]
1234                D012 = sqrt((D0+0j).real**2+(D1+0j).real**2+(D2+0j).real**2)
1235
1236                coordA=[ line[1][0], line[1][1], line[1][2] ]
1237                coordB=[ line[2][0], line[2][1], line[2][2] ]
1238                if Wrap == "Y2A" or Wrap == "Y2B":
1239                    #if line[1][1].imag == 0:
1240                    if (not isinstance(line[1][1], complex)):
1241                        coordA[1]=sign*degrees(line[1][1]/Rstock)
1242                    #if line[2][1].imag == 0:
1243                    if (not isinstance(line[2][1], complex)):
1244                        coordB[1]=sign*degrees(line[2][1]/Rstock)
1245                elif Wrap == "X2B" or Wrap == "X2A":
1246                    #if line[1][0].imag == 0:
1247                    if (not isinstance(line[1][0], complex)):
1248                        coordA[0]=sign*degrees(line[1][0]/Rstock)
1249                    #if line[2][0].imag == 0:
1250                    if (not isinstance(line[2][0], complex)):
1251                        coordB[0]=sign*degrees(line[2][0]/Rstock)
1252
1253                dx = coordA[0]-LASTX
1254                dy = coordA[1]-LASTY
1255                dz = coordA[2]-LASTZ
1256
1257                # Check if next point is coincident with the
1258                # current point withing the set accuracy
1259                if sqrt((dx+0j).real**2 + (dy+0j).real**2 + (dz+0j).real**2) > self.accuracy and gen_rapids == True:
1260                    ### Move tool to safe height (z_safe) ###
1261                    if no_variables==False:
1262                        g_code.append("G0 %c #<z_safe> " %(AXIS[2]) )
1263                        self.MODAL_VAL[AXIS[2]] = z_safe
1264                    else:
1265                        LINE = "G0"
1266                        LINE = self.app_gcode_line(LINE,AXIS[2],z_safe,DECP[2],WriteAll)
1267                        if len(LINE) > 2: g_code.append(LINE)
1268
1269                    ### Move tool to coordinates of next cut ###
1270                    LINE = "G0"
1271                    LINE = self.app_gcode_line(LINE,AXIS[0],coordA[0],DECP[0],WriteAll)
1272                    LINE = self.app_gcode_line(LINE,AXIS[1],coordA[1],DECP[1],WriteAll)
1273                    if len(LINE) > 2: g_code.append(LINE)
1274
1275                    if float(coordA[2]) < float(z_safe):
1276                        if no_variables==False:
1277                            LINE = "G1"
1278                            LINE = self.app_gcode_line(LINE,AXIS[2],coordA[2],DECP[2],WriteAll)
1279                            LINE = LINE + " F #<plunge_feed>"
1280                            self.MODAL_VAL["F"] = plunge_feed
1281                            if len(LINE) > 2: g_code.append(LINE)
1282                        else:
1283                            LINE = "G1"
1284                            LINE = self.app_gcode_line(LINE,AXIS[2],coordA[2]  ,DECP[2]  , WriteAll)
1285                            LINE = self.app_gcode_line(LINE,"F"    ,plunge_feed,PLACES_F, WriteAll)
1286                            if len(LINE) > 2: g_code.append(LINE)
1287
1288                LASTX = coordB[0]
1289                LASTY = coordB[1]
1290                LASTZ = coordB[2]
1291
1292            if (line[0] == 1) or (line[0] == 2) or (line[0] == 3) or (line[0] == 0 and (gen_rapids == False)):
1293                try:    LAST0 = float(self.MODAL_VAL[AXIS[0]])
1294                except: LAST0 = coordB[0]
1295                try:    LAST1 = float(self.MODAL_VAL[AXIS[1]])
1296                except: LAST1 = coordB[1]
1297                try:    LAST2 = float(self.MODAL_VAL[AXIS[2]])
1298                except: LAST2 = coordB[2]
1299
1300                LINE = "G%d" %(line[0])
1301                LINE = self.app_gcode_line(LINE,AXIS[0],coordB[0],DECP[0],WriteAll)
1302                LINE = self.app_gcode_line(LINE,AXIS[1],coordB[1],DECP[1],WriteAll)
1303                LINE = self.app_gcode_line(LINE,AXIS[2],coordB[2],DECP[2],WriteAll)
1304                if (line[0] == 1):
1305                    if ((LINE.find("A") > -1) or (LINE.find("B") > -1)) and (FSCALE == "Scale-Rotary") and (D012>self.Zero):
1306                        if (LINE.find("X") > -1) or (LINE.find("Y") > -1) or (LINE.find("Z") > -1):
1307                            if Wrap == "Y2A" or Wrap == "Y2B":
1308                                Df = sqrt(D0**2 + D2**2)
1309                            elif Wrap == "X2B" or Wrap == "X2A":
1310                                Df = sqrt(D1**2 + D2**2)
1311                            Feed_adj = abs(Df / (D012/line[3]))
1312                        else:
1313                            if Wrap == "Y2A" or Wrap == "Y2B":
1314                                DAf = coordB[1]-LAST1
1315                                Feed_adj = abs(DAf / (D012/line[3]))
1316                            elif Wrap == "X2B" or Wrap == "X2A":
1317                                DAf = coordB[0]-LAST0
1318                                Feed_adj = abs(DAf / (D012/line[3]))
1319                    else:
1320                        Feed_adj = line[3]
1321                    LINE = self.app_gcode_line(LINE,"F",Feed_adj  ,PLACES_F,WriteAll)
1322
1323                elif (line[0] == 2) or (line[0] == 3):
1324                    LINE = self.app_gcode_line(LINE,"I",line[3][0],DECP[0]  ,WriteAll)
1325                    LINE = self.app_gcode_line(LINE,"J",line[3][1],DECP[1]  ,WriteAll)
1326                    LINE = self.app_gcode_line(LINE,"F",Feed_adj  ,PLACES_F,WriteAll)
1327                if len(LINE) > 2: g_code.append(LINE)
1328
1329            elif (line[0] == 0 and gen_rapids == True):
1330                pass
1331
1332            elif line[0] == ";":
1333                if not NoComments:
1334                    g_code.append("%s" %(line[1]))
1335
1336            elif line[0] == "M2":
1337                if gen_rapids == True:
1338                    if no_variables==False:
1339                        g_code.append("G0 %c #<z_safe> " %(AXIS[2]) )
1340                        self.MODAL_VAL[AXIS[2]] = z_safe
1341                    else:
1342                        LINE = "G0"
1343                        LINE = self.app_gcode_line(LINE,AXIS[2],z_safe,DECP[2],WriteAll)
1344                        g_code.append(LINE)
1345
1346                for entry in postamble.split('|'):
1347                    g_code.append(entry)
1348                #g_code.append(line[0])
1349            else:
1350                g_code.append(line)
1351        ########################
1352        ## END G-CODE WRITING ##
1353        ########################
1354        return g_code
1355
1356
1357    def app_gcode_line(self,LINE,CODE,VALUE,PLACES,WriteAll):
1358        if isinstance(VALUE, complex):
1359            return LINE
1360        #if VALUE.imag != 0:
1361        #    return LINE
1362
1363        if CODE == "F":
1364            if (VALUE*10**PLACES) < 1:
1365                # Fix Zero feed rate
1366                VALUE = 1.0/(10**PLACES)
1367
1368        if PLACES > 0:
1369            FORMAT="%% .%df" %(PLACES)
1370        else:
1371            FORMAT="%d"
1372        VAL = FORMAT %(VALUE)
1373
1374        if ( VAL != self.MODAL_VAL[CODE] )\
1375            or ( CODE=="I" ) \
1376            or ( CODE=="J" ) \
1377            or  (WriteAll):
1378            LINE = LINE +  " %s%s" %(CODE, VAL)
1379            self.MODAL_VAL[CODE] = VAL
1380
1381        return LINE
1382
1383    def get_arc_intersects(self, p1, p2, xsplit, cent, code):
1384        xcross1= xsplit
1385        xcross2= xsplit
1386
1387        R = sqrt( (cent[0]-p1[0])**2 + (cent[1]-p1[1])**2 )
1388        Rt= sqrt( (cent[0]-p2[0])**2 + (cent[1]-p2[1])**2 )
1389        if abs(R-Rt) > self.accuracy:  self.fmessage("Radius Warning: R1=%f R2=%f"%(R,Rt))
1390
1391        val =  R**2 - (xsplit - cent[0])**2
1392        if val >= 0.0:
1393            root = sqrt( val )
1394            ycross1 = cent[1] - root
1395            ycross2 = cent[1] + root
1396        else:
1397            return []
1398
1399        theta = self.Get_Angle2(p1[0]-cent[0],p1[1]-cent[1])
1400
1401        xbeta,ybeta = self.Transform(p2[0]-cent[0],p2[1]-cent[1],radians(-theta))
1402        beta  = self.Get_Angle2(xbeta,ybeta,code)
1403
1404        if abs(beta) <= self.Zero: beta = 360.0
1405
1406        xt,yt = self.Transform(xsplit-cent[0],ycross1-cent[1],radians(-theta))
1407        gt1 = self.Get_Angle2(xt,yt,code)
1408
1409        xt,yt = self.Transform(xsplit-cent[0],ycross2-cent[1],radians(-theta))
1410        gt2 = self.Get_Angle2(xt,yt,code)
1411
1412        if gt1 < gt2:
1413           gamma1 = gt1
1414           gamma2 = gt2
1415        else:
1416           gamma1 = gt2
1417           gamma2 = gt1
1418           temp = ycross1
1419           ycross1 = ycross2
1420           ycross2 = temp
1421
1422        dz = p2[2] - p1[2]
1423        da = beta
1424        mz = dz/da
1425        zcross1 = p1[2] + gamma1 * mz
1426        zcross2 = p1[2] + gamma2 * mz
1427
1428        output=[]
1429        if gamma1 < beta and gamma1 > self.Zero and gamma1 < beta-self.Zero:
1430            output.append([xcross1,ycross1,zcross1])
1431        if gamma2 < beta and gamma1 > self.Zero and gamma2 < beta-self.Zero:
1432            output.append([xcross2,ycross2,zcross2])
1433
1434        #print(" start: x1 =%5.2f y1=%5.2f z1=%5.2f" %(p1[0],     p1[1],     p1[2]))
1435        #print("   end: x2 =%5.2f y2=%5.2f z2=%5.2f" %(p2[0],     p2[1],     p2[2]))
1436        #print("center: xc =%5.2f yc=%5.2f xsplit=%5.2f code=%s" %(cent[0],cent[1],xsplit,code))
1437        #print("R = %f" %(R))
1438        #print("theta =%5.2f" %(theta))
1439        #print("beta  =%5.2f gamma1=%5.2f gamma2=%5.2f\n" %(beta,gamma1,gamma2))
1440        #cnt=0
1441        #for line in output:
1442        #    cnt=cnt+1
1443        #    print("arc cross%d: %5.2f, %5.2f, %5.2f" %(cnt, line[0], line[1], line[2]))
1444        #print(output)
1445        #print("----------------------------------------------\n")
1446
1447        return output
1448
1449    def arc2lines(self, p1, p2, cent, code, plane="17"):
1450        if   plane == "18":
1451            xind=2
1452            yind=0
1453            zind=1
1454        elif plane == "19":
1455            xind=1
1456            yind=2
1457            zind=0
1458        elif plane == "17":
1459            xind=0
1460            yind=1
1461            zind=2
1462
1463        R = sqrt( (cent[xind]-p1[xind])**2 + (cent[yind]-p1[yind])**2 )
1464        Rt= sqrt( (cent[xind]-p2[xind])**2 + (cent[yind]-p2[yind])**2 )
1465        if abs(R-Rt) > self.accuracy:  self.fmessage("Radius Warning: R1=%f R2=%f "%(R,Rt))
1466
1467        if code == 3:
1468            theta = self.Get_Angle2(p1[xind]-cent[xind],p1[yind]-cent[yind])
1469            xbeta,ybeta = self.Transform(p2[xind]-cent[xind],p2[yind]-cent[yind],radians(-theta))
1470            X1 = p1[xind]
1471            Y1 = p1[yind]
1472            Z1 = p1[zind]
1473            zstart = Z1
1474            zend   = p2[zind]
1475        if code == 2:
1476            theta = self.Get_Angle2(p2[xind]-cent[xind],p2[yind]-cent[yind])
1477            xbeta,ybeta = self.Transform(p1[xind]-cent[xind],p1[yind]-cent[yind],radians(-theta))
1478            X1 = p2[xind]
1479            Y1 = p2[yind]
1480            Z1 = p2[zind]
1481            zstart = Z1
1482            zend   = p1[zind]
1483
1484        beta  = self.Get_Angle2(xbeta,ybeta) #,code)
1485
1486        if abs(beta) <= self.Zero: beta = 360.0
1487        ##########################################
1488        arc_step=self.arc_angle
1489
1490        my_range=[]
1491
1492        at=arc_step
1493        while at < beta:
1494            my_range.append(at)
1495            at = at+arc_step
1496        my_range.append(beta)
1497
1498
1499
1500        new_lines=[]
1501        for at in my_range:
1502            xt,yt = self.Transform(R,0,radians(theta+at))
1503
1504            X2 = cent[xind] + xt
1505            Y2 = cent[yind] + yt
1506            #Z2 = p1[zind] + at*(p2[zind]-p1[zind])/beta
1507            Z2 = zstart + at*(zend-zstart)/beta
1508            data = ["","","","","",""]
1509
1510
1511            if code == 3:
1512                data[xind]=X1
1513                data[yind]=Y1
1514                data[zind]=Z1
1515                data[3+xind]=X2
1516                data[3+yind]=Y2
1517                data[3+zind]=Z2
1518                new_lines.append(data)
1519            else:
1520                data[xind]=X2
1521                data[yind]=Y2
1522                data[zind]=Z2
1523                data[3+xind]=X1
1524                data[3+yind]=Y1
1525                data[3+zind]=Z1
1526                new_lines.insert(0, data)
1527
1528            X1=X2
1529            Y1=Y2
1530            Z1=Z2
1531            at = at+arc_step
1532
1533        return new_lines
1534
1535    def get_line_intersect(self,p1, p2, xsplit):
1536        dx = p2[0] - p1[0]
1537        dy = p2[1] - p1[1]
1538        dz = p2[2] - p1[2]
1539
1540        xcross = xsplit
1541        try:
1542            my = dy/dx
1543            by = p1[1] - my * p1[0]
1544            ycross = my*xsplit + by
1545        except:
1546            ycross = p1[1]
1547        try:
1548            mz = dz/dx
1549            bz = p1[2] - mz * p1[0]
1550            zcross = mz*xsplit + bz
1551        except:
1552            zcross = p1[2]
1553
1554        output=[]
1555        if xcross > min(p1[0],p2[0])+self.Zero and xcross < max(p1[0],p2[0])-self.Zero:
1556            output.append([xcross,ycross,zcross])
1557        return output
1558
1559
1560    def apleft(self, gtext):
1561        self.left_side.append(gtext)
1562
1563
1564    def apright(self, gtext):
1565        self.right_side.append(gtext)
1566
1567
1568    def apboth(self, gtext):
1569        self.left_side.append(gtext)
1570        self.right_side.append(gtext)
1571
1572
1573    def rm_text(self,line,s,e):
1574        if e == -1:
1575            e = len(line)
1576        temp1 = line[0:s]
1577        temp2 = line[e+1:len(line)]
1578        return temp1+temp2
1579
1580
1581    def insert_text(self,line,itext,s):
1582        temp1 = line[0:s]
1583        temp2 = line[s:len(line)]
1584        return temp1+itext+temp2
1585
1586
1587    def coordop(self,coords,offset,rot):
1588        x = coords[0]
1589        y = coords[1]
1590        z = coords[2]
1591        x = x - offset[0]
1592        y = y - offset[1]
1593        z = z - offset[2]
1594        x,y = self.Transform(x,y, radians(rot) )
1595        return [x,y,z]
1596
1597
1598    def coordunop(self,coords,offset,rot):
1599        x = coords[0]
1600        y = coords[1]
1601        z = coords[2]
1602        x,y = self.Transform(x,y, radians(-rot) )
1603        x = x + offset[0]
1604        y = y + offset[1]
1605        z = z + offset[2]
1606        return [x,y,z]
1607
1608    #######################################    #######################################
1609    #######################################    #######################################
1610    #######################################    #######################################
1611    #######################################    #######################################
1612
1613    def FUNCTION_EVAL(self,list):
1614        #list consists of [name,val1,val2]
1615        name = list[0]
1616        val1 = float(list[1])
1617        fval = float(val1)
1618        #############################################
1619        ########### G-CODE FUNCTIONS ################
1620        #############################################
1621        # ATAN[Y]/[X] Four quadrant inverse tangent #
1622        # ABS[arg]    Absolute value                #
1623        # ACOS[arg]   Inverse cosine                #
1624        # ASIN[arg]   Inverse sine                  #
1625        # COS[arg]    Cosine                        #
1626        # EXP[arg]    e raised to the given power   #
1627        # FIX[arg]    Round down to integer         #
1628        # FUP[arg]    Round up to integer           #
1629        # ROUND[arg]  Round to nearest integer      #
1630        # LN[arg]     Base-e logarithm              #
1631        # SIN[arg]    Sine                          #
1632        # SQRT[arg]   Square Root                   #
1633        # TAN[arg]    Tangent                       #
1634        # EXISTS[arg]	Check named Parameter   #
1635        #############################################
1636        if name == "ATAN":
1637            fval2 = float(list[2])
1638            atan2(fval1,fval2)
1639        if name == "ABS":
1640            return abs(fval)
1641        if name == "ACOS":
1642            return degrees(acos(fval))
1643        if name == "ASIN":
1644            return degrees(asin(fval))
1645        if name == "COS":
1646            return cos(radians(fval))
1647        if name == "EXP":
1648            return exp(fval)
1649        if name == "FIX":
1650            return floor(fval)
1651        if name == "FUP":
1652            return ceil(fval)
1653        if name == "ROUND":
1654            return round(fval)
1655        if name == "LN":
1656            return log(fval)
1657        if name == "SIN":
1658            return sin(radians(fval))
1659        if name == "SQRT":
1660            return sqrt(fval)
1661        if name == "TAN":
1662            return tan(radians(fval))
1663        if name == "EXISTS":
1664            pass
1665
1666    def EXPRESSION_EVAL(self,line):
1667        ###################################################
1668        ###          EVALUATE MATH IN REGION            ###
1669        ###################################################
1670        line_in = line
1671        P = 0
1672        if P==1: self.fmessage("line=%s" %(line))
1673
1674        if len(line)<2:
1675            MSG = "ERROR EXP-1: Unable to evaluate expression: %s\n" %(line_in)
1676            raise ValueError(MSG)
1677
1678
1679        line = line.replace(" ","")
1680
1681        #################################################
1682        ###           G-CODE OPPERATORS               ###
1683        ###          In Precedence Order              ###
1684        #################################################
1685        ##    **                                        #
1686        ##    * / MOD                                   #
1687        ##    + -                                       #
1688        ##    EQ NE GT GE LT LE                         #
1689        ##    AND OR XOR                                #
1690        #################################################
1691
1692        #################################################
1693        ### Replace multiple +/- with single operator ###
1694        #################################################
1695        cnt = 1
1696        while cnt > 0:
1697            if (not self.cmp_new(line[cnt],'+')) or (not self.cmp_new(line[cnt],'-')):
1698                sign = 1
1699                span = 0
1700                FLAG = True
1701                while FLAG:
1702                    if not self.cmp_new(line[cnt+span],'+'):
1703                        span = span + 1
1704                    elif not self.cmp_new(line[cnt+span],'-'):
1705                        sign = -sign
1706                        span = span + 1
1707                    else:
1708                        FLAG = False
1709                tmp1=line[:(cnt)]
1710                tmp2=line[(cnt+span):]
1711                if sign > 0:
1712                    line = tmp1+'+'+tmp2
1713                else:
1714                    line = tmp1+'-'+tmp2
1715            cnt=cnt + 1
1716            if cnt >= len(line):
1717                cnt = -1
1718
1719        #########################################
1720        ### Replace multi-character operators ###
1721        ### with single character identifiers ###
1722        #########################################
1723        line = line.replace("XOR","|")
1724        line = line.replace("AND","&")
1725        line = line.replace("LE","l")
1726        line = line.replace("LT","<")
1727        line = line.replace("GE","g")
1728        line = line.replace("GT",">")
1729        line = line.replace("NE","!")
1730        line = line.replace("EQ","=")
1731        line = line.replace("**","^")
1732
1733        #########################################
1734        ###     Split the text into a list    ###
1735        #########################################
1736        line = re.split( "([\[,\],\^,\*,\/,\%,\+,\-,\|  ,\&  ,\l ,\< ,\g ,\> ,\! ,\= ])", line)
1737
1738        #########################################
1739        ### Remove empty items from the list  ###
1740        #########################################
1741        for i in range(line.count('')): line.remove('')
1742
1743        #########################################
1744        ###   Find the last "[" in the list   ###
1745        #########################################
1746        s=-1
1747        for cnt in range(s+1,len(line)):
1748            if line[cnt] == '[':
1749                s = cnt
1750        if s == -1:
1751            MSG = "ERROR EXP-2: Unable to evaluate expression: %s" %(line_in)
1752            #self.fmessage(MSG)
1753            raise ValueError(MSG)
1754
1755        #################################################################
1756        ###  While there are still brackets "[...]" keep processing   ###
1757        #################################################################
1758        while s != -1:
1759            ##############################################################
1760            ### Find the first occurance of "]" after the current "["  ###
1761            ##############################################################
1762            e=-1
1763            for cnt in range(len(line)-1,s,-1):
1764                if line[cnt] == ']':
1765                    e = cnt
1766
1767            #############################################
1768            ###  Find the items between the brackets  ###
1769            #############################################
1770            temp = line[s+1:e]
1771
1772            ##############################################################
1773            ###  Fix Some Special Cases                                ###
1774            ##############################################################
1775            ### **-  *-  MOD-                                          ###
1776            ##############################################################
1777            for cnt in range(0,len(temp)):
1778                if (not self.cmp_new(temp[cnt],'^')) or \
1779                   (not self.cmp_new(temp[cnt],'*')) or \
1780                   (not self.cmp_new(temp[cnt],'%')):
1781                    if not self.cmp_new(temp[cnt+1],'-'):
1782                        temp[cnt+1]=''
1783                        temp[cnt+2]= -float(temp[cnt+2])
1784                    elif not self.cmp_new(temp[cnt+1],'+'):
1785                        temp[cnt+1]=''
1786                        temp[cnt+2]= float(temp[cnt+2])
1787            for i in range(temp.count('')): temp.remove('')
1788
1789            #####################################
1790            XOR_operation = self.list_split(temp,"|") #XOR
1791            for iXOR in range(0,len(XOR_operation)):
1792                #####################################
1793                AND_operation = self.list_split(XOR_operation[iXOR],"&") #AND
1794                for iAND in range(0,len(AND_operation)):
1795                    #####################################
1796                    LE_operation = self.list_split(AND_operation[iAND],"l") #LE
1797                    for iLE in range(0,len(LE_operation)):
1798                        #####################################
1799                        LT_operation = self.list_split(LE_operation[iLE],"<") #LT
1800                        for iLT in range(0,len(LT_operation)):
1801                            #####################################
1802                            GE_operation = self.list_split(LT_operation[iLT],"g") #GE
1803                            for iGE in range(0,len(GE_operation)):
1804                                #####################################
1805                                GT_operation = self.list_split(GE_operation[iGE],">") #GT
1806                                for iGT in range(0,len(GT_operation)):
1807                                    #####################################
1808                                    NE_operation = self.list_split(GT_operation[iGT],"!") #NE
1809                                    for iNE in range(0,len(NE_operation)):
1810                                        #####################################
1811                                        EQ_operation = self.list_split(NE_operation[iNE],"=") #EQ
1812                                        for iEQ in range(0,len(EQ_operation)):
1813                                            #####################################
1814                                            add = self.list_split(EQ_operation[iEQ],"+")
1815                                            for cnt in range(1,len(add)):
1816                                                if add[cnt-1]==[]:
1817                                                    add[cnt-1]  = ''
1818                                            for i in range(add.count('')): add.remove('')
1819                                            for iadd in range(0,len(add)):
1820                                                #####################################
1821                                                subtract = self.list_split(add[iadd],"-")
1822                                                for cnt in range(1,len(subtract)):
1823                                                    if subtract[cnt-1]==[]:
1824                                                        subtract[cnt-1]  = ''
1825                                                        subtract[cnt][0] = -float(subtract[cnt][0])
1826                                                for i in range(subtract.count('')): subtract.remove('')
1827                                                for isub in range(0,len(subtract)):
1828                                                    #####################################
1829                                                    multiply = self.list_split(subtract[isub],"*")
1830                                                    for imult in range(0,len(multiply)):
1831                                                        #####################################
1832                                                        divide = self.list_split(multiply[imult],"/")
1833                                                        for idiv in range(0,len(divide)):
1834                                                            #####################################
1835                                                            mod = self.list_split(divide[idiv],"%")
1836                                                            for imod in range(0,len(mod)):
1837                                                                #####################################
1838                                                                power = self.list_split(mod[imod],"^")
1839                                                                for ipow in range(0,len(power)):
1840                                                                    if power[ipow]==[]:
1841                                                                        MSG = "ERROR EXP-3: Unable to evaluate expression: %s" %(line_in)
1842                                                                        raise ValueError(MSG)
1843
1844                                                                    if type(power[0]) is list:
1845                                                                        power_len = len(power[0])
1846                                                                    else:
1847                                                                        power_len = 1
1848                                                                    if power_len > 1:
1849                                                                        power[ipow] = self.FUNCTION_EVAL(power[0])
1850                                                                    else:
1851                                                                        power[ipow] = float(power[ipow][0])
1852                                                                #####################################
1853                                                                res_power=power[0]
1854                                                                for k in range(1,len(power)):
1855                                                                    res_power = res_power**power[k]
1856                                                                    if P==True: self.fmessage("  POWER"+str(power)+"="+str(res_power))
1857                                                                mod[imod]=res_power
1858                                                            #####################################
1859                                                            #REVERSE MOD
1860                                                            res_mod=mod[len(mod)-1]
1861                                                            for k in range(len(mod),1,-1):
1862                                                                res_mod = mod[k-2]%res_mod
1863                                                                self.fmessage("     MOD"+str(mod)+"="+str(res_mod))
1864                                                            divide[idiv]=res_mod
1865                                                        #####################################
1866                                                        res_divide=divide[0]
1867                                                        for k in range(1,len(divide),1):
1868                                                            res_divide = res_divide/divide[k]
1869                                                            if P==True: self.fmessage("  DIVIDE"+str(divide)+"="+str(res_divide))
1870                                                        multiply[imult]=res_divide
1871                                                    #####################################
1872                                                    res_multiply=multiply[0]
1873                                                    for k in range(1,len(multiply)):
1874                                                        res_multiply = res_multiply*multiply[k]
1875                                                        if P==True: self.fmessage("MULTIPLY"+str(multiply)+"="+str(res_multiply))
1876                                                    subtract[isub]=res_multiply
1877                                                #####################################
1878                                                res_subtract=subtract[0]
1879                                                for k in range(1,len(subtract)):
1880                                                    res_subtract = res_subtract-subtract[k]
1881                                                    if P==True: self.fmessage("SUBTRACT"+str(subtract)+"="+str(res_subtract))
1882                                                add[iadd]=res_subtract
1883                                            #####################################
1884                                            res_add=add[len(add)-1]
1885                                            for k in range(len(add),1,-1):
1886                                                res_add = add[k-2]+res_add
1887                                                if P==True: self.fmessage("     ADD"+str(add)+"="+str(res_add))
1888                                            EQ_operation[iEQ]=res_add
1889                                        #####################
1890                                        res_EQ=EQ_operation[0]
1891                                        for k in range(1,len(EQ_operation),1):
1892                                            if res_EQ == EQ_operation[k]:
1893                                                res_EQ = 1
1894                                            else:
1895                                                res_EQ = 0
1896                                            if P==True: self.fmessage("      EQ"+str(EQ_operation)+"="+str(res_EQ))
1897                                        NE_operation[iNE]=res_EQ
1898                                    #####################
1899                                    res_NE=NE_operation[0]
1900                                    for k in range(1,len(NE_operation),1):
1901                                        if res_NE != NE_operation[k]:
1902                                            res_NE = 1
1903                                        else:
1904                                            res_NE = 0
1905                                        if P==True: self.fmessage("      NE"+str(NE_operation)+"="+str(res_NE))
1906                                    GT_operation[iGT]=res_NE
1907                                #####################
1908                                res_GT=GT_operation[0]
1909                                for k in range(1,len(GT_operation),1):
1910                                    if res_GT > GT_operation[k]:
1911                                        res_GT = 1
1912                                    else:
1913                                        res_GT = 0
1914                                    if P==True: self.fmessage("      GT"+str(GT_operation),"="+str(res_GT))
1915                                GE_operation[iGE]=res_GT
1916                            #####################
1917                            res_GE=GE_operation[0]
1918                            for k in range(1,len(GE_operation),1):
1919                                if res_GE >= GE_operation[k]:
1920                                    res_GE = 1
1921                                else:
1922                                    res_GE = 0
1923                                if P==True: self.fmessage("      GE"+str(GE_operation)+"="+str(res_GE))
1924                            LT_operation[iLT]=res_GE
1925                        #####################
1926                        res_LT=LT_operation[0]
1927                        for k in range(1,len(LT_operation),1):
1928                            if res_LT < LT_operation[k]:
1929                                res_LT = 1
1930                            else:
1931                                res_LT = 0
1932                            if P==True: self.fmessage("      LT"+str(LT_operation)+"="+str(res_LT))
1933                        LE_operation[iLE]=res_LT
1934                    #####################
1935                    res_LE=LE_operation[0]
1936                    for k in range(1,len(LE_operation),1):
1937                        if res_LE <= LE_operation[k]:
1938                            res_LE = 1
1939                        else:
1940                            res_LE = 0
1941                        if P==True: self.fmessage("      LE"+str(LE_operation)+"="+str(res_LE))
1942                    AND_operation[iAND]=res_LE
1943                #####################
1944                res_AND=AND_operation[0]
1945                for k in range(1,len(AND_operation),1):
1946                    if res_AND and AND_operation[k]:
1947                        res_AND = 1
1948                    else:
1949                        res_AND = 0
1950                    if P==True: self.fmessage("      AND"+str(AND_operation)+"="+str(res_AND))
1951                XOR_operation[iXOR]=res_AND
1952            #####################
1953            res_XOR=XOR_operation[0]
1954            for k in range(1,len(XOR_operation),1):
1955                if bool(res_XOR) ^ bool(XOR_operation[k]):
1956                    res_XOR = 1
1957                else:
1958                    res_XOR = 0
1959                if P==True: self.fmessage("      XOR"+str(XOR_operation)+"="+str(res_XOR))
1960
1961            #####################################
1962            ### Return NEW VALUE to the list  ###
1963            #####################################
1964            for i in range(e,s-1,-1): line.pop(i)
1965            line.insert(int(s),res_XOR)
1966
1967            #############################
1968            # Find Last "[" in the list #
1969            #############################
1970            s=-1
1971            for cnt in range(s+1,len(line)):
1972                if line[cnt] == '[':
1973                    s = cnt
1974        #################################################################
1975        ###  END of While there are still brackets "[...]"            ###
1976        #################################################################
1977
1978        if len(line) > 1:
1979            MSG = "ERROR EXP-5: Unable to evaluate expression: %s" %(line_in)
1980            raise ValueError(MSG)
1981        return "%.4f" %(line[0])
1982
1983
1984    def list_split(self,lst,obj):
1985        loc=[]
1986        index = -1
1987        for cnt in range(0,len(lst)):
1988            if not self.cmp_new(lst[cnt],obj):
1989                loc.append( lst[index+1:cnt] )
1990                index=cnt
1991        loc.append( lst[index+1:len(lst)])
1992        return loc
1993
1994
1995    #Define self.cmp_new, cmp replacement for Python 3 compatability
1996    def cmp_new(self,A,B):
1997        if A==B:
1998            return False
1999        else:
2000            return True
2001
2002    ############################################################################
2003    # routine takes an x and a y coords and does a cordinate transformation    #
2004    # to a new coordinate system at angle from the initial coordinate system   #
2005    # Returns new x,y tuple                                                    #
2006    ############################################################################
2007    def Transform(self,x,y,angle):
2008        newx = x * cos(angle) - y * sin(angle)
2009        newy = x * sin(angle) + y * cos(angle)
2010        return newx,newy
2011
2012    ############################################################################
2013    # routine takes an sin and cos and returnss the angle (between 0 and 360)  #
2014    ############################################################################
2015    def Get_Angle2(self,x,y,code=""):
2016        angle = 90.0-degrees(atan2(x,y))
2017        if angle < 0:
2018            angle = 360 + angle
2019        if code == "G2":
2020            return (360.0 - angle)
2021        else:
2022            return angle
2023
2024
2025    ##################################################
2026    ###  Generate paths for Laser cutter           ###
2027    ##################################################
2028    def generate_laser_paths(self,side,Rapids=True):
2029        ecoords = []
2030        #################################
2031        xlast = -99999
2032        ylast = -99999
2033        loop = 1
2034        for line in side:
2035            #print line
2036            if line[0] == 1:
2037                feed    = line[3]
2038                spindle = line[4]
2039                x1=(line[1][0]+0j).real
2040                y1=(line[1][1]+0j).real
2041                z1=(line[1][2]+0j).real
2042
2043                x2=(line[2][0]+0j).real
2044                y2=(line[2][1]+0j).real
2045                z2=(line[2][2]+0j).real
2046
2047                if xlast!=x1 or ylast!=y1:
2048                    loop = loop+1
2049                    ecoords.append([x1,y1,loop,feed,spindle])
2050
2051                if x2!=x1 or y2!=y1:
2052                    ecoords.append([x2,y2,loop,feed,spindle])
2053
2054                xlast = x2
2055                ylast = y2
2056        ######################################
2057        return ecoords
2058    ##################################################
2059    ###    End Generate paths for Laser cutter     ###
2060    ##################################################
2061
2062
2063
2064if __name__ == "__main__":
2065    g_rip = G_Code_Rip()
2066    filename="Z:\\text.ngc"
2067    MSG = g_rip.Read_G_Code(filename, XYarc2line = True, arc_angle=2, units="in", Accuracy="")
2068    print(MSG)
2069    ecoords = g_rip.generate_laser_paths(g_rip.g_code_data)
2070    for line in ecoords:
2071        print(line)
2072