#!/usr/bin/python

## image-to-gcode is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by the
## Free Software Foundation; either version 2 of the License, or (at your
## option) any later version.  image-to-gcode is distributed in the hope 
## that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
## warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
## the GNU General Public License for more details.  You should have
## received a copy of the GNU General Public License along with image-to-gcode;
## if not, write to the Free Software Foundation, Inc., 59 Temple Place,
## Suite 330, Boston, MA 02111-1307 USA
## 
## image-to-gcode.py is Copyright (C) 2005 Chris Radek
## chris@timeguy.com
## image-to-gcode.py is Copyright (C) 2006 Jeff Epler
## jepler@unpy.net
## image-to-gcode.py is Copyright (C) 2013 Harmonist
## cnc-club.ru

import sys, os
BASE = os.path.abspath(os.path.join(os.path.dirname(sys.argv[0]), ".."))
sys.path.insert(0, os.path.join(BASE, "lib", "python"))

import gettext;
gettext.install("linuxcnc", localedir=os.path.join(BASE, "share", "locale"), unicode=True)

import Image

try:
    import numpy.numarray as numarray
    import numpy.core
    olderr = numpy.core.seterr(divide='ignore')
    plus_inf = (numarray.array((1.,))/0.)[0]
    numpy.core.seterr(**olderr)
except ImportError:
    import numarray, numarray.ieeespecial
    plus_inf = numarray.ieeespecial.inf

from rs274.author import Gcode
import rs274.options

from math import *
import operator

import datetime

epsilon = 1e-5
epsilon16 = 1e-16
is_ddd1 = True
roughing_depth_delta = 0.2   # ~20% 
prn_detal = 1  #Print detal

def ball_tool(r,rad):
    if r == rad: return rad
    #s = -sqrt(rad**2-r**2)
    s = rad-sqrt(rad**2-r**2)
    return s

def endmill(r,dia):
    return 0

def vee_common(angle):
    #slope = tan(angle * pi / 180)
    slope = tan(radians((180-angle)/2) )
    def f(r, dia):
        return r * slope
    return f

#def vee_common_new():
#    slope = 0    
#    def f(r, dia):
#        return r * slope
#    return f
#
#    def init(angle):
#        slope = tan(((180-angle)/2) * pi / 180)

tool_makers = [ ball_tool, endmill, vee_common(30), vee_common(45), vee_common(60), vee_common(90)]

def make_tool_shape(f, wdia, resp, is_offset= False, tool_type=0, wdia2=-9999, degr=60):
    res = 1. / resp

    # if cell_center:  =-> 'smooth terrain, but can cut the edge'; 
    # elif 'cell_contact' in other words 'Touch a cell at the nearest point': =-> 'more accurate'
    #
    # if cell_coeff ==  0 -   0% - the same as 'cell contact'; 
    # if cell_coeff ==2/3 -  33% - this is '33% from cell contact'; 
    # if cell_coeff ==  1 -  50% - this is 'cell center'; 
    # if cell_coeff ==  2 - 100% - this is 'the far corner of the cell'; This is MAX of cell_coeff.
    cell_coeff = 2./3.

    # that there - dia - is always an odd
    #  because that more accurately
    always_an_odd = True

    dia = int(wdia*res)
    if dia/res < wdia: dia = dia +1
    if dia < 1: dia = 1
    
    if is_offset:
        if dia == 1: 
            dia = 2
            if prn_detal > 0: print "(Warning: offset[{0}] <= pixel size[{1}]! Use function correct_offset!)".format((wdia/2),resp)
            #this not enaf! Use function correct_offset - for correct offset!
            wdia = resp*2
    
    if always_an_odd and not dia % 2: dia = dia +1

    if is_offset and prn_detal > 0: print "(Real offset = {0} dia: {1} pixels)".format((dia*resp/2),dia)

    wrad = wdia/2.
    wrad0= wrad

    if wdia2 > wdia and degr > 0 and degr < 180:
        degr2 = (180-degr)/2
        
        f2 = vee_common(degr)
        if tool_type == 0:
            wrad0 = cos(radians(90-degr2))*wrad

            if wrad0 > wrad or wrad0 == 0 and prn_detal > -1: print("Error tool2(2): ",wrad0)
            if prn_detal > 0: print(" rad: ",wrad0)

        if   tool_type == 0: h0 = (wrad - sin(radians(90-degr2))*wrad) / sin(radians(90-degr2))
        elif tool_type == 1: h0 = wrad/tan(radians(degr/2))
        elif tool_type == 2: h0 = f2(wrad, wrad) - f(wrad, wrad)
        elif tool_type == 3: h0 = f2(wrad, wrad) - f(wrad, wrad)
        elif tool_type == 4: h0 = f2(wrad, wrad) - f(wrad, wrad)
        elif tool_type == 5: h0 = f2(wrad, wrad) - f(wrad, wrad)

        wrad2 = wdia2/2.
        dia2= int(wdia2*res+.5)
        if dia2/res < wdia2: dia2 = dia2 +1
    
        if always_an_odd and not dia2 % 2: dia2 = dia2 +1

        dia = max(dia,dia2)
    else:
        wrad2 = -plus_inf

    n = numarray.array([[plus_inf] * dia] * dia, type="Float32")
    hdia = dia / 2.
    #hdia = int(hdia)
    l = []

    for x in range(dia):
        for y in range(dia):
            if dia % 2 and x == dia/2 and y == dia/2:
                z = f(0, wrad)
                l.append(z)
                if z != 0.: 
                    if prn_detal > -1: print("Error(0) tool center mast be = 0, bat z=",z)
                    z = 0 
                n[x,y] = z  # z = 0 - mast be
            else:

                x1 = x-hdia
                y1 = y-hdia
                dopx2 = 0.
                dopy2 = 0.

                if x1 <= 0. and y1 < 0.:
                    if x1 == 0.: dopx = 0.
                    elif x1 >= -.5: dopx = 0.5
                    else: 
                        dopx = 1
                        dopx2 = -0.5

                    if y1 == 0.: dopy = 0.
                    elif y1 >= -.5: dopy = 0.5
                    else: 
                        dopy = 1
                        dopy2 = -0.5
                elif x1 <= 0.:
                    if x1 == 0.: dopx = 0.
                    elif x1 >= -.5: dopx = 0.5
                    else: 
                        dopx = 1
                        dopx2 = -0.5
                    dopy = 0
                    dopy2 = 0.5
                elif y1 < 0.:
                    dopx = 0
                    dopx2 = 0.5
                    if y1 == 0.: dopy = 0.
                    elif y1 >= -.5: dopy = 0.5
                    else: 
                        dopy = 1
                        dopy2 = -0.5
                else:
                    dopx = 0
                    dopy = 0
                    dopx2 = 0.5
                    dopy2 = 0.5

                dopx2 *= cell_coeff
                dopy2 *= cell_coeff
            
                r = hypot(x1+dopx+dopx2,   y1+dopy+dopy2)   * resp

                if r < wrad0:
                    z = f(r, wrad)
                    l.append(z)
                    n[x,y] = z
                    if z < 0. and prn_detal > -1: print(" tool error 1: r=",r," x=",x," y=",y," hight<0 hight= ",z,", mast be >= 0")
                    #if z > 300: print(" error 1: tool hight1: r=",r," x=",x," y=",y," hight= ",z," is too big!")
                elif r < wrad2:
                    z = f2(r,wrad2) - h0
                    l.append(z)
                    n[x,y] = z
                    if z < 0. and prn_detal > -1: print(" tool error 2: r=",r," x=",x," y=",y," z<0 z= ",z,", mast be >= 0")
                    #if z > 300: print(" error 2: tool hight1: r=",r," x=",x," y=",y," hight= ",z," is too big!")

    if n.min() != 0. and prn_detal > -1: print("error(0): tool.minimum = ",n.min(),", mast be == 0")
    return n

def correct_offset(offset, resp):
    if offset > epsilon and offset < resp: 
        if prn_detal > -1: print "(Warning: offset[{0}] <= pixel size[{1}]. New offset = {1} units)".format(offset,resp)
        offset = resp
    return offset

def optim_tool_shape(n,hig,offset,tolerance):

    #return n

    lineLF = -1
    minH = hig+offset+tolerance+.5
    P = True
    dia = len(n)
    while P and lineLF < (dia-1):
        lineLF = lineLF + 1
        #left
        for x in range(dia):
            if n[lineLF,x] < minH:
                P = False
                break

        if P:
            #up
            for y in range(dia):
                if n[y,lineLF] < minH:
                    P = False
                    break
    #@ dia = 3, lineLF = 1

    #lineLF = lineLF - 1

    if lineLF > 0:


        P = True
        line2 = dia
        while P and line2 >= lineLF:
            line2 = line2 - 1
            #right
            for x in range(dia):
                if n[line2,x] < minH:
                    P = False
                    break
            if P:
                #down
                for y in range(dia):
                    if n[y,line2] < minH:
                        P = False
                        break
        #@ dia = 3, line2 = 1
        line2 = line2 + 1

        if prn_detal > 0: print("tool opitimize - dia = ",dia," lines:")
        if prn_detal > 0: print(" cut lines[left  and up  ]: ",lineLF)
        if prn_detal > 0: print(" cut lines[right and down]: ",(dia-line2))

        if line2 < dia:
            dia2 = line2 - lineLF
            n2 = numarray.array([[plus_inf] * dia2] * dia2, type="Float32")
            d2range = range(lineLF,line2)
            for x in d2range:
                for y in d2range:
                    n2[x-lineLF,y-lineLF] = n[x,y]
        else:
            dia2 = dia - lineLF
            n2 = numarray.array([[plus_inf] * dia2] * dia2, type="Float32")
            d2range = range(lineLF,dia)
            for x in d2range:
                for y in d2range:
                    n2[x-lineLF,y-lineLF] = n[x,y]
        if len(n2) == 0 and prn_detal > -1: print("Error(): ",dia,lineLF,line2)
        if n2.min() <> 0 and prn_detal > -1: print("Error(): n2=",n2)
        return n2
    else:
        return n




def amax(seq):
    res = 0
    for i in seq:
        if abs(i) > abs(res): res = i
    return res

def group_by_sign(seq, slop=sin(pi/18), key=lambda x:x):
    sign = None
    subseq = []
    for i in seq:
        ki = key(i)
        if sign is None:
            subseq.append(i)
            if ki != 0:
                sign = ki / abs(ki)
        else:
            subseq.append(i)
            if sign * ki < -slop:
                sign = ki / abs(ki)
                yield subseq
                subseq = [i]
    if subseq: yield subseq

class Convert_Scan_Alternating:
    def __init__(self):
        self.st = 0

    def __call__(self, primary, items):
        st = self.st = self.st + 1
        if st % 2: items.reverse()
        if st == 1: yield True, items
        else: yield False, items

    def reset(self):
        self.st = 0

class Convert_Scan_Increasing:
    def __call__(self, primary, items):
        yield True, items

    def reset(self):
        pass

class Convert_Scan_Decreasing:
    def __call__(self, primary, items):
        items.reverse()
        yield True, items

    def reset(self):
        pass

class Convert_Scan_Upmill:
    def __init__(self, slop = sin(pi / 18)):
        self.slop = slop

    def __call__(self, primary, items):
        for span in group_by_sign(items, self.slop, operator.itemgetter(2)):
            if amax([it[2] for it in span]) < 0:
                span.reverse()
            yield True, span

    def reset(self):
        pass

class Convert_Scan_Downmill:
    def __init__(self, slop = sin(pi / 18)):
        self.slop = slop

    def __call__(self, primary, items):
        for span in group_by_sign(items, self.slop, operator.itemgetter(2)):
            if amax([it[2] for it in span]) > 0:
                span.reverse()
            yield True, span

    def reset(self):
        pass

class Reduce_Scan_Lace:
    def __init__(self, converter, slope, keep):
        self.converter = converter
        self.slope = slope
        self.keep = keep

    def __call__(self, primary, items):
        slope = self.slope
        keep = self.keep
        if primary:
            idx = 3
            test = operator.le
        else:
            idx = 2
            test = operator.ge

        def bos(j):
            return j - j % keep

        def eos(j):
            if j % keep == 0: return j
            return j + keep - j%keep

        for i, (flag, span) in enumerate(self.converter(primary, items)):
            subspan = []
            a = None
            for i, si in enumerate(span):
                ki = si[idx]
                if a is None:
                    if test(abs(ki), slope):
                        a = b = i
                else:
                    if test(abs(ki), slope):
                        b = i
                    else:
                        if i - b < keep: continue
                        yield True, span[bos(a):eos(b+1)]
                        a = None
            if a is not None:
                yield True, span[a:]

    def reset(self):
        self.converter.reset()

unitcodes = ['G20', 'G21']
convert_makers = [ Convert_Scan_Increasing, Convert_Scan_Decreasing, Convert_Scan_Alternating, Convert_Scan_Upmill, Convert_Scan_Downmill ]

def progress(a, b, cstr="FILTER_PROGRESS=%d"):
    if os.environ.has_key("AXIS_PROGRESS_BAR"):
        print >>sys.stderr, cstr % int(a*100./b+.5)
        sys.stderr.flush()

class Converter:
    def __init__(self,
            image, units, tool_shape, pixelsize, pixelstep, safetyheight, \
            tolerance, feed, convert_rows, convert_cols, cols_first_flag,
            entry_cut, spindle_speed, roughing_offset, roughing_depth,
            roughing_feed,invert,background_border,cut_top_jumper,
            dont_cut_angles,optimize_path,roughing_completed,
            max_bg_lan,pattern_objectiv,
            roughing_minus_finishing,pixelstep_roughing,tool_roughing,min_delta_rf,
            previous_offset):
        self.image = image
        self.units = units
        self.tool = tool_shape
        self.pixelsize = pixelsize
        self.pixelstep = pixelstep
        self.safetyheight = safetyheight
        self.tolerance = tolerance
        self.base_feed = feed
        self.convert_rows = convert_rows
        self.convert_cols = convert_cols
        self.cols_first_flag = cols_first_flag
        self.entry_cut = entry_cut
        self.spindle_speed = spindle_speed
        self.roughing_offset = roughing_offset
        self.previous_offset = previous_offset
        self.roughing_depth = roughing_depth
        self.roughing_feed = roughing_feed
        self.invert = invert
        self.background_border = background_border
        self.cut_top_jumper = cut_top_jumper
        self.dont_cut_angles = dont_cut_angles
        self.optimize_path = optimize_path
        self.roughing_completed = roughing_completed
        self.max_bg_lan = max_bg_lan
        self.pattern_objectiv = pattern_objectiv
        self.row_mill = self.convert_rows
        self.start_moment = datetime.datetime.now()

        self.roughing_offset = correct_offset(self.roughing_offset, self.pixelsize)
        self.previous_offset = correct_offset(self.previous_offset, self.pixelsize)

        self.delineating = True #False #
        self.layer = 0
        if self.invert:
            self.MaxBackground_down = self.background_border*(-1)
            self.MaxBackground_up = (self.image.min()+self.background_border)
        else:
            self.MaxBackground_down = (self.image.min()+self.background_border);
            self.MaxBackground_up = (self.image.max()-self.background_border);

        w, h = self.w, self.h = image.shape

        self.roughing_minus_finishing = roughing_minus_finishing
        if self.roughing_minus_finishing:
            self.pixelstep_roughing = pixelstep_roughing
            self.tool_roughing = tool_roughing
            self.min_delta_rf = min_delta_rf

            #I did not use the function 'get_RowCol' to quickly work out.
            if self.row_mill:
                self.map_tool2 = numarray.zeros((w, h), 'Float32') - plus_inf

                # map_rmf>=0 - True;  map_rmf<0 - False
                self.map_rmf = numarray.zeros((w, h), 'Float32') - plus_inf
            else:
                self.map_tool2 = numarray.zeros((h, w), 'Float32') - plus_inf

                # map_rmf>=0 - True;  map_rmf<0 - False
                self.map_rmf = numarray.zeros((h, w), 'Float32') - plus_inf
            self.map_tool2 = numarray.zeros((w, h), 'Float32') - plus_inf

            # map_rmf>=0 - True;  map_rmf<0 - False
            #self.map_rmf = numarray.zeros((w, h), 'Float32') - plus_inf

        self.cache = {}

        self.cache_abs = {}

        ts = self.ts = tool_shape.shape[0]
        if prn_detal > 0: print "(Tool shape = {0} pixels)".format(ts)

        # "-1" - for functions get_dz_dy and get_dz_dx - code "+1"
        self.h1 = h - ts -1
        self.w1 = w - ts -1

        self.ts2 = self.ts/2

        #self.tool_shape = tool_shape * self.pixelsize * ts / 2;
    
    def one_pass(self):
        g = self.g
        g.set_feed(self.feed)

        self.layer = self.layer + 1
        if prn_detal > 0: print "(Layer {0} layer height {1})".format(self.layer,self.rd)



        if self.convert_cols and self.cols_first_flag:
            self.row_mill = False
            self.g.set_plane(19)
            if self.roughing_minus_finishing:
                if prn_detal > 0: print "(Error(!) This function is not implemented!)"

            elif self.pattern_objectiv:
                self.mill_objectiv(self.convert_cols, True)
            else:
                self.mill_cols(self.convert_cols, True)
            if self.convert_rows: g.safety()

        if self.convert_rows:
            self.row_mill = True
            self.g.set_plane(18)
            if self.roughing_minus_finishing: 
                self.mill_roughing_minus_finishing(self.convert_rows, not self.cols_first_flag)

            elif self.pattern_objectiv:
                self.mill_objectiv(self.convert_rows, not self.cols_first_flag)
            else:
                self.mill_rows(self.convert_rows, not self.cols_first_flag)

        if self.convert_cols and not self.cols_first_flag:
            self.row_mill = False
            self.g.set_plane(19)
            if self.convert_rows: g.safety()
            if self.roughing_minus_finishing: 
                self.mill_roughing_minus_finishing(self.convert_cols, not self.convert_rows)

            elif self.pattern_objectiv:
                self.mill_objectiv(self.convert_cols, not self.convert_rows)
            else:
                self.mill_cols(self.convert_cols, not self.convert_rows)

        if self.convert_cols:
            self.convert_cols.reset()
        if self.convert_rows:
            self.convert_rows.reset()
        g.safety()


    def set_tool3(self,base_image,map_tool1,roughing_offset,tool_diameter,tool_type,pixelstep,roughing_depth,min_delta_rf):
        # map_tool3 = min(map_tool1,map_tool2)
        #testing only for "Rows"!!! "Cols" - not testing!!!

        jrange = range(self.w1)
        irange = range(self.h1)
        trange = range(0,self.ts)

        for y in jrange:    #lines image
            for x in irange:    #pixels image

                for i in trange:    #lines tool
                    for j in trange:    #pixels tool

                        ty = i+y
                        tx = j+x

                        map_tool1[ty,tx] = self.map_tool2[ty,tx]

        #clear caches
        self.map_rmf[:] = -plus_inf
        self.map_tool2[:] = -plus_inf
        self.cache.clear()
        self.cache_abs.clear()

        # new params and tool
        if self.roughing_offset != roughing_offset:
            self.roughing_offset = roughing_offset
            self.image = base_image
            self.image = self.make_offset(self.roughing_offset,self.image)

        self.tool = make_tool_shape(tool_type, tool_diameter, self.pixelsize)
        self.tool = optim_tool_shape(self.tool,-base_image.min(),self.roughing_offset,max(self.pixelsize,self.tolerance))

        self.pixelstep = pixelstep
        self.roughing_depth = roughing_depth
        self.min_delta_rf = min_delta_rf

        self.ts = self.tool.shape[0]
        if prn_detal > 0: print "(Next tool shape = {0} pixels)".format(self.ts)

        # "-1" - for functions get_dz_dy and get_dz_dx - code "+1"
        self.h1 = self.h - self.ts -1
        self.w1 = self.w - self.ts -1

        self.ts2 = self.ts/2
                        
        return map_tool1

    def make_offset(self,offset,image):
        if offset > epsilon16:
            rough = make_tool_shape(ball_tool,
                                2*offset, self.pixelsize, True)
            rough = optim_tool_shape(rough,-image.min(),offset,max(self.pixelsize,self.tolerance))
            w, h = image.shape
            tw, th = rough.shape
            w1 = w + tw
            h1 = h + th
            nim1 = numarray.zeros((w1, h1), 'Float32') + image.min()
            nim1[tw/2:tw/2+w, th/2:th/2+h] = image
            image = numarray.zeros((w,h), type="Float32")
            for j in range(0, w):
                progress(j,w)
                for i in range(0, h):
                    image[j,i] = (nim1[j:j+tw,i:i+th] - rough).max()
        return image

    def convert(self):
        self.g = g = Gcode(safetyheight=self.safetyheight,
                           tolerance=self.tolerance,
                           spindle_speed=self.spindle_speed,
                           units=self.units)
        g.begin()
        g.continuous(self.tolerance)
        g.safety()

        if prn_detal > 0: print "(Start make g-kod at {0})".format(datetime.datetime.now())
        if self.safetyheight == 0 and prn_detal > -1: 
            print "(Warning: safety height = 0! You can give error like: 'Start of arc is the same as end!')"

        if self.roughing_minus_finishing:

            if -self.MaxBackground_down <= self.roughing_depth and prn_detal > -1: 
                print "(Warning: roughing depth >= image high! G-kode can not be formed.')"

            base_image = self.image
            self.feed = self.roughing_feed
            
            offset_image = self.make_offset(self.previous_offset,self.image)

            self.rd = self.image.min()
            map_tool1 = self.get_rmf_map_tool(offset_image,self.tool_roughing,self.previous_offset,self.pixelstep_roughing)

            self.image = self.make_offset(self.roughing_offset,self.image)

            self.ro = self.roughing_offset
            self.set_rmf(base_image,map_tool1)
            #self.cache.clear()



            '''#----------------------------------------------------------------
            # map_tool3 = min(map_tool1,map_tool2)
            next_roughing_offset = 0.3
            next_tool_diameter = 1.5
            next_tool_type = ball_tool
            next_pixelstep = 1
            next_roughing_depth = 1.7
            next_min_delta_rf = 2.0 #0.0 #1.8 #
            map_tool1 = self.set_tool3(base_image,map_tool1,next_roughing_offset,next_tool_diameter,\
                                        next_tool_type,next_pixelstep,next_roughing_depth,next_min_delta_rf)
            self.set_rmf(base_image,map_tool1)
            #----------------------------------------------------------------'''


            '''#----------------------------------------------------------------
            # map_tool4 = min(map_tool2,map_tool3)
            next_roughing_offset = 0
            next_tool_diameter = 0.4
            next_tool_type = ball_tool
            next_pixelstep = 1
            next_roughing_depth = 0.4
            next_min_delta_rf = 1.2 #0.3 #
            map_tool1 = self.set_tool3(base_image,map_tool1,next_roughing_offset,next_tool_diameter,\
                                        next_tool_type,next_pixelstep,next_roughing_depth,next_min_delta_rf)
            self.set_rmf(base_image,map_tool1)
            #----------------------------------------------------------------'''
            

        elif (self.roughing_depth > epsilon16 and self.roughing_offset > epsilon16) or (self.roughing_depth > epsilon16 and self.optimize_path > epsilon16):
            base_image = self.image
            if not self.optimize_path:
                self.image = self.make_offset(self.roughing_offset,self.image)

            self.feed = self.roughing_feed
            m = self.image.min()
            if self.optimize_path:
                self.ro = 0.
            else:
                self.ro = self.roughing_offset

            if not self.optimize_path:
                r = -self.roughing_depth
            else:
                r = self.image.max()-self.ro

            while r > m:
                self.rd = r
                self.one_pass()
                r = r - self.roughing_depth
            if r < m + epsilon and not self.optimize_path:
                self.rd = m
                if prn_detal > 0: print "(Layer: {0} rd={1} m+epsilon={2})".format(self.layer,self.rd,(m + epsilon))
                self.one_pass()
            self.image = base_image
            self.cache.clear()
        self.feed = self.base_feed
        if self.roughing_minus_finishing:
            self.ro = self.roughing_offset
        else:
            self.ro = 0.
        self.rd = self.image.min()
        self.one_pass()
        g.end()
        if prn_detal > 0: print "(End make at {0} maked from {1})".format(datetime.datetime.now(),(datetime.datetime.now()-self.start_moment))

        
    def get_dz_dy(self, x, y):
        y1 = max(0, y-1)
        y2 = min(self.image.shape[0]-1, y+1)
        #y2 = min(self.image.shape[0]-1-self.ts, y+1)
        dy = self.pixelsize * (y2-y1)
        return ((self.get_z(x, y2)) - (self.get_z(x, y1))) / dy
        
    def get_dz_dx(self, x, y):
        x1 = max(0, x-1)
        x2 = min(self.image.shape[1]-1, x+1)
        #x2 = min(self.image.shape[1]-1-self.ts, x+1)
        dx = self.pixelsize * (x2-x1)
        return ((self.get_z(x2, y)) - (self.get_z(x1, y))) / dx

    def get_z(self, x, y):

        try:
            return min(0, max(self.rd, self.cache[x,y])) + self.ro
        except KeyError:
            m1 = self.image[y:y+self.ts, x:x+self.ts]
            #if you get ERROR in line below - this means 
            #   that Y or X LARGER than the size of image! Probably Y its X and X its Y... 
            self.cache[x,y] = d = (m1 - self.tool).max()
            return min(0, max(self.rd, d)) + self.ro


    def get_RowCol(self, x, y):
        if self.row_mill:
            return x,y
        else:
            return y,x

    def get_z_abs(self, x, y):
        try:
            return min(0, self.cache_abs[x,y])
        except KeyError:
            m1 = self.image[y:y+self.ts, x:x+self.ts]
            #if you get ERROR in line below - this means 
            #   that Y or X LARGER than the size of image! Probably Y its X and X its Y... 
            d = (m1 - self.tool).max()
            self.cache_abs[x,y] = d
            return min(0,d)

    def get_rmf_map_tool(self,offset_image,tool_roughing,previous_offset,pixelstep_roughing):

        #Dictionary faster then array
        map_tool1 = {}  #numarray.zeros((self.w, self.h), 'Float32') - self.image.min()

        ts_roughing = tool_roughing.shape[0]
        if prn_detal > 0: print "(Tool shape roughing = {0} pixels)".format(ts_roughing)

        if self.row_mill:
            jrange = range(0, self.w - ts_roughing - 1, pixelstep_roughing)
            irange = range(0, self.h - ts_roughing - 1)
            ln = self.w - ts_roughing - 1
        else:
            jrange = range(0, self.h - ts_roughing - 1, pixelstep_roughing)
            irange = range(0, self.w - ts_roughing - 1)
            ln = self.h - ts_roughing - 1

            
        trange = range(0,ts_roughing)

        for ry in jrange:    #lines
            progress(ry, ln)
            for rx in irange:    #pixels

                if self.row_mill:
                    y,x = ry,rx
                else:
                    x,y = ry,rx

                m1 = offset_image[y:y+ts_roughing, x:x+ts_roughing]
                hhh1 = (m1 - tool_roughing).max() + previous_offset

                for i in trange:    #lines tool
                    for j in trange:    #pixels tool

                        t = tool_roughing[i,j]
                        if isinf(t): continue

                        ty = i+y
                        tx = j+x
                        # self.image[ty,tx] <= hhh1 !!! t >= 0
                        dt = -self.image[ty,tx] + hhh1 + t
                        #dt = round(dt,16)

                        if dt < -epsilon and prn_detal > -1: print(" delta < -0.00001 ",ty,tx,dt,self.image[ty,tx], hhh1, t)
                        if dt < .0: dt = .0
                            
                        try:
                            if map_tool1[ty,tx] > dt: 
                                map_tool1[ty,tx] = dt
                        except KeyError:
                            map_tool1[ty,tx] = dt

        if prn_detal > 0: print "(End make map tool1 at {0})".format(datetime.datetime.now())

        return map_tool1

    def set_rmf(self,base_image,map_tool1):

        #map_tool2 = {}

        if self.row_mill:
            jrange = range(0, self.w1, self.pixelstep)
            irange = range(0, self.h1)
            ln = self.w1
        else:
            jrange = range(0, self.h1, self.pixelstep)
            irange = range(0, self.w1)
            ln = self.h1

        trange = range(0,self.ts)

        for lin in jrange:    #lines
            progress(lin, ln)
            for pix in irange:    #pixels

                if self.row_mill:
                    y,x = lin,pix
                else:
                    x,y = lin,pix

                hhh1 = self.get_z(x,y)

                founded = False

                for i in trange:    #lines tool
                    for j in trange:    #pixels tool

                        t = self.tool[i,j]
                        if isinf(t): continue

                        ty = i+y
                        tx = j+x
                        #dt - without offset (base_image != self.image)!
                        dt = -base_image[ty,tx] + hhh1 + t      #-base_image[ty,tx] + min(0.0, hhh1 + t)
                        #dt = round(dt,16)
                        
                        if dt < -epsilon and prn_detal > -1: 
                            print "( delta[{0}] < -0.00001 y,x[{1}, {2}] image={3} hhh1={4} tool={5})".format(dt,ty,tx,base_image[ty,tx], hhh1, t)
                            if dt < .0: dt = .0
                        
                        if isinf(self.map_tool2[ty,tx]) or self.map_tool2[ty,tx] > dt: 
                            self.map_tool2[ty,tx] = dt
                        
                        try:
                            # "dt" must be >= "map_tool1", but not always
                            delta = map_tool1[ty,tx] - dt
                            
                            if delta >= self.min_delta_rf:
                                #find minimum of delta
                                if isinf(self.map_rmf[lin,pix]) or delta >= self.map_rmf[lin,pix]:  #(y,x)!!!  Not (ty,tx)!!!
                                    #self.map_rmf[lin,pix] = delta
                                    if self.min_delta_rf == 0 and delta == 0:
                                        if self.not_background(hhh1-self.roughing_offset,lin,pix):
                                            self.map_rmf[lin,pix] = epsilon16
                                        #else: self.map_rmf[lin,pix] = -1
                                    else:
                                        self.map_rmf[lin,pix] = delta

                            elif self.min_delta_rf == 0 and delta < 0 and isinf(self.map_rmf[lin,pix]):
                                #self.map_rmf[lin,pix] = epsilon16

                                if self.not_background(hhh1-self.roughing_offset,lin,pix):
                                    self.map_rmf[lin,pix] = epsilon16
                                else:  
                                    self.map_rmf[lin,pix] = -1


                        except: pass

        if prn_detal > 0: print "(Max delta 'Roughing Minus Finish': {0} units)".format(self.map_rmf.max())

        if self.map_rmf.max() < 0 and prn_detal > -1: print "(Error() Min RMF: {0} < 0 units)".format(self.map_rmf.max())
        if prn_detal > 0: print "(End make map rmf at {0})".format(datetime.datetime.now())

        return



    #+++++++++++++++++++++++++++++++++++++++++++++++++++++++
    def pixel_use(self, hhh1,y,x):

        if self.roughing_minus_finishing:
            if hhh1-self.roughing_depth > self.rmd or (hhh1 + self.map_rmf[y,x]) < self.rmd:
                return False
            else:
                return True #Dont remove this line!

        if self.roughing_completed and hhh1 <= self.rd:
            return False

        return self.not_background(hhh1,y,x)

    def not_background(self, hhh1,y,x):

        if self.invert:
            if hhh1 <= self.MaxBackground_down\
                and (not self.cut_top_jumper or hhh1 >= self.MaxBackground_up):
                return True
        else:
            if hhh1 >= self.MaxBackground_down \
                and (not self.cut_top_jumper or hhh1 <= self.MaxBackground_up):
                return True
        return False

    def not_processed_prev(self, hhh1,y,x):
        if self.layer == 1 or not self.optimize_path: 
            return True
        elif hhh1 <= (self.rd+self.roughing_depth): # +self.ro
        #elif (hhh1-epsilon) <= (self.rd+self.roughing_depth): # +self.ro
            return True
        return False

    def not_processed_cur(self, y,x,processed_cur):
        lin,pix = self.get_RowCol(y,x)
        try:
            d= processed_cur[y,x]   #777
            return False
        except KeyError:
            return True

    def gk_maker(self, make_gk, max_pix, processed_cur, convert_scan, primary,object_num,layer=-1):
        if layer == -1: clayer = self.layer  #from 1 - len
        else: clayer = layer                 #from 0 - (len-1) - self.roughing_minus_finishing
        d_len = 1
        lines = sorted(make_gk)
        ld = len(make_gk)

        convert_scan.reset()    #???

        if not self.row_mill: lines.reverse()
        for j in lines:
            progress(j, ld)
            Pixel_entry_f,Pixel_entry_l = make_gk[j]
                        
            #can't make g-code from 1 pixel
            P_f = max(0, Pixel_entry_f - d_len)
            P_l = min(max_pix, Pixel_entry_l + d_len+1)    #self.h1-1
            #if d_len == 0: P_l = min(self.h1,Pixel_entry_l + 1)

            if prn_detal > 1: 
                if self.roughing_minus_finishing:
                    print "( GK> Layer: {0} obj: {1} RD[{2}] row: {3} [F{4}, L{5}] [{6}, {7}] RD: {8})".format(layer,object_num,\
                            self.rd,j,Pixel_entry_f,Pixel_entry_l,P_f,P_l,self.rd) 

                else:
                    print "( GK> Layer: {0} obj: {1} RD[{2}] row: {3} [F{4}, L{5}] [{6}, {7}] )".format(self.layer,object_num,\
                            self.rd,j,Pixel_entry_f,Pixel_entry_l,P_f,P_l) 

            # clean covered
            for i in range(Pixel_entry_f,Pixel_entry_l+1): processed_cur[j,i] = 7

            scan = []
            #I did not use the function 'get_RowCol' to quickly work out.
            if self.row_mill:
                #Rows
                y = (self.w1-j+self.ts2) * self.pixelsize
                for i in range(P_f,P_l):

                    x = (i+self.ts2) * self.pixelsize
                    milldata = (i, (x, y, self.get_z(i, j)),
                        self.get_dz_dx(i, j), self.get_dz_dy(i, j))
                    scan.append(milldata)

                # make g-code
                if len(scan) != 0:
                    for flag, points in convert_scan(primary, scan):
                        if flag:
                            self.entry_cut(self, points[0][0], j, points)   #ArcEntryCut
                        for p in points:
                            self.g.cut(*p[1])
                else:
                    if prn_detal > -1: print "Error(0) g-kode not make L: {0} [{1}, {2}] !!!".format(clayer,P_f,P_l)
            else:
                #Cols
                x = (j+self.ts2) * self.pixelsize
                scan = []
                for i in range(P_f,P_l):
                    y = (self.w1-i+self.ts2) * self.pixelsize
                    milldata = (i, (x, y, self.get_z(j, i)),
                      self.get_dz_dy(j, i), self.get_dz_dx(j, i))
                    scan.append(milldata)

                # make g-code
                if len(scan) != 0:
                    for flag, points in convert_scan(primary, scan):
                        if flag:
                            self.entry_cut(self, j, points[0][0], points)
                        for p in points:
                            self.g.cut(*p[1])
                else:
                    if prn_detal > -1: print(clayer,"Error len(scan) == 0! g-kode not make from ",P_f," to ",P_l," !!!")
            self.g.flush()
            
        self.g.safety()
        convert_scan.reset()

        if prn_detal > 1: print "(End of object: g.safety, convert_rows.reset )"

    #+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

    def get_entry_pixels_go_end(self, start, stop, j, processed_cur,min_pix,max_pix):
        Pixel_entry_f = -1
        Pixel_entry_l = -1
        Blen = 0;

        y,x = self.get_RowCol(j,start)
        hhh1 = self.get_z(x,y)
        Curn_P = self.pixel_use(hhh1,j,start)
        hhh2 = hhh1
        Next_P = Curn_P


        First_border = False
        Last_border  = False
        verify_prev = True  #not entry first/last
        verify_curn = True  #not entry last /right
        if self.delineating:
            t_i = start-1   #prev pixel
            if t_i >= min_pix:
                y,x = self.get_RowCol(j,t_i)
                hhh3 = self.get_z(x,y)
                Prev_P  = self.pixel_use(hhh3,j,t_i)
            else:
                hhh3 = -777
                Prev_P  = False

            if Curn_P != Prev_P: 
                if Prev_P: 
                    if prn_detal > 9: print("  [e.0] first border ",Curn_P,Prev_P,t_i,j,hhh3)
                    verify_prev = False
                    Last_border = True
                elif Curn_P: 
                    if prn_detal > 9: print("  [e.0] last border ",Curn_P,Prev_P,t_i,j,hhh3)
                    verify_curn = False
                    First_border = True
        
        for i in range(start,max_pix+1):    #pixels
            next_i = i+1
            if next_i <= max_pix:
                y,x = self.get_RowCol(j,next_i)
                hhh2 = self.get_z(x,y)
                Next_P  = self.pixel_use(hhh2,j,next_i)
            else:
                Next_P  = False

            if self.delineating: 
                if Next_P and Curn_P != Next_P: 
                    verify_prev = False
                    First_border = True
                    if prn_detal > 9: print("  [e] first border ",Curn_P,Next_P,next_i,j,hhh2)
                else: verify_prev = True
                if Curn_P and Curn_P != Next_P: 
                    verify_curn = False
                    Last_border = True
                    if prn_detal > 9: print("  [e] last border ",Curn_P,Next_P,i,j,hhh1)

            Cur_tmp = False
            if Curn_P and self.not_processed_cur(j,i,processed_cur):
                Cur_tmp = Curn_P

                if not self.delineating: 
                    Cur_tmp = self.not_processed_prev(hhh1,j,i)
                else:
                    if verify_curn: 
                        if Curn_P == Next_P:    #Curn_P or not Next_P
                            Cur_tmp = self.not_processed_prev(hhh1,j,i)
                        elif prn_detal > 9: print("  [e] dont verify prev layer ",Curn_P,Next_P,i,j,hhh1)

            if Cur_tmp:
                Pixel_entry_l = i

                if Pixel_entry_f == -1:
                    #if first
                    Pixel_entry_f = i

                Blen = 0
            else:
                if i > stop:
                    # kogda roughing_minus_finishing i "delta" - malen'kaya - to g-kod ne na vseh sloyah formiruetsa
                    #elif i > stop or (self.roughing_minus_finishing and start != 0): Blen = Blen + 1
                    if Pixel_entry_f != -1: Blen = Blen + 1
                    else: break
                else:
                    if Pixel_entry_f != -1: Blen = Blen + 1

                if Blen > self.max_bg_lan: break

            hhh1 = hhh2
            Curn_P = Next_P
            verify_curn = verify_prev
        return Pixel_entry_f, Pixel_entry_l, First_border, Last_border

    def get_entry_pixels_go_start(self, start, stop, j, processed_cur,min_pix,max_pix):
        Pixel_entry_f = -1
        Pixel_entry_l = -1
        Blen = 0;

        y,x = self.get_RowCol(j,start)
        hhh1 = self.get_z(x,y)
        Curn_P = self.pixel_use(hhh1,j,start)
        hhh2 = hhh1
        Next_P = Curn_P

        First_border = False
        Last_border = False
        verify_prev = True
        verify_curn = True
        if self.delineating:
            t_i = start+1   #prev pixel
            if t_i <= max_pix:
                y,x = self.get_RowCol(j,t_i)
                hhh3 = self.get_z(x,y)
                Prev_P  = self.pixel_use(hhh3,j,t_i)
            else:
                hhh3 = -777
                Prev_P  = False

            if Curn_P != Prev_P: 
                if Prev_P: 
                    verify_prev = False
                    First_border = True
                    if prn_detal > 9: print("  [l.0] first border ",Curn_P,Prev_P,t_i,j,hhh3)
                elif Curn_P: 
                    verify_curn = False
                    Last_border = True
                    if prn_detal > 9: print("  [l.0] last border ",Curn_P,Prev_P,t_i,j,hhh3)

        for i in range(start, min_pix-1, -1):   #pixels - go to the left

            next_i = i-1
            if next_i >= 0:
                y,x = self.get_RowCol(j,next_i)
                hhh2 = self.get_z(x,y)
                Next_P = self.pixel_use(hhh2,j,next_i)
            else:
                Next_P = False

            if self.delineating: 
                if Next_P and Curn_P != Next_P: 
                    verify_prev = False
                    Last_border = True
                    if prn_detal > 9: print("  [l] first border ",Curn_P,Next_P,i-1,j,hhh2)
                else: verify_prev = True
                if Curn_P and Curn_P != Next_P: 
                    verify_curn = False
                    First_border = True
                    if prn_detal > 9: print("  [l] last border ",Curn_P,Next_P,i,j,hhh1)

            Cur_tmp = False
            if Curn_P and self.not_processed_cur(j,i,processed_cur):
                Cur_tmp = Curn_P

                if not self.delineating: 
                    Cur_tmp = self.not_processed_prev(hhh1,j,i)
                else:
                    if verify_curn: 
                        if Curn_P == Next_P:    #Curn_P or not Next_P
                            Cur_tmp = self.not_processed_prev(hhh1,j,i)
                        elif prn_detal > 9: print("  [l] dont verify prev layer ",Curn_P,Next_P,i,j)

            if Cur_tmp:
                #find first
                Pixel_entry_f = i

                if Pixel_entry_l == -1:
                    #if last
                    Pixel_entry_l = i

                Blen = 0
            else:
                if i < stop:
                    # when roughing_minus_finishing and "delta" is small - g-kod ne na vseh sloyah formiruetsa
                    #elif i < stop or (self.roughing_minus_finishing and start != 0): Blen = Blen + 1
                    if Pixel_entry_l != -1: Blen = Blen + 1 #countdown
                    else: break
                else:
                    if Pixel_entry_l != -1: Blen = Blen + 1 #countdown

                if Blen > self.max_bg_lan: break
            hhh1 = hhh2
            Curn_P = Next_P
            verify_curn = verify_prev
        return Pixel_entry_f, Pixel_entry_l, First_border, Last_border

    def get_dict_gk(self,processed_cur,line_cache,object_num,mas_obj_filter):
        make_gk = {}
        mas_p = (-1,-1)
        object_is_found = False

        #dictionary of GK is make without ro and rd
        temp_ro,temp_rd = self.ro,self.rd   #remember the ro and rd
        self.ro = 0.0                       #This nead for get_z!
        self.rd = self.image.min()          #This nead for get_z!

        Pixel_entry_f = -1
        Pixel_entry_l = -1
        Pixel_entry_f_prev = -1
        Pixel_entry_l_prev = -1
        Pixel_entry_f_next = -1
        Pixel_entry_l_next = -1
        First_border = False
        Last_border = False
        ld = len(mas_obj_filter)
        gk_rows = mas_obj_filter.keys()
        gk_rows.sort()  #this importent! Don't remove!

        Prev_line = -1
        st = 1
        if not self.row_mill: gk_rows.reverse()

        for j in gk_rows:   #lines
            progress(j, ld)

            min_pix,max_pix = mas_obj_filter[j]

            #if j >= 50 and object_num > 3: return

            if Pixel_entry_f == -1:
                # find Pixel entry
                #*************************************************************************************************************************************************
                Pixel_entry_f,Pixel_entry_l,First_border,Last_border = self.get_entry_pixels_go_end(min_pix, max_pix, j, processed_cur,min_pix,max_pix)
                #*************************************************************************************************************************************************

                if not object_is_found:
                    if self.row_mill:
                        if line_cache < j: line_cache = j
                    else:
                        if line_cache > j: line_cache = j

                if prn_detal > 3: print "( f-d entry rezult: L{0} obj{1} row{2} PL{3} border[f{4}, l{5}] [F{6},L{7}])".format(\
                                    self.layer,object_num,j,Prev_line,First_border,Last_border,Pixel_entry_f,Pixel_entry_l) 
            else:

                Pixel_entry_f_next = -1
                Pixel_entry_l_next = -1

                if (Pixel_entry_f > max_pix) or (min_pix > Pixel_entry_l):
                    #end of object
                    pass
                elif (st % 2) and First_border:
                    # fist - go left from Pixel_entry_f - then - right
                    if prn_detal > 3: print "( f-d from First border: L{0} obj{1} row{2} PL{3} border[f{4}, l{5}] fr[{8}, {9}] [F{6}, L{7}])".format(\
                                        self.layer,object_num,j,Prev_line,First_border,Last_border,Pixel_entry_f,Pixel_entry_l,min_pix,max_pix)    
                    
                    CurF = max(Pixel_entry_f,min_pix)
                    CurL = min(Pixel_entry_l,max_pix)

                    # left ****************************************************************************************************************************************
                    Pixel_entry_f_next,Pixel_entry_l_next, First_border, Last_border = self.get_entry_pixels_go_start(CurF,CurF , j, processed_cur,min_pix,max_pix)
                    #*******************************************************************************************************************************************


                    # go to the left  from Pixel_entry_f
                    # right **************************************************************************************************************************
                    if Pixel_entry_l_next == -1:
                        start_from = CurF
                        if prn_detal > 4: print "( -Wasn't f-d: border[f{0}, l{1}] Next[F{2}, L{3}] start[{4}])".format(\
                                            First_border,Last_border, Pixel_entry_f_next, Pixel_entry_l_next,start_from)
                    else:
                        start_from = Pixel_entry_l_next
                        if prn_detal > 4: print "( + Found: border[f{0}, l{1}] Next[F{2}, L{3}] start[{4}])".format(\
                                            First_border,Last_border, Pixel_entry_f_next, Pixel_entry_l_next,start_from)


                    tPixel_entry_f,Pixel_entry_l_next,tFirst_border,tLast_border = self.get_entry_pixels_go_end(\
                        start_from, CurL, j, processed_cur,min_pix,max_pix)

                    if Pixel_entry_f_next == -1: Pixel_entry_f_next = tPixel_entry_f

                    First_border = (tFirst_border or First_border)
                    Last_border  = (tLast_border or Last_border)

                    if prn_detal > 2: print "(f-d from First border rezult: L{0} obj{1} row{2} border[f{3}, l{4}] Next[F{5}, L{6}])".format(\
                                        self.layer,object_num,j,First_border,Last_border, Pixel_entry_f_next, Pixel_entry_l_next)
                    #**************************************************************************************************************************
                else:
                    # fist - go right - then - left
                    if prn_detal > 3: print "( f-d from Last border: L{0} obj{1} row{2} PL{3} border[f{4}, l{5}] fr[{8}, {9}] [{6}, {7}])".\
                                        format(self.layer,object_num,j,Prev_line,First_border,Last_border,Pixel_entry_f,Pixel_entry_l,min_pix,max_pix)    
                    
                    CurF = max(Pixel_entry_f,min_pix)
                    CurL = min(Pixel_entry_l,max_pix)

                    #**************************************************************************************************************************
                    Pixel_entry_f_next,Pixel_entry_l_next,First_border,Last_border = self.get_entry_pixels_go_end(CurL, CurL, j,\
                                                                                                         processed_cur,min_pix,max_pix)
                    #**************************************************************************************************************************

                    # go to the left  from Pixel_entry_l
                    #**************************************************************************************************************************
                    if Pixel_entry_f_next == -1:
                        start_from = CurL
                        if prn_detal > 4: print "( -Wasn't f-d: border[f{0}, l{1}] Next[F{2}, L{3}] start[{4}])".format(\
                                            First_border,Last_border, Pixel_entry_f_next, Pixel_entry_l_next,start_from)
                    else:
                        start_from = Pixel_entry_f_next
                        if prn_detal > 4: print "( + Found: border[f{0}, l{1}] Next[F{2}, L{3}] start[{4}])".format(\
                                            First_border,Last_border, Pixel_entry_f_next, Pixel_entry_l_next,start_from)


                    Pixel_entry_f_next, tPixel_entry_l, tFirst_border, tLast_border = self.get_entry_pixels_go_start(\
                        start_from,CurF, j, processed_cur,min_pix,max_pix)

                    if Pixel_entry_l_next == -1: Pixel_entry_l_next = tPixel_entry_l

                    First_border = (tFirst_border or First_border)
                    Last_border  = (tLast_border or Last_border)

                    if prn_detal > 2: print "(f-d from Last border rezult: L{0} obj{1} row{2} border[f{3}, l{4}] Next[{5}, {6}])".format(\
                                            self.layer,object_num,j, First_border,Last_border, Pixel_entry_f_next, Pixel_entry_l_next)
                    #**************************************************************************************************************************

                #if (Pixel_entry_f_next == -1 or Pixel_entry_l_next == -1) and (Pixel_entry_f_next != -1 or Pixel_entry_l_next != -1):
                #    print "Error() L{0} obj{1} row{2} Next[{3}, {4}]".format(self.layer,object_num,j, Pixel_entry_f_next, Pixel_entry_l_next)


                Pixel_entry_f_next,Pixel_entry_l_next = self.chek_gr(Pixel_entry_f_next,Pixel_entry_l_next,min_pix,max_pix)

                if self.dont_cut_angles:
                    if prn_detal > 8: 
                        print "( dont_cut_angles r:{0} F prev[{3}] current[{1}] next[{2}])".format(Prev_line,Pixel_entry_f,Pixel_entry_f_next,Pixel_entry_f_prev)
                        print "( dont_cut_angles r:{0} L prev[{3}] current[{1}] next[{2}])".format(Prev_line,Pixel_entry_l,Pixel_entry_l_next,Pixel_entry_l_prev)
                    if st % 2:
                        if Pixel_entry_l_prev  > Pixel_entry_l: 
                            if prn_detal > 2: print "( dont_cut_angles r:{0} l_prev:{1} -> l:{2})".format(Prev_line,Pixel_entry_l_prev,Pixel_entry_l)
                            Pixel_entry_l = Pixel_entry_l_prev
                        if Pixel_entry_f_next != -1 and Pixel_entry_f_next < Pixel_entry_f: 
                            if prn_detal > 2: print "( dont_cut_angles r:{0} f_next:{1} -> f:{2})".format(Prev_line,Pixel_entry_f_next,Pixel_entry_f)
                            Pixel_entry_f = Pixel_entry_f_next
                    else:
                        if Pixel_entry_l_next  > Pixel_entry_l: 
                            if prn_detal > 2: print "( dont_cut_angles r:{0} l_next:{1} -> l:{2})".format(Prev_line,Pixel_entry_l_next,Pixel_entry_l)
                            Pixel_entry_l = Pixel_entry_l_next
                        if Pixel_entry_f_prev !=-1 and Pixel_entry_f_prev < Pixel_entry_f: 
                            if prn_detal > 2: print "( dont_cut_angles r:{0} f_prev:{1} -> f:{2})".format(Prev_line,Pixel_entry_f_prev,Pixel_entry_f)
                            Pixel_entry_f = Pixel_entry_f_prev

                # make g-code from previous entrys
                if Pixel_entry_f != -1 and Pixel_entry_l != -1:
                    object_is_found = True
                    
                    if st == 1: mas_p = (Prev_line,Pixel_entry_l)   #point of entry - always Last
                    st = st + 1

                    make_gk[Prev_line] = (Pixel_entry_f,Pixel_entry_l)

                    if prn_detal > 1: 
                        print "( Add to dict GK: L{0} obj{1} row{2} [F{3}, L{4}] st={5})".format(self.layer,object_num,\
                            Prev_line,Pixel_entry_f,Pixel_entry_l,(st-1)%2)   #,P_f,P_l,self.ts) 
                    if prn_detal > 2: print " "

                elif Pixel_entry_f != -1 or Pixel_entry_l != -1: 
                    if prn_detal > -1: print("ERROR (*)******************",self.layer," row: ",j, Pixel_entry_f, Pixel_entry_l)

                Pixel_entry_f_prev = Pixel_entry_f
                Pixel_entry_l_prev = Pixel_entry_l
                Pixel_entry_f = Pixel_entry_f_next
                Pixel_entry_l = Pixel_entry_l_next

                if object_is_found and Pixel_entry_f == -1 and Pixel_entry_l == -1: 
                    if prn_detal > 1: print "( Don't find Next[F{3}, L{4}] in row:{2})".format(self.layer,object_num,j, Pixel_entry_f,Pixel_entry_l)
                    break

            Pixel_entry_f,Pixel_entry_l = self.chek_gr(Pixel_entry_f,Pixel_entry_l,min_pix,max_pix)
            Prev_line = j

        # make g-code from last entrys for last row.
        if Pixel_entry_f != -1 and Pixel_entry_l != -1:
            object_is_found = True
            
            if self.dont_cut_angles:
                if st % 2:
                    if Pixel_entry_l_prev  > Pixel_entry_l: 
                        if prn_detal > 2: print "( dont_cut_angles r:{0} l_prev:{1} -> l:{2})".format(j,Pixel_entry_l_prev,Pixel_entry_l)
                        Pixel_entry_l = Pixel_entry_l_prev
                else:
                    if Pixel_entry_f_prev !=-1 and Pixel_entry_f_prev < Pixel_entry_f: 
                        if prn_detal > 2: print "( dont_cut_angles r:{0} f_prev:{1} -> f:{2})".format(j,Pixel_entry_f_prev,Pixel_entry_f)
                        Pixel_entry_f = Pixel_entry_f_prev

            if st == 1: mas_p = (j,Pixel_entry_l)   #point of entry - always Last

            make_gk[j] = (Pixel_entry_f,Pixel_entry_l)

            if prn_detal > 1: print "( Add to dict GK_: L{0} obj{1} row{2} [F{3}, L{4}] st={5})".format(\
                                self.layer,object_num,j,Pixel_entry_f,Pixel_entry_l,st%2)   #,P_f,P_l,self.ts) 

        elif Pixel_entry_f != -1 or Pixel_entry_l != -1: 
            if prn_detal > -1: print("ERROR (**)*****************",self.layer," row: ",j, Pixel_entry_f, Pixel_entry_l)

        if prn_detal > 1:
            if object_is_found: 
                print "(End of obj: {0} rows: {1} entry point [{2}, {3}])".format(object_num,len(make_gk),mas_p[0],mas_p[1]) 
            else:
                print "(Object not found.)"

        #dictionary of GK is make without ro and rd
        self.ro,self.rd = temp_ro,temp_rd    #restore the ro and rd

        return make_gk,line_cache,object_is_found,mas_p

    def chek_gr(self,first,last,min_pix,max_pix):
        if (first == -1) and (min_pix == -1):
            return first,last
        elif (first > max_pix) or (min_pix > last):
            #end of object
            first = -1
            last  = -1
            return first,last

        if first > max_pix:
            #suda ne doljno zaiti!!!!
            if prn_detal > -1: print("Error logik(!) Pixel_entry_f_next > max_pix: ",first, ">", max_pix,"layer",self.layer," obj",object_num," R",j)
            first = max_pix
        elif first < min_pix:
            #suda ne doljno zaiti!!!!
            if prn_detal > -1: print("Error logik(!) Pixel_entry_f_next < min_pix: ",first, "<", min_pix,"layer",self.layer," obj",object_num," R",j)
            first = min_pix

        if max_pix < last:
            #suda ne doljno zaiti!!!!
            if prn_detal > -1: print("Error logik(!) Pixel_entry_l_next > max_pix: ",last, ">", max_pix,"layer",self.layer," obj",object_num," R",j)
            last = max_pix
        elif min_pix > last:
            #suda ne doljno zaiti!!!!
            if prn_detal > -1: print("Error logik(!) Pixel_entry_l_next > min_pix: ",last, "<", min_pix,"layer",self.layer," obj",object_num," R",j)
            last = min_pix

        return first,last

    def find_next_e(self, mas_p,y1,x1,max_y,max_x):
        max_ = max(max_y,max_x)

        i = 0
        if prn_detal > 1: print("start the search next entry from [",y1,x1,"]")

        while True:

            if i > max_:
                if prn_detal > -1: print("BREAK: LOGIK ERROR(0): ",i)
                break

            ww2 = {}
            i = i + 1

            #  _____
            # |     |
            # |  *  |
            # |_____|
            xrang_r  = range(max(x1-i  ,0),     min(x1+i+1,max_x+1)     )
            xrang_l  = range(min(x1+i,max_x),   max(x1-i-1,-1),       -1)
            yrang_up = range(max(y1-i+1,0),     min(y1+i  ,max_y+1)     )
            yrang_dn = range(min(y1+i-1,max_y), max(y1-i  ,-1),       -1)

            num = 0
            if y1-i >= 0:
                #if y1 == 23 and x1 == 0: print("1-",y1-i,xrang_r)
                for m in xrang_r:
                    num = num + 1
                    ww2[num] = (y1-i,m)
            if x1+i <= max_x:
                #if y1 == 23 and x1 == 0: print("2-",x1+i,yrang_up)
                for m in yrang_up:
                    num = num + 1
                    ww2[num] = (m,x1+i)
            if y1+i <= max_y:
                #if y1 == 23 and x1 == 0: print("3-",y1+i,xrang_l)
                for m in xrang_l:
                    num = num + 1
                    ww2[num] = (y1+i,m)
            if x1-i >= 0:
                #if y1 == 23 and x1 == 0: print("4-",x1-i,yrang_dn)
                for m in yrang_dn:
                    num = num + 1
                    ww2[num] = (m,x1-i)


            if len(ww2) == 0:
                if prn_detal > -1: 
                    print("BREEEEEAK: LOGIK ERROR(!): ",i,len(mas_p),max_x,max_y,y1-i,y1+i,x1-i,x1+i)
                    print(mas_p)
                    #print(ww2)                
                break

            # cheking
            tmm = ww2.keys()
            tmm.sort()
            for num in tmm:
                y,x = ww2[num]
                try:
                    m = mas_p[y,x]
                    if prn_detal > 2: print(" find next entry in [",y,x,"] obj: ",m," iter: ",i)
                    return y,x
                except KeyError:
                    m = 0

        return y1,x1
        

        


    def BinSearchVirt(self,li, x):
        i = 0
        j = len(li)-1
        while i < j:
            m = int((i+j)/2)
            if x > li[m]:
                i = m+1
            else:
                j = m
        #here it does not matter j or i
        if li[j] == x:
            return True
        else:
            return False

    def objects_relate_to(self,obj_rt,rw,first, last):
        mas_rows = obj_rt.keys()   #[59, 60, 61]
        mas_rows.sort()
        if self.BinSearchVirt(mas_rows, rw):
            Cur_f, Cur_l = obj_rt[rw]
            if (first <= Cur_f <= last) or (Cur_f <= first <= Cur_l):
                return True
        return False


    def get_mas_glp(self,objects_layer,layer,object_num_prt):

        #warning: objects on one layer may overlap ONLY due corrections angles 'dont_cut_angles' in 'get_dict_gk'
        #Warning: objects may intersect at several locations simultaneously

        if prn_detal > 1: print "(Create a list of adjacent objects of this layer: {0} parent object num: {1})".format(layer,object_num_prt)

        max_line,max_pix = self.get_RowCol(self.w, self.h)  # not (self.? - self.ts_roughing - 1) !?

        mas_glp = {}
        ld = len(objects_layer)
        if ld <= 1: return mas_glp

        for num in objects_layer: #main obj
            progress(num, ld)
            y,x,make_gk = objects_layer[num]      #objects_layer - {1: {59: (0, 0), 60: (0, 3), 61: (0, 3)}}
            for rw in make_gk:  #rows
                Cur_f, Cur_l = make_gk[rw]

                for num2 in objects_layer:    #obj relate_to
                    if num2 == num: continue
                    y,x,make_gk2 = objects_layer[num2]
                    if rw - 1 >= 0 \
                        and self.objects_relate_to(make_gk2,rw-1,Cur_f, Cur_l):
                        if prn_detal > 1: print "( gluing pieces N {0} and N {1} lines: {2},{3} pixels: {4},{5})".format(num,num2,rw,rw-1,Cur_f, Cur_l)
                        try:
                            if num2 not in mas_glp[num]: mas_glp[num].append(num2)
                        except:
                            try:
                                mas_glp[num].append(num2)
                            except:
                                mas_glp[num] = [num2]
                    elif rw + 1 <= max_line \
                        and self.objects_relate_to(make_gk2,rw+1,Cur_f, Cur_l):
                        if prn_detal > 1: print "( gluing pieces N {0} and N {1} lines: {2},{3} pixels: {4},{5})".format(num,num2,rw,rw+1,Cur_f, Cur_l)
                        try:
                            if num2 not in mas_glp[num]: mas_glp[num].append(num2)
                        except:
                            try:
                                mas_glp[num].append(num2)
                            except:
                                mas_glp[num] = [num2]
                    #??? elif self.objects_relate_to(make_gk2,rw,Cur_f, Cur_l):

        #print("mas_glp: ",mas_glp)
        return mas_glp

    def get_mas_child(self,prt_gk,objects_layer,layer,num):

        mas_child = []

        for rw in prt_gk:  #rows of parent object
            Cur_f, Cur_l = prt_gk[rw]

            #objects_layer - [(1, 59, 0, {59: (0, 0), 60: (0, 3), 61: (0, 3)})]
            for num2 in objects_layer:    #obj child
                y,x,make_gk = objects_layer[num2]
                if num2 in mas_child: continue
                if self.objects_relate_to(make_gk,rw,Cur_f, Cur_l):
                    if prn_detal > 2: print "( L: {0} parent pieces N {1} child N {2} line: {3} pixels: {4},{5})".format(layer,num,num2,rw,Cur_f, Cur_l)
                    mas_child.append(num2)

        #print("mas_child: ",mas_child)
        return mas_child

    def get_tree_prt(self,mas_pieces):

        #: overlapping objects on different layers are considered related
        #: parent object is an object on the layer that is deeper
        #: parent may have more than one child objects
        #: child object can have multiple parent

        if prn_detal > 0: print "(Create a tree of relationships between objects. Start at {0})".format(datetime.datetime.now())

        max_line,max_pix = self.get_RowCol(self.w, self.h)  # not (self.? - self.ts_roughing - 1) !?

        tree_prt = {}   #{layer0={num parent0=[num child1,num child2,num child3,...]},...}

        ld = len(mas_pieces)
        if ld <= 1: return tree_prt

        lrange = range(1,ld)
        for layer in lrange:
            objects_of_layer = {}
            for object_num in mas_pieces[layer]:
                y,x,make_gk = mas_pieces[layer][object_num]
                #objects_intersect
                mas_childs = self.get_mas_child(make_gk,mas_pieces[layer-1],layer,object_num)
                if len(mas_childs) > 0:
                    objects_of_layer[object_num] = mas_childs

            tree_prt[layer] = objects_of_layer
        #print("tree_prt: ",tree_prt)
        return tree_prt

    def mill_roughing_minus_finishing(self, convert_scan, primary):

        if prn_detal > 0: print "(Start mill roughing mode minus finishing mode [objectiv R2] {0}.)".format(datetime.datetime.now())

        if self.roughing_offset > 0 and self.pixelsize <= self.roughing_offset:
            maxgk = int(self.pixelsize/self.roughing_offset)
        else: 
            maxgk = 0

        max_line,max_pix = self.get_RowCol(self.w1,self.h1)

        mas_pieces = []       #the massiv of pieces of all objects on all layers
        mas_glp  = []         #(bonding) gluing (adjacent,nearest) portions of objects in each layer
        tree_prt = {}         #the tree of links between objects of adjacent layers
        mas_rd  = []

        #**********************************************************************************
        #Step 1 - make filter all image
        mas_obj_filter = {}
        jrange = range(0, max_line, self.pixelstep)
        if max_line not in jrange: jrange.append(max_line)
        if not self.row_mill: jrange.reverse()  #for cols'''
        for row in jrange:
            mas_obj_filter[row]=(0,max_pix)

        #Step 2 - make filter without background
        objects_filter = []
        if self.max_bg_lan >= self.h1:
            #???????? sdelat tolko esli 4ernovaja freza "ne prjamaja"!
            objects_filter.append((1,mas_obj_filter))
        else:
            processed_cur = {}

            if prn_detal > 0: print "( Make filter for background. Start at {0})".format(datetime.datetime.now())
            if self.row_mill: line_cache = 0
            else:       line_cache = self.h1
            object_is_found = True
            object_num = 0
            self.roughing_minus_finishing = False
            while object_is_found:

                object_num = object_num + 1
                if prn_detal > 1: print "(  Object_num: {0} line_cache {1})".format(object_num,line_cache)

                make_gk,line_cache,object_is_found,(y,x) = self.get_dict_gk(processed_cur,line_cache,object_num,mas_obj_filter)

                if object_is_found:
                    objects_filter.append((object_num,make_gk))
                    for j in make_gk:
                        Pixel_entry_f,Pixel_entry_l = make_gk[j]
                        # clean covered
                        for i in range(Pixel_entry_f,Pixel_entry_l+1): processed_cur[j,i] = 7

            self.max_bg_lan = 3 #???
            self.roughing_minus_finishing = True
        #**********************************************************************************

        #Step 3 - make list of objects to make gk
        if prn_detal > 0: print "(Make list of objects to make gk. Start at {0})".format(datetime.datetime.now())
        self.layer = 0
        
        item_npp = 0

        r = .0
        imin = self.image.min()
        self.rd = self.image.min()

        while r > imin:

            #if self.layer > 3: break
            #if self.layer < 7: continue

            self.layer = self.layer + 1
            
            #step = roughing_depth
            r = max(r - self.roughing_depth,imin)
            if (r - imin) < (self.roughing_depth*roughing_depth_delta): r = imin

            self.rmd = r
            mas_rd.append(self.rmd)

            if prn_detal > 1: print "(  Layer {0} rmd={1} min: {2})".format(self.layer,r,imin)


            processed_cur = {}
            objects_layer = {}    #clear !!!????  not "{}"!!!!!
            object_num = 0
            for object_num_prt,mas_obj_filter in objects_filter:
                
                if self.row_mill:   line_cache = 0          #y
                else:               line_cache = self.h1    #??? x

                object_is_found = True
                while object_is_found:

                    object_num = object_num + 1
                    if prn_detal > 1:
                        print " "
                        print "(  object_num: {0} paren object {1} line_cache {2})".format(object_num,object_num_prt,line_cache)
                         
                    object_is_found = False
                    make_gk,line_cache,object_is_found,(y,x) = self.get_dict_gk(processed_cur,line_cache,object_num,mas_obj_filter)

                    if object_is_found:

                        if maxgk > 0 and len(make_gk) == 1 and abs(make_gk[make_gk.keys()[0]][0]-make_gk[make_gk.keys()[0]][1]) <= maxgk:
                            pass
                        else:
                            objects_layer[object_num] = (y,x,make_gk)
                        for j in make_gk:
                            Pixel_entry_f,Pixel_entry_l = make_gk[j]
                            # clean covered
                            for i in range(Pixel_entry_f,Pixel_entry_l+1): processed_cur[j,i] = 7

            item_npp += len(objects_layer)

            #add all pieces of objects in this layer
            mas_pieces.append(objects_layer)

            #gluing pieces of the object
            #format mas_glp {39: [42], 42: [39,34], 34: [42], 13: [15], 15: [13], 25: [28], 28: [25], 63: [64]}
            mas_glp.append(self.get_mas_glp(objects_layer,self.layer,object_num_prt))



        if prn_detal > 0: print "(End make list of objects. Layers: {0} Items: {1}.)".format(len(mas_pieces),item_npp)

        #Step 4 - group list of objects to tree
        #tree_prt[layer,num parent] = [num child1,num child2,num child3,...]
        if prn_detal > 0: print "(Group list of objects to trees. Start at {0})".format(datetime.datetime.now())
        tree_prt = self.get_tree_prt(mas_pieces)

        #Step 5 - sorting trees of objects
        if prn_detal > 0: print "(Sorting of all trees. Start at {0})".format(datetime.datetime.now())
        
        #format processed_obj[layer,num parent] = sequence(traversal) order
        processed_obj = {}        
        while True:

            layer,object_num = self.get_first_piec(mas_pieces,processed_obj)
            if layer < 0 or object_num < 0: break

            processed_obj = self.sort_tree(layer,object_num,mas_pieces,processed_obj,tree_prt,mas_glp,item_npp)

        if prn_detal > 0: print "(End sorting all trees at {0})".format(datetime.datetime.now())

        #Step 6 - make GK        
        sorted_tree = sorted(processed_obj.items(), key=lambda (k, v): v)
        #print sorted_tree
        for (layer,object_num),npp in sorted_tree:
            self.rd = mas_rd[layer]
            y,x,make_gk = mas_pieces[layer][object_num]
            self.gk_maker(make_gk, max_pix, {}, convert_scan, primary,object_num,layer)

        if prn_detal > 0: print "(End make g-kod at {0})".format(datetime.datetime.now())
            
        
    def get_first_piec(self,mas_pieces,processed_obj):

        ld = len(mas_pieces)
        lrange = range(ld)
        if len(processed_obj) > 0:
            layer_last,object_num_last = sorted(processed_obj.items(), key=lambda (k, v): v)[-1][0]        
            mass = []
            for layer in lrange:
                for object_num in mas_pieces[layer]:
                    try:
                        isprocessed = processed_obj[layer,object_num]
                    except:
                        mass.append((layer,object_num))
            layer,object_num = self.get_nearest_obj(-1,-1,layer_last,object_num_last,mas_pieces,mass)

            
            return layer,object_num
        else:
            for layer in lrange:
                for object_num in mas_pieces[layer]:
                    if prn_detal > 1: print "( Start tree from item- L: {0} obj: {1})".format(layer,object_num)
                    return layer,object_num

            return -1,-1

    def is_processed(self,layer,object_num,processed_obj):
        try:
            if processed_obj[layer,object_num] != -1:
                return True
        except:
            pass
        return False

    def get_mas_eb_rekurs(self,layer,object_num,tree_prt,processed_obj,mass):

        #Create an array of 'Ends of the branches'
        #This proc look only Up.

        #*******************************************************************
        #look up for child
        try:
            mas_child = tree_prt[layer][object_num]
        except:
            mas_child = []

        child_is_found = False
        for child_num in mas_child:
            #recurs make child first
            if self.is_processed(layer-1,child_num,processed_obj): continue

            #if prn_detal > 2: print "(  first process his child- L: {0} obj: {1})".format(layer-1,child_num)
            child_is_found = True

            mass = self.get_mas_eb_rekurs(layer-1,child_num,tree_prt,processed_obj,mass)
        #*******************************************************************

        #warning: branches can be fused. Therefore '(layer,object_num) not in mass'
        if not child_is_found and ((layer,object_num) not in mass): 
            mass.append((layer,object_num))
            if prn_detal > 2: print "(   *add end of the branch - L: {0} obj: {1})".format(layer,object_num)


        #*******************************************************************
        #pieces - object (gluing adjacent piece)
        try:
            cmglp = mas_glp[layer][object_num]
        except:
            cmglp = []
 
        for cur_p in cmglp:
            if self.is_processed(layer,cur_p,processed_obj): continue
            if prn_detal > 2: print "(  *add end of the branch in adjacent branch- L: {0} obj: {1})".format(layer,cur_p)

            mass = self.get_mas_eb_rekurs(layer,cur_p,tree_prt,processed_obj,mass)
        #*******************************************************************

        return mass

    def get_nearest_obj(self,layer,object_num,layer_last,object_num_last,mas_pieces,mass):
         
        next_layer,next_object_num = layer,object_num
        if len(mass) < 1:
            pass
        elif len(mass) == 1:
            (next_layer,next_object_num) = mass[0]
        else:
            y,x,make_gk = mas_pieces[layer_last][object_num_last]
            min_len = plus_inf
            for layer2,object_num2 in mass:
                y2,x2,make_gk = mas_pieces[layer2][object_num2]
                #if necessary more speed - use: 'self.find_next_e(mas_p,y,x,max_line,max_pix)' (not 'hypot')
                cur_len = hypot(y2-y,x2-x)
                if min_len > cur_len: 
                    min_len = cur_len
                    next_layer,next_object_num = layer2,object_num2
            if prn_detal > 2: print "(   L: {0} obj: {1} closest to him is L: {2} obj: {3} [{4}])".format(\
                                    layer_last,object_num_last,next_layer,next_object_num,len(mass))
            
        if next_layer == layer_last and next_object_num == object_num_last:
            if prn_detal > -1: print "(   Error() nearest- not found L: {0} obj: {1} [{2}])".format(next_layer,next_object_num,len(mass))

        return next_layer,next_object_num


    def sort_tree(self,layer,object_num,mas_pieces,processed_obj,tree_prt,mas_glp,item_npp):

        if prn_detal > 1: print "( look- L: {0} obj: {1})".format(layer,object_num)
        layer_last,object_num_last = layer,object_num
        max_layer = len(mas_pieces)

        #format ar_prt[layer][parent_object_num1] = num_sort
        # ar_prt = {0:{0:0, 0:1, 0:2}, 1:{0:3, 0:4}, 2:{0:5, 1:6}}
        ar_prt = {}

        while True:     #For non-recursive calls

            if prn_detal > 2: print "( process piec - L: {0} obj: {1})".format(layer,object_num)

            #1. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
            #first of all - look up for child
            try:
                mas_child = tree_prt[layer][object_num]
            except:
                mas_child = []

            child_is_found = False
            for child_num in mas_child:
                #recurs make child first
                if self.is_processed(layer-1,child_num,processed_obj): continue

                if prn_detal > 2: print "(  he has child/s...)"
                child_is_found = True


                # format mass = ((layer1,object_num1),(layer2,object_num1),(layer2,object_num2),..)
                mass = self.get_mas_eb_rekurs(layer,object_num,tree_prt,processed_obj,[])

                layer,object_num = self.get_nearest_obj(layer,object_num,layer_last,object_num_last,mas_pieces,mass)

                #layer,object_num = layer-1,child_num

                if prn_detal > 2: print "(  first process his child- L: {0} obj: {1})".format(layer,object_num)
                break

            if child_is_found: continue
            #^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        


            #2. *******************************************************************
            #make current        
            try:
                isprocessed = processed_obj[layer,object_num]
                if prn_detal > -1: print "(!!!! ERROR(0) ALREDY PROCESSED!!! L: {0} obj: {1})".format(layer,object_num)
                return processed_obj
            except:
                #obj for GK make here

                if prn_detal > 1: print "(  make GK- L: {0} obj: {1})".format(layer,object_num)
                layer_last,object_num_last = layer,object_num
                cur_npp = len(processed_obj)
                processed_obj[layer,object_num] = cur_npp

                progress(cur_npp, item_npp)

                try: 
                    ar_prt[layer].remove(object_num)
                    #print "(& parent processed: L: {0} obj: {1})".format(layer,object_num)
                except: 
                    #print "(& not parent: L: {0} obj: {1})".format(layer,object_num)
                    pass
            #*******************************************************************



            #3.1. --------------------------------------------------------------------------
            #before moving next - remember the parents of the current
            # If you do not do that because of transitions to neighboring - may be missing some parents
            # And later will have to do extra returns to missed parents.
            try:
                prt_layer = ar_prt[layer+1]
            except:
                ar_prt[layer+1] = []
                prt_layer = ar_prt[layer+1]


            num_order = len(prt_layer)

            #add parents
            try:
                prt_obj = tree_prt[layer+1]
            except:
                prt_obj = []
            #fill array parent
            for prt_object_num in prt_obj:
                if self.is_processed(layer+1,prt_object_num,processed_obj): continue
                try:
                    mas_child = prt_obj[prt_object_num]
                except:
                    mas_child = []
                if object_num in mas_child:
                    #This is parent of current piece.
                    if prt_object_num not in prt_layer:

                        if prn_detal > 2: print "(   remember parent- L: {0} obj: {1} order: {2})".format(\
                                            layer+1,prt_object_num,len(prt_layer))
                        #order in this array is order to process.
                        prt_layer.insert(0,prt_object_num)  #append(

                    elif prt_layer[0] != prt_object_num:
                        #Parent alredy added. Typically, a parent is closest to the "current".
                        #If we don't process it in the following cycle, then we will have to come back to it later.
                        #Then we lose time by doing a long way to go back!
                        #Parent of current piece mast be next processed. 
                        #Raise the priority of parent.
                        if prn_detal > 2: print "(   Raise the priority of parent - L: {0} obj: {1} was order: {2})".format(\
                                            layer+1,prt_object_num,prt_layer.index(prt_object_num))
                        prt_layer.remove(prt_object_num)
                        prt_layer.insert(0,prt_object_num)
                    

            ar_prt[layer+1] = prt_layer
            #--------------------------------------------------------------------------

            #3.2 ========================================================================
            #before moving further - remember the adjacent pieces of the current layer
            try:
                cur_adj = ar_prt[layer]
            except:
                cur_adj = []
            #add adjacent
            try:
                cmglp = mas_glp[layer][object_num]
            except:
                cmglp = []
            for cur_p in cmglp:
                if self.is_processed(layer,cur_p,processed_obj): continue

                if cur_p not in cur_adj:
                    if prn_detal > 2: print "(   remember adjacent piece- L: {0} obj: {1})".format(layer,cur_p)
                    cur_adj.append(cur_p)

            ar_prt[layer] = cur_adj
            #========================================================================


            #4.1 --------------------------------------------------------------------------
            # parent of this branch (in added order!!!)
            #mass = []
            parent_is_found = False
            for nlayer in range(layer+1,max_layer): #importent: from "layer+1"!
                try:
                    prt_layer = ar_prt[nlayer]
                except:
                    prt_layer = []
                for prt_object_num in prt_layer:
                    if self.is_processed(nlayer,prt_object_num,processed_obj):
                        #if prn_detal > -1: print "(Error() current parent alredy processed!? L{0} obj{1})".format(layer,prt_object_num)
                        continue
                    if prn_detal > 2: print "(  process remembered parent - L: {0} obj: {1} order: {2})".format(\
                                        nlayer,prt_object_num,prt_layer.index(prt_object_num))
                    layer = nlayer
                    object_num = prt_object_num
                    parent_is_found = True
                    break
                if parent_is_found: break

            if parent_is_found: continue
            #--------------------------------------------------------------------------


            #4.2 --------------------------------------------------------------------------
            # (nearest!?)parent of another branches
            parent_is_found = False
            for nlayer in range(max_layer,-1,-1):     #instead "max_layer" - correct(better) to use "layer", but reliable to use "max_layer".
                try:
                    prt_layer = ar_prt[nlayer]
                except:
                    prt_layer = []
                for prt_object_num in prt_layer:
                    if self.is_processed(nlayer,prt_object_num,processed_obj):
                        #if prn_detal > -1: print "(Error() current parent alredy processed!? L{0} obj{1})".format(layer,prt_object_num)
                        continue
                    if prn_detal > 2: print "(  process remembered parent adj. - L: {0} obj: {1} order: {2} in {3})".format(\
                                        nlayer,prt_object_num,prt_layer.index(prt_object_num),len(prt_layer))
                    layer = nlayer
                    object_num = prt_object_num
                    parent_is_found = True
                    break
                if parent_is_found: break

            if parent_is_found: continue

            '''        mass.append((nlayer,prt_object_num))
                if len(mass)>0: break

            if len(mass) > 0:
                layer,object_num = self.get_nearest_obj(layer_last,object_num_last,layer_last,object_num_last,mas_pieces,mass)
                if prn_detal > 2: print "(  process remembered - L: {0} obj: {1})".format(layer,prt_object_num)
                continue'''
            #--------------------------------------------------------------------------


            break   #main exit from 'while'

        if prn_detal > 1: print "( END of tree.)"
        return processed_obj


    def mill_objectiv(self, convert_scan, primary):

        if prn_detal > 0: print("Start mill objectiv mode. layer ",self.layer,self.MaxBackground_down,self.MaxBackground_up,self.image.min(),self.background_border,self.rd)  

        processed_cur = {}

        if self.row_mill: line_cache = 0
        else:       line_cache = self.h1

        max_line,max_pix = self.get_RowCol(self.w1,self.h1)

        #**********************************************************************************
        #Step 1 - make filter all image
        mas_obj_filter = {}
        jrange = range(0, max_line, self.pixelstep)
        if max_line not in jrange: jrange.append(max_line)
        if not self.row_mill: jrange.reverse()  #for cols'''
        for row in jrange:
            mas_obj_filter[row]=(0,max_pix)

        object_is_found = True
        object_num = 0
        while object_is_found:


            object_num = object_num + 1
            if prn_detal > 1: print "(  object_num: {0})".format(object_num) 
            #if object_num >= 12: break


            object_is_found = False
            make_gk,line_cache,object_is_found,(y,x) = self.get_dict_gk(processed_cur,line_cache,object_num,mas_obj_filter )

            # make g-code from entrys in dict make_gk
            self.gk_maker(make_gk, max_pix, processed_cur, convert_scan, primary,object_num)

    #////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    def Get_first_last_pixels(self, irange, j):
        row_y_first = 0;
        row_y_last  = 0;
        for i in irange: #pixels
            y,x = self.get_RowCol(j,i)
            hhh1 = self.get_z_abs(y,x)
            if self.not_background(hhh1,j,i): 
                row_y_last  = i
            else:
                if row_y_last == 0: row_y_first = i
        if row_y_first > 0: row_y_first = row_y_first -1
        if row_y_last < len(irange): row_y_last = row_y_last +1
        return row_y_last, row_y_first

    def mill_rows(self, convert_scan, primary):
        w1 = self.w1; h1 = self.h1;
        pixelsize = self.pixelsize; pixelstep = self.pixelstep
        jrange = range(0, w1, pixelstep)
        if w1 not in jrange: jrange.append(w1)
        irange = range(h1)
        st = 2

        max_j = jrange[-1]
        Prev_line = -1
        for j in jrange:    #rows
            progress(j, max_j)

            # find background--------------------------------------
            st = st + 1
            row_y_first = 0
            if self.background_border == 0:
               row_y_last  = irange[-1];
            else:
                row_y_last  = 0;

                #********************************
                row_y_last, row_y_first = self.Get_first_last_pixels(irange, j)
                #********************************
  
                if self.dont_cut_angles:
                    # find new row MaxBackgrounds
                    row_y_first_next = irange[-1];
                    row_y_last_next  = 0;
                    if (jrange.index(j)+1) < max_j:
                        j_next = jrange[jrange.index(j)+1];
                        #********************************
                        row_y_last_next, row_y_first_next = self.Get_first_last_pixels(irange, j_next)
                        #********************************

                    # find previous row MaxBackgrounds
                    row_y_first_prev = irange[-1];
                    row_y_last_prev  = 0;
                    if Prev_line > 0:

                        #********************************
                        row_y_last_prev, row_y_first_prev = self.Get_first_last_pixels(irange, Prev_line)
                        #********************************
                
                    if st % 2:
                        if row_y_last_prev > row_y_last: row_y_last = row_y_last_prev
                        if row_y_first_next < row_y_first: row_y_first = row_y_first_next
                    else:
                        if row_y_last_next > row_y_last: row_y_last = row_y_last_next
                        if row_y_first_prev < row_y_first: row_y_first = row_y_first_prev
            #--------------------------------------
            
            y = (w1-j+self.ts2) * pixelsize
            scan = []
            for i in irange:
                if i >= row_y_first and i<= row_y_last:
                    x = (i+self.ts2) * pixelsize
                    milldata = (i, (x, y, self.get_z(i, j)),
                        self.get_dz_dx(i, j), self.get_dz_dy(i, j))
                    scan.append(milldata)

            # make g-code
            if len(scan) != 0:
              for flag, points in convert_scan(primary, scan):
                if flag:
                    self.entry_cut(self, points[0][0], j, points)
                for p in points:
                    self.g.cut(*p[1])
            else:
                st = st - 1
            self.g.flush()
            Prev_line = j

    def mill_cols(self, convert_scan, primary):
        w1 = self.w1; h1 = self.h1;
        pixelsize = self.pixelsize; pixelstep = self.pixelstep
        jrange = range(0, h1, pixelstep)
        irange = range(w1)
        if h1 not in jrange: jrange.append(h1)
        jrange.reverse()
        st = 0

        max_j = jrange[0]
        Prev_line = -1
        for j in jrange:    #cols
            progress(j, max_j)

            # find background--------------------------------------
            st = st + 1
            row_y_first = 0;
            if self.background_border == 0:
               row_y_last  = irange[-1];
            else:
                row_y_last  = 0;

                #********************************
                row_y_last, row_y_first = self.Get_first_last_pixels(irange, j)
                #********************************
  
                if self.dont_cut_angles:
                    # find new col MaxBackgrounds
                    row_y_first_next = irange[-1];
                    row_y_last_next  = 0;
                    if (jrange.index(j)+1) < max_j:
                        j_next = jrange[jrange.index(j)+1];
                        #********************************
                        row_y_last_next, row_y_first_next = self.Get_first_last_pixels(irange, j_next)
                        #********************************

                    # find previous col MaxBackgrounds
                    row_y_first_prev = irange[-1];
                    row_y_last_prev  = 0;
                    if Prev_line > 0:

                       #********************************
                       row_y_last_prev, row_y_first_prev = self.Get_first_last_pixels(irange, Prev_line)
                       #********************************
                
                    if st % 2:
                        if row_y_last_prev > row_y_last: row_y_last = row_y_last_prev
                        if row_y_first_next < row_y_first: row_y_first = row_y_first_next
                    else:
                        if row_y_last_next > row_y_last: row_y_last = row_y_last_next
                        if row_y_first_prev < row_y_first: row_y_first = row_y_first_prev
            #--------------------------------------

            x = (j+self.ts2) * pixelsize
            scan = []
            for i in irange:
                if i >= row_y_first and i<= row_y_last:
                    y = (w1-i+self.ts2) * pixelsize
                    milldata = (i, (x, y, self.get_z(j, i)),
                      self.get_dz_dy(j, i), self.get_dz_dx(j, i))
                    scan.append(milldata)

            # make g-code
            if len(scan) != 0:
              for flag, points in convert_scan(primary, scan):
                if flag:
                    self.entry_cut(self, j, points[0][0], points)
                for p in points:
                    self.g.cut(*p[1])
            else:
                st = st - 1
            self.g.flush()
            Prev_line = j

def convert(*args, **kw):
    return Converter(*args, **kw).convert()

class SimpleEntryCut:
    def __init__(self, feed):
        self.feed = feed

    def __call__(self, conv, i0, j0, points):
        p = points[0][1]
        if self.feed:
            conv.g.set_feed(self.feed)
        conv.g.safety()
        conv.g.rapid(p[0], p[1])
        if self.feed:
            conv.g.set_feed(conv.feed)

def circ(r,b): 
    """\
Calculate the portion of the arc to do so that none is above the
safety height (that's just silly)"""

    z = r**2 - (r-b)**2
    if z < 0: z = 0
    return z**.5

class ArcEntryCut:
    def __init__(self, feed, max_radius):
        self.feed = feed
        self.max_radius = max_radius

    def __call__(self, conv, i0, j0, points):
        if len(points) < 2:
            p = points[0][1]
            if self.feed:
                conv.g.set_feed(self.feed)
            conv.g.safety()
            conv.g.rapid(p[0], p[1])
            if self.feed:
                conv.g.set_feed(conv.feed)
            return

        p1 = points[0][1]
        p2 = points[1][1]
        z0 = p1[2]

        lim = int(ceil(self.max_radius / conv.pixelsize))
        r = range(1, lim)

        if self.feed:
            conv.g.set_feed(self.feed)
        conv.g.safety()

        x, y, z = p1

        pixelsize = conv.pixelsize
        
        cx = cmp(p1[0], p2[0])
        cy = cmp(p1[1], p2[1])

        radius = self.max_radius

        if cx != 0:     #if rows
            h1 = conv.h1
            for di in r:
                dx = di * pixelsize
                i = i0 + cx * di
                if i < 0 or i >= h1: break
                z1 = conv.get_z(i, j0)
                dz = (z1 - z0)
                if dz <= 0: continue
                if dz > dx:
                    conv.g.write("(case 1)")
                    radius = dx
                    break
                rad1 = (dx * dx / dz + dz) / 2
                if rad1 < radius:
                    radius = rad1
                if dx > radius:
                    break

            z1 = min(p1[2] + radius, conv.safetyheight)
            x1 = p1[0] + cx * circ(radius, z1 - p1[2])
            conv.g.rapid(x1, p1[1])
            conv.g.cut(z=z1)

            conv.g.flush(); conv.g.lastgcode = None
            if cx > 0:
                conv.g.write("G3 X%f Z%f R%f" % (p1[0], p1[2], radius))
            else:
                conv.g.write("G2 X%f Z%f R%f" % (p1[0], p1[2], radius))
            conv.g.lastx = p1[0]
            conv.g.lasty = p1[1]
            conv.g.lastz = p1[2]
        else:        #if cols
            w1 = conv.w1
            for dj in r:
                dy = dj * pixelsize
                j = j0 - cy * dj
                if j < 0 or j >= w1: break
                z1 = conv.get_z(i0, j)
                dz = (z1 - z0)
                if dz <= 0: continue
                if dz > dy:
                    radius = dy
                    break
                rad1 = (dy * dy / dz + dz) / 2
                if rad1 < radius: radius = rad1
                if dy > radius: break

            z1 = min(p1[2] + radius, conv.safetyheight)
            y1 = p1[1] + cy * circ(radius, z1 - p1[2])
            conv.g.rapid(p1[0], y1)
            conv.g.cut(z=z1)

            conv.g.flush(); conv.g.lastgcode = None
            if cy > 0:
                conv.g.write("G2 Y%f Z%f R%f" % (p1[1], p1[2], radius))
            else:
                conv.g.write("G3 Y%f Z%f R%f" % (p1[1], p1[2], radius))
            conv.g.lastx = p1[0]
            conv.g.lasty = p1[1]
            conv.g.lastz = p1[2]
        if self.feed:
            conv.g.set_feed(conv.feed)

def ui(im, nim, im_name):
    import Tkinter
    import ImageTk
    import pickle
    import nf

    app = Tkinter.Tk()
    rs274.options.install(app)
    app.tk.call("source", os.path.join(BASE, "share", "axis", "tcl", "combobox.tcl"))

    name = os.path.basename(im_name)
    app.wm_title(_("%s: Image to gcode") % name)
    app.wm_iconname(_("Image to gcode"))
    w, h = im.size
    r1 = w / 300.
    r2 = h / 300.
    nw = int(w / max(r1, r2))
    nh = int(h / max(r1, r2))

    ui_image = im.resize((nw,nh), Image.ANTIALIAS)
    ui_image = ImageTk.PhotoImage(ui_image, master = app)
    i = Tkinter.Label(app, image=ui_image, compound="top",
        text=_("Image size: %(w)d x %(h)d pixels\n"
                "Minimum pixel value: %(min)d\nMaximum pixel value: %(max)d")
            % {'w': im.size[0], 'h': im.size[1], 'min': nim.min(), 'max': nim.max()},
        justify="left")
    f = Tkinter.Frame(app)
    g = Tkinter.Frame(app)
    b = Tkinter.Frame(app)
    i.grid(row=0, column=0, sticky="nw")
    f.grid(row=0, column=1, sticky="nw")
    b.grid(row=1, column=0, columnspan=2, sticky="ne")

    def filter_nonint(event):
        if event.keysym in ("Return", "Tab", "ISO_Left_Tab", "BackSpace"):
            return
        if event.char == "": return
        if event.char in "0123456789": return
        return "break"

    def filter_nonfloat(event):
        if event.keysym in ("Return", "Tab", "ISO_Left_Tab", "BackSpace"):
            return
        if event.char == "": return
        if event.char in "0123456789.": return
        return "break"
        
    validate_float    = "expr {![regexp {^-?([0-9]+(\.[0-9]*)?|\.[0-9]+|)$} %P]}"
    validate_int      = "expr {![regexp {^-?([0-9]+|)$} %P]}"
    validate_posfloat = "expr {![regexp {^?([0-9]+(\.[0-9]*)?|\.[0-9]+|)$} %P]}"
    validate_posint   = "expr {![regexp {^([0-9]+|)$} %P]}"
    def floatentry(f, v):
        var = Tkinter.DoubleVar(f)
        var.set(v)
        w = Tkinter.Entry(f, textvariable=var, validatecommand=validate_float, validate="key", width=10)
        return w, var

    def intentry(f, v):
        var = Tkinter.IntVar(f)
        var.set(v)
        w = Tkinter.Entry(f, textvariable=var, validatecommand=validate_int, validate="key", width=10)
        return w, var

    def checkbutton(k, v):
        var = Tkinter.BooleanVar(f)
        var.set(v)
        g = Tkinter.Frame(f)
        w = Tkinter.Checkbutton(g, variable=var, text=_("Yes"))
        w.pack(side="left")
        return g, var 

    def intscale(k, v, min=1, max = 100):
        var = Tkinter.IntVar(f)
        var.set(v)
        g = Tkinter.Frame(f, borderwidth=0)
        w = Tkinter.Scale(g, orient="h", variable=var, from_=min, to=max, showvalue=False)
        l = Tkinter.Label(g, textvariable=var, width=3)
        l.pack(side="left")
        w.pack(side="left", fill="x", expand=1)
        return g, var

    def intscale2(k, v, min=1):
        var = Tkinter.IntVar(f)
        var.set(v)
        g = Tkinter.Frame(f, borderwidth=0)
        w, h = im.size
        mmax = max(w,h)
        w = Tkinter.Scale(g, orient="h", variable=var, from_=min, to=mmax, showvalue=False)
        l = Tkinter.Label(g, textvariable=var, width=3)
        l.pack(side="left")
        w.pack(side="left", fill="x", expand=1)
        return g, var

    def _optionmenu(k, v, *options):
        options = list(options)
        def trace(*args):
            try:
                var.set(options.index(svar.get()))
            except ValueError:
                pass

        try:
            opt = options[v]
        except (TypeError, IndexError):
            v = 0
            opt = options[0]

        var = Tkinter.IntVar(f)    
        var.set(v)
        svar = Tkinter.StringVar(f)
        svar.set(options[v])
        svar.trace("w", trace)
        wp = f._w.rstrip(".") + ".c" + svar._name
        f.tk.call("combobox::combobox", wp, "-editable", 0, "-width",
                max(len(opt) for opt in options)+3, "-textvariable", svar._name,
                "-background", "white")
        f.tk.call(wp, "list", "insert", "end", *options)
        w = nf.makewidget(f, Tkinter.Widget, wp)
        return w, var

    def optionmenu(*options): return lambda f, v: _optionmenu(f, v, *options)

    rc = os.path.expanduser("~/.image2gcoderc")
    constructors = [
        ("units", optionmenu(_("G20 (in)"), _("G21 (mm)"))),
        ("invert", checkbutton),
        ("normalize", checkbutton),
        ("expand", optionmenu(_("None"), _("White"), _("Black"))),
        ("tolerance", floatentry),
        ("pixel_size", floatentry),
        ("feed_rate", floatentry),
        ("plunge_feed_rate", floatentry),
        ("spindle_speed", floatentry),
        ("pattern", optionmenu(_("Rows"), _("Columns"), _("Rows then Columns"), _("Columns then Rows"), _("Rows Object"), _("Cols Object"))),
        ("converter", optionmenu(_("Positive"), _("Negative"), _("Alternating"), _("Up Milling"), _("Down Milling"))),
        ("depth", floatentry),
        ("background_border", floatentry),
        ("max_bg_lan", intscale2),
        ("dont_cut_angles", checkbutton),
        ("cut_top_jumper", checkbutton),
        ("pixelstep", intscale),
        ("safety_height", floatentry),
        ("tool_diameter", floatentry),
        ("tool_type", optionmenu(_("Ball End"), _("Flat End"), _("30 Degree"), _("45 Degree"), _("60 Degree"), _("90 Degree"))),
        ("tool_diameter2", floatentry),
        ("angle2", floatentry),
        ("bounded", optionmenu(_("None"), _("Secondary"), _("Full"))),
        ("contact_angle", floatentry),
        ("optimize_path", checkbutton),
        ("roughing_completed", checkbutton),
        ("roughing_minus_finishing", checkbutton),
        ("min_delta_rf",floatentry),
        ("previous_offset", floatentry),
        ("roughing_offset", floatentry),
        ("roughing_depth", floatentry),
        ("pixelstep_roughing", intscale),
        ("tool_diameter_roughing", floatentry),
        ("tool_type_roughing", optionmenu(_("Ball End"), _("Flat End"), _("30 Degree"), _("45 Degree"), _("60 Degree"), _("90 Degree"))),
        ("tool_diameter_roughing2", floatentry),
        ("angle2_roughing", floatentry)
    ]

    defaults = dict(
        invert = False,
        normalize = False,
        expand = 0,
        pixel_size = .006,
        depth = 0.25,
        background_border = .0,
        max_bg_lan = 1,
        cut_top_jumper = False,
        dont_cut_angles= False,
        pixelstep = 8,
        safety_height = .012,
        tool_diameter = 1/16.,
        tool_type = 0,
        tool_diameter2 = 1/16.,
        angle2 = .0,
        tolerance = .001,
        feed_rate = 12,
        plunge_feed_rate = 12,
        units = 0,
        pattern = 0,
        converter = 0,
        bounded = 0,
        contact_angle = 45,
        spindle_speed = 1000,
        optimize_path = False,
        roughing_completed = False,
        roughing_minus_finishing = False,
        min_delta_rf = .0,
        previous_offset = .1,
        roughing_offset = .1,
        roughing_depth = .25,
        pixelstep_roughing = 8,
        tool_diameter_roughing = 1/16.,
        tool_type_roughing = 0,
        tool_diameter_roughing2 =1/16.,
        angle2_roughing=.0
    )

    texts = dict(
        invert=_("Invert Image"),
        normalize=_("Normalize Image"),
        expand=_("Extend Image Border"),
        pixel_size=_("Pixel Size (Units)"),
        depth=_("Depth (units)"),
        background_border=_("Border background (units, 0=no cutting)"),
        max_bg_lan=_("Max background len (pixels)"),
        dont_cut_angles=_("Dont cut angles"),
        cut_top_jumper=_("Cut top jumper"),
        tolerance=_("Tolerance (units)"),
        pixelstep=_("Stepover (pixels)"),
        tool_diameter=_("Tool Diameter (units)"),
        tool_type=_("Tool Type"),
        tool_diameter2=_("Tool Diameter 2 (units)"),
        angle2=_("Angle of tool 2"),
        feed_rate=_("Feed Rate (units per minute)"),
        plunge_feed_rate=_("Plunge Feed Rate (units per minute)"),
        units=_("Units"),
        safety_height=_("Safety Height (units)"),
        pattern=_("Scan Pattern"),
        converter=_("Scan Direction"),
        bounded=_("Lace Bounding"),
        contact_angle=_("Contact Angle (degrees)"),
        spindle_speed=_("Spindle Speed (RPM)"),
        optimize_path=_("Optimize path"),
        roughing_completed =_("Roughing is completed"),
        roughing_minus_finishing =_("Roughing mode minus finishing mode"),
        min_delta_rf=_("min delta rf(units)"),
        previous_offset=_("Previous offset (units, 0=no roughing)"),
        roughing_offset=_("Roughing offset (units, 0=no roughing)"),
        roughing_depth=_("Roughing depth per pass (units)"),
        pixelstep_roughing =_("Roughing stepover (pixels)"),
        tool_diameter_roughing =_("Roughing tool Diameter (units)"),
        tool_type_roughing =_("Roughing tool Type"),
        tool_diameter_roughing2 =_("Roughing tool Diameter 2 (units)"),
        angle2_roughing=_("Roughing Angle of tool diameter 2")
    )

    try:
        defaults.update(pickle.load(open(rc, "rb")))
    except (IOError, pickle.PickleError): pass

    vars = {}
    widgets = {}
    for j, (k, con) in enumerate(constructors):
        v = defaults[k]
        text = texts.get(k, k.replace("_", " "))
        lab = Tkinter.Label(f, text=text)
        widgets[k], vars[k] = con(f, v)
        lab.grid(row=j, column=0, sticky="w")
        widgets[k].grid(row=j, column=1, sticky="ew")

    def trace_pattern(*args):
        if vars['pattern'].get() == 2 or vars['pattern'].get() == 3:
            widgets['bounded'].configure(state="normal")
            trace_bounded()
        else:
            widgets['bounded'].configure(state="disabled")
            widgets['contact_angle'].configure(state="disabled")
        trace_optimize_path()
        valyd_max_bg_lan()

    def trace_bounded(*args):
        if vars['bounded'].get() != 0:
            widgets['contact_angle'].configure(state="normal")
        else:
            widgets['contact_angle'].configure(state="disabled")

    def trace_offset(*args):
        if vars['roughing_offset'].get() > 0:
            widgets['roughing_depth'].configure(state="normal")
        else:
            if vars['optimize_path'].get() or vars['roughing_minus_finishing'].get() or vars['roughing_completed'].get():
                widgets['roughing_depth'].configure(state="normal")
            else:
                widgets['roughing_depth'].configure(state="disabled")

    def valyd_max_bg_lan(*args):
        if vars['pattern'].get() < 4:
            vars['max_bg_lan'].set(0)
        else:
            if vars['max_bg_lan'].get() == 0:
                w, h = im.size
                if vars['roughing_minus_finishing'].get():
                    vars['max_bg_lan'].set(max(2,h /100 * 0.3)) # default = 0.3%
                else:
                    vars['max_bg_lan'].set(max(1,h /100 * 5))   # default = 5%

    def trace_optimize_path(*args):
        if vars['pattern'].get() < 4:
            vars['optimize_path'].set(False)
            vars['roughing_completed'].set(False)
            vars['roughing_minus_finishing'].set(False)

        if vars['optimize_path'].get():
            widgets['roughing_depth'].configure(state="normal")
        else:
            widgets['roughing_offset'].configure(state="normal")
            if vars['roughing_offset'].get() > 0 or vars['roughing_minus_finishing'].get():
                widgets['roughing_depth'].configure(state="normal")
            else:            
                widgets['roughing_depth'].configure(state="disabled")


    def trace_background_border(*args):
        if vars['background_border'].get() != 0:
            widgets['cut_top_jumper'].configure(state="normal")
        else:
            widgets['cut_top_jumper'].configure(state="disabled")

    def trace_roughing_minus_finishing(*args):
        trace_optimize_path()
        if vars['roughing_minus_finishing'].get():
            valyd_max_bg_lan()
            widgets['previous_offset'].configure(state="normal")
            trace_offset()
            widgets['min_delta_rf'].configure(state="normal")
            widgets['tool_diameter_roughing'].configure(state="normal")
            widgets['tool_type_roughing'].configure(state="normal")
            widgets['tool_diameter_roughing2'].configure(state="normal")
            widgets['angle2_roughing'].configure(state="normal")
            if vars['pixelstep_roughing'].get() == 0:
                vars['pixelstep_roughing'].set(1)
        else:
            widgets['previous_offset'].configure(state="disabled")
            trace_offset()
            widgets['min_delta_rf'].configure(state="disabled")
            widgets['tool_diameter_roughing'].configure(state="disabled")
            widgets['tool_type_roughing'].configure(state="disabled")
            widgets['tool_diameter_roughing2'].configure(state="disabled")
            widgets['angle2_roughing'].configure(state="disabled")
            vars['pixelstep_roughing'].set(0)

    vars['pattern'].trace('w', trace_pattern)
    vars['bounded'].trace('w', trace_bounded)
    vars['roughing_offset'].trace('w', trace_offset)

    vars['background_border'].trace('w', trace_background_border)
    vars['optimize_path'].trace('w', trace_optimize_path)
    vars['roughing_minus_finishing'].trace('w', trace_roughing_minus_finishing)
    vars['max_bg_lan'].trace('w', valyd_max_bg_lan)

    trace_optimize_path()
    trace_pattern()
    trace_offset()
    #trace_background_border()
    trace_roughing_minus_finishing()

    valyd_max_bg_lan()

    status = Tkinter.IntVar()
    bb = Tkinter.Button(b, text=_("OK"), command=lambda:status.set(1), width=8, default="active")
    bb.pack(side="left", padx=4, pady=4)
    bb = Tkinter.Button(b, text=_("Cancel"), command=lambda:status.set(-1), width=8, default="normal")
    bb.pack(side="left", padx=4, pady=4)
    
    app.bind("<Escape>", lambda evt: status.set(-1))
    app.bind("<Return>", lambda evt: status.set(1))
    app.wm_protocol("WM_DELETE_WINDOW", lambda: status.set(-1))
    app.wm_resizable(0,0)

    app.wait_visibility()
    app.tk.call("after", "idle", ("after", "idle", "focus [tk_focusNext .]"))
    #app.tk_focusNext().focus()
    app.wait_variable(status)


    for k, v in vars.items():
        defaults[k] = v.get()

    app.destroy()

    if status.get() == -1:
        raise SystemExit(_("image-to-gcode: User pressed cancel"))

    pickle.dump(defaults, open(rc, "wb"))

    return defaults

def main():
    if len(sys.argv) > 1:
        im_name = sys.argv[1]
    else:
        import tkFileDialog, Tkinter
        im_name = tkFileDialog.askopenfilename(defaultextension=".png",
            filetypes = (
                (_("Depth images"), ".gif .png .jpg"),
                (_("All files"), "*")))
        if not im_name: raise SystemExit
        Tkinter._default_root.destroy()
        Tkinter._default_root = None
    im = Image.open(im_name)
    size = im.size
    im = im.convert("L") #grayscale
    w, h = im.size

    nim = numarray.fromstring(im.tostring(), 'UInt8', (h, w)).astype('Float32')
    options = ui(im, nim, im_name)

    step = options['pixelstep']
    depth = options['depth']

    if options['normalize']:
        a = nim.min()
        b = nim.max()
        if a != b:
            nim = (nim - a) / (b-a)
    else:
        nim = nim / 255.0

    maker = tool_makers[options['tool_type']]
    tool_diameter = options['tool_diameter']
    pixel_size = options['pixel_size']
    tool = make_tool_shape(maker, tool_diameter, pixel_size,False,options['tool_type'],options['tool_diameter2'],options['angle2'])
    tool = optim_tool_shape(tool,depth,options['roughing_offset'],max(options['pixel_size'],options['tolerance']))

    if options['expand']:
        if options['expand'] == 1: pixel = 1
        else: pixel = 0
        w, h = nim.shape
        tw, th = tool.shape
        w1 = w + 2*tw
        h1 = h + 2*th
        nim1 = numarray.zeros((w1, h1), 'Float32') + pixel
        nim1[tw:tw+w, th:th+h] = nim
        nim = nim1
        w, h = w1, h1
    nim = nim * depth

    if options['invert']:
        nim = -nim
    else:
        nim = nim - depth

    if prn_detal > 0: print "(Image max= {0} min={1})".format(nim.max(),nim.min())

    rows = options['pattern'] != 1 and options['pattern'] != 5
    columns = options['pattern'] != 0 and options['pattern'] != 4
    columns_first = options['pattern'] == 3
    pattern_objectiv = options['pattern'] > 3
    spindle_speed = options['spindle_speed']
    if rows: convert_rows = convert_makers[options['converter']]()
    else: convert_rows = None
    if columns: convert_cols = convert_makers[options['converter']]()
    else: convert_cols = None

    if options['bounded'] and rows and columns:
        slope = tan(((180-options['contact_angle'])/2) * pi / 180)
        if columns_first:
            convert_rows = Reduce_Scan_Lace(convert_rows, slope, step+1)
        else:
            convert_cols = Reduce_Scan_Lace(convert_cols, slope, step+1)
        if options['bounded'] > 1:
            if columns_first:
                convert_cols = Reduce_Scan_Lace(convert_cols, slope, step+1)
            else:
                convert_rows = Reduce_Scan_Lace(convert_rows, slope, step+1)

    maker_roughing = tool_makers[options['tool_type_roughing']]
    tool_diameter_roughing = options['tool_diameter_roughing']
    tool_roughing = make_tool_shape(maker_roughing, tool_diameter_roughing, pixel_size,False,options['tool_type_roughing'],options['tool_diameter_roughing2'],options['angle2_roughing'])
    tool_roughing = optim_tool_shape(tool_roughing,depth,options['roughing_offset'],max(options['pixel_size'],options['tolerance']))

    units = unitcodes[options['units']]
    convert(nim, units, tool, pixel_size, step,
        options['safety_height'], options['tolerance'], options['feed_rate'],
        convert_rows, convert_cols, columns_first, ArcEntryCut(options['plunge_feed_rate'], .125),
        spindle_speed, options['roughing_offset'], options['roughing_depth'], options['feed_rate'],
        options['invert'],options['background_border'],options['cut_top_jumper'],
        options['dont_cut_angles'],options['optimize_path'],options['roughing_completed'],options['max_bg_lan'],
        pattern_objectiv,
        options['roughing_minus_finishing'],options['pixelstep_roughing'],tool_roughing,options['min_delta_rf'],options['previous_offset'])

if __name__ == '__main__':
    main()

# vim:sw=4:sts=4:et:
