#!/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("emc2", 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

epsilon = 1e-5
epsilon16 = 1e-16
is_ddd1 = True


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

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

    #dia = int(wdia*res+.5)
    dia = int(wdia*res)
    if dia/res < wdia: dia = dia +1
    if dia < 1: dia = 1
    
    if is_offset: 
        if dia == 1: 
            dia = 2
            print("Warning: offset[",wdia,"] <= pixel size[",resp,"]. New offset = ",(dia*resp/2))
        else:
            print("Offset = ",(dia*resp/2))

    
    if always_an_odd and not dia % 2: dia = dia +1

    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: print("Error tool2(2): ",wrad0)
            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.: 
                    print("Error 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

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

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

                if r < wrad0:
                    z = f(r, wrad)
                    l.append(z)
                    n[x,y] = z
                    if z < 0. : 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.: 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.: print("error: tool.minimum = ",n.min(),", mast be == 0")
    return n

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

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

    if line > 0:


        P = True
        line2 = dia
        while P and line2 > (line+1):
            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

        line2 = line2 + 1

        print("tool opitimize - ",dia," lines:")
        print(" cut lines[left  and up  ]: ",line)
        print(" cut lines[right and down]: ",(dia-line2))

        if line2 < dia:
            dia2 = line2 - line
            n2 = numarray.array([[plus_inf] * dia2] * dia2, type="Float32")
            d2range = range(line,line2)
            for x in d2range:
                for y in d2range:
                    n2[x-line,y-line] = n[x,y]
        else:
            dia2 = dia - line
            n2 = numarray.array([[plus_inf] * dia2] * dia2, type="Float32")
            d2range = range(line,dia)
            for x in d2range:
                for y in d2range:
                    n2[x-line,y-line] = n[x,y]

        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):
    if os.environ.has_key("AXIS_PROGRESS_BAR"):
        print >>sys.stderr, "FILTER_PROGRESS=%d" % 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):
        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.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.ppr = True #False #
        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.ts_roughing = tool_roughing.shape[0]
            print("Tool shape roughing = ",self.ts_roughing," pixels")
            self.min_delta_rf = min_delta_rf
            self.rmf = numarray.zeros((w, h), 'Float32') - plus_inf #-1.
            self.map_tool1 = {}
        self.max_rmf = 0
        self.min_rmf = self.image.min()


        self.cache = {}
        self.cache_abs = {}

        ts = self.ts = tool_shape.shape[0]
        print("Tool shape = ",ts," pixels")

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

        if roughing_minus_finishing:
            self.h0 = h - self.ts_roughing -1
            self.w0 = w - self.ts_roughing -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
        print("Layer ",self.layer," layer height ",self.rd)



        if self.convert_cols and self.cols_first_flag:
            self.g.set_plane(19)
            if self.roughing_minus_finishing: 
                self.mill_roughing_minus_finishing(self.convert_cols, True)
            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.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.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 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 self.roughing_minus_finishing:
            #base_image = self.image
            if self.roughing_offset > epsilon16:
                rough = make_tool_shape(ball_tool,
                                    2*self.roughing_offset, self.pixelsize, True)
                rough = optim_tool_shape(rough,-self.image.min(),self.roughing_offset,max(self.pixelsize,self.tolerance))
                w = self.w
                h = self.h
                tw, th = rough.shape
                w1 = w + tw
                h1 = h + th
                nim1 = numarray.zeros((w1, h1), 'Float32') + self.image.min()
                nim1[tw/2:tw/2+w, th/2:th/2+h] = self.image
                self.image = numarray.zeros((w,h), type="Float32")
                for j in range(0, w):
                    progress(j,w)
                    for i in range(0, h):
                        self.image[j,i] = (nim1[j:j+tw,i:i+th] - rough).max()
            self.feed = self.roughing_feed

            if self.convert_rows and not self.convert_cols:
                self.set_rmf_row()
            else:
                self.set_rmf_col()

        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:
                rough = make_tool_shape(ball_tool,
                                    2*self.roughing_offset, self.pixelsize, True)
                rough = optim_tool_shape(rough,-self.image.min(),self.roughing_offset,max(self.pixelsize,self.tolerance))
                w, h = base_image.shape
                tw, th = rough.shape
                w1 = w + tw
                h1 = h + th
                nim1 = numarray.zeros((w1, h1), 'Float32') + base_image.min()
                nim1[tw/2:tw/2+w, th/2:th/2+h] = base_image
                self.image = numarray.zeros((w,h), type="Float32")
                for j in range(0, w):
                    progress(j,w)
                    for i in range(0, h):
                        self.image[j,i] = (nim1[j:j+tw,i:i+th] - rough).max()
            self.feed = self.roughing_feed
            m = self.image.min()
            if self.optimize_path:
                self.ro = 0.
            else:
                self.ro = self.roughing_offset
            r = self.image.max()-self.ro    #self.image.min()+self.ro    #-self.roughing_depth

            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 self.ppr: print("@--@@@@@@@ rd=",self.layer,self.rd,m,(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()

        
    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]
            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.convert_rows and not self.convert_cols:
            return x,y
        else:
            return y,x

    def get_z_abs(self, y, x):
        lin,pix = self.get_RowCol(y,x)
        try:
            return min(0, self.cache_abs[lin,pix])
        except KeyError:
            m1 = self.image[lin:lin+self.ts, pix:pix+self.ts]
            d = (m1 - self.tool).max()
            self.cache_abs[lin,pix] = d
            return min(0,d)
        
    def get_rmf_map_tool_row(self):

        map_tool1 = {}
        jrange = range(0, self.w0, self.pixelstep_roughing)
        irange = range(0, self.h0)

        trange = range(0,self.ts_roughing)

        for y in jrange:    #lines
            progress(y, self.w0)
            for x in irange:    #pixels

                m1 = self.image[y:y+self.ts_roughing, x:x+self.ts_roughing]
                hhh1 = (m1 - self.tool_roughing).max() + self.roughing_offset

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

                        t = self.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(-self.image[ty,tx] + hhh1 + t,16)

                        if dt < -epsilon: 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

        return map_tool1

    def set_rmf_row(self):

        tool_cicl = self.get_tool_cicl()
        self.map_tool1 = self.get_rmf_map_tool_row()
        
        map_tool2 = {}

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

        trange = range(0,self.ts)

        for y in jrange:    #lines
            progress(y, self.w1)
            for x in irange:    #pixels

                hhh1 = self.get_z_abs(y,x)
                hhh1 = hhh1 + self.roughing_offset
                #m1 = self.image[y:y+self.ts, x:x+self.ts]
                #hhh1 = (m1 - self.tool).max() + self.roughing_offset

                founded = False

                """#--------------------------------------------------------------------------------
                for (y3,x3),h2 in tool_cicl.items():
                    y2 = y + y3 - 1
                    x2 = x + x3 - 1
                    p_h = -self.image[y2,x2]
                    try:
                        h_fin = map_tool2[y2,x2]
                    except:
                        h_fin = plus_inf
                    try:
                        h_roug = self.map_tool1[y2,x2]
                    except: 
                        h_roug = plus_inf
                    if isinf(h_roug) and isinf(h_fin): continue

                    # hhh1 < 0, (h_roug and h_fin and p_h) > 0, p_h >= h_?
                    delta = p_h-min(h_roug,h_fin) + hhh1
                    if delta > self.min_delta_rf:
                        founded = True
                        
                        if y2 == (y + self.ts): dopY = 1
                        else: dopY = 0 
                        if x2 == (x + self.ts): dopX = 1
                        else: dopX = 0 

                        if isinf(map_tool2[y+dopY,x+dopX]): 
                            print("Error(98) finish tool in [",(y+dopY),(x+dopX),"] is not define = ",map_tool2[y+dopY,x+dopX]," dopY:",dopY," dopX:",dopX)
                            self.rmf[y+dopY,x+dopX] = 0.0
                        else:
                            self.rmf[y+dopY,x+dopX] = map_tool2[y+dopY,x+dopX]

                        if dopY <> 0 or dopX <> 0: 
                            self.rmf[y,x] = map_tool2[y,x]

                            if self.rmf[y,x] < .0: 
                                print("Error(96'')(",y,x,")(",y3,x3,") rmf<0: ",self.rmf[y,x]," map_tool2[y,x]: ",map_tool2[y,x])  
                            elif self.image[y,x]+self.rmf[y,x] > 1.0: 
                                print("Error(95')(",y,x,")(",y3,x3,") rmf+>0: ",self.rmf[y,x]," map_tool2[y,x]: ",map_tool2[y,x])

                        if self.rmf[y+dopY,x+dopX] < .0: 
                            print("Error(96')(",(y+dopY),(x+dopX),")(",y3,x3,") rmf<0: ",self.rmf[y+dopY,x+dopX]," map_tool2[y+,x+]: ",map_tool2[y+dopY,x+dopX])  
                        elif self.image[y+dopY,x+dopX]+self.rmf[y+dopY,x+dopX] > 1.0: 
                            print("Error(95)(",(y+dopY),(x+dopX),")(",y3,x3,") rmf+>0: ",self.rmf[y+dopY,x+dopX]," map_tool2[y+,x+]: ",map_tool2[y+dopY,x+dopX])  

                        break
                #--------------------------------------------------------------------------------
                """

                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 = -self.image[ty,tx] + hhh1 + t
                        #dt = round(-self.image[ty,tx] + hhh1 + t,16)
                        
                        if dt < -epsilon: print(" delta < -0.00001 ",ty,tx,dt,self.image[ty,tx], hhh1, t)
                        if dt < .0: dt = .0
                        
                        try:
                            if map_tool2[ty,tx] > dt: 
                                map_tool2[ty,tx] = dt
                        except KeyError:
                            map_tool2[ty,tx] = dt

                        try:
                            if not founded and (self.map_tool1[ty,tx] - dt) > self.min_delta_rf: 
                                #founded = True

                                #delta = self.map_tool1[ty,tx] - dt
                                #if delta > 0.: self.rmf[y,x] = delta
                                delta = self.image[ty,tx]+self.map_tool1[ty,tx]
                                if delta <= 0.: self.rmf[y,x] = delta
                                else: self.rmf[y,x] = 0.

                        except: pass

        return

    def get_rmf_map_tool_col(self):

        map_tool1 = {}
        jrange = range(0, self.h0, self.pixelstep_roughing)
        irange = range(0, self.w0)

        trange = range(0,self.ts_roughing)

        for x in jrange:    #lines
            progress(x, self.h0)
            for y in irange:    #pixels

                m1 = self.image[y:y+self.ts_roughing, x:x+self.ts_roughing]
                hhh1 = (m1 - self.tool_roughing).max() + self.roughing_offset

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

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

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

                        if dt < -epsilon: 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

        return map_tool1

    def set_rmf_col(self):

        tool_cicl = self.get_tool_cicl()
        self.map_tool1 = self.get_rmf_map_tool_col()

        map_tool2 = {}

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

        trange = range(0,self.ts)

        for x in jrange:    #lines
            progress(x, self.h1)
            for y in irange:    #pixels

                hhh1 = self.get_z_abs(x,y)
                hhh1 = hhh1 + self.roughing_offset
                #m1 = self.image[y:y+self.ts, x:x+self.ts]
                #hhh1 = (m1 - self.tool).max() + self.roughing_offset

                founded = False

                """#--------------------------------------------------------------------------------
                for (y3,x3),h2 in tool_cicl.items():
                    y2 = y + y3 - 1
                    x2 = x + x3 - 1
                    p_h = -self.image[y2,x2]
                    try:
                        h_fin = map_tool2[y2,x2]
                    except:
                        h_fin = plus_inf
                    try:
                        h_roug = self.map_tool1[y2,x2]
                    except: 
                        h_roug = plus_inf
                    if isinf(h_roug) and isinf(h_fin): continue

                    # hhh1 < 0, (h_roug and h_fin and p_h) > 0, p_h >= h_?
                    delta = p_h-min(h_roug,h_fin) + hhh1
                    if delta > self.min_delta_rf:
                        founded = True
                        
                        if y2 == (y + self.ts): dopY = 1
                        else: dopY = 0 
                        if x2 == (x + self.ts): dopX = 1
                        else: dopX = 0 

                        if isinf(map_tool2[y+dopY,x+dopX]): 
                            print("Error(98) finish tool in [",(y+dopY),(x+dopX),"] is not define = ",map_tool2[y+dopY,x+dopX]," dopY:",dopY," dopX:",dopX)
                            self.rmf[y+dopY,x+dopX] = 0.0
                        else:
                            self.rmf[y+dopY,x+dopX] = map_tool2[y+dopY,x+dopX]

                        if dopY <> 0 or dopX <> 0: 
                            self.rmf[y,x] = map_tool2[y,x]

                            if self.rmf[y,x] < .0: 
                                print("Error(96'')(",y,x,")(",y3,x3,") rmf<0: ",self.rmf[y,x]," map_tool2[y,x]: ",map_tool2[y,x])  
                            elif self.image[y,x]+self.rmf[y,x] > 1.0: 
                                print("Error(95')(",y,x,")(",y3,x3,") rmf+>0: ",self.rmf[y,x]," map_tool2[y,x]: ",map_tool2[y,x])

                        if self.rmf[y+dopY,x+dopX] < .0: 
                            print("Error(96')(",(y+dopY),(x+dopX),")(",y3,x3,") rmf<0: ",self.rmf[y+dopY,x+dopX]," map_tool2[y+,x+]: ",map_tool2[y+dopY,x+dopX])  
                        elif self.image[y+dopY,x+dopX]+self.rmf[y+dopY,x+dopX] > 1.0: 
                            print("Error(95)(",(y+dopY),(x+dopX),")(",y3,x3,") rmf+>0: ",self.rmf[y+dopY,x+dopX]," map_tool2[y+,x+]: ",map_tool2[y+dopY,x+dopX])  

                        break
                #--------------------------------------------------------------------------------
                """

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

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

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

                        if dt < -epsilon: print(" delta < -0.00001 ",ty,tx,dt,self.image[ty,tx], hhh1, t)
                        if dt < .0: dt = .0

                        try:
                            if map_tool2[ty,tx] > dt: 
                                map_tool2[ty,tx] = dt
                        except KeyError:
                            map_tool2[ty,tx] = dt

                        try:
                            if not founded and (self.map_tool1[ty,tx] - dt) > self.min_delta_rf: 
                                #founded = True

                                #delta = self.map_tool1[ty,tx] - dt
                                #if delta > 0.: self.rmf[y,x] = delta
                                delta = self.image[ty,tx]+self.map_tool1[ty,tx]
                                if delta <= 0.: self.rmf[y,x] = delta
                                else: self.rmf[y,x] = 0.

                        except: pass

        return

    def is_inf(self,temptool,tool_cicl,ty3,tx3,ty4,tx4):
        try:
            d1 = tool_cicl[ty3,tx3]
        except:
            d1 = -1

        try:
            d2 = tool_cicl[ty4,tx4]
        except:
            d2 = -1

        if d1 != -1 and d2 != -1:
            return False, False,0,0
        elif d1 == -1 and isinf(temptool[ty3,tx3]):
            return True, True,  ty3, tx3
        elif d2 == -1 and isinf(temptool[ty4,tx4]):
            return True, True,  ty4, tx4
        elif d1 == -1:
            return True, False, ty3, tx3
        elif d2 == -1:
            return True, False, ty4, tx4

    def get_tool_cicl(self):
        #for "roughing minus finish"
        # make "cicle" from tool
        
        tool_cicl = {}
        temp = {}

        dia2 = len(self.tool[0]) + 2
        temptool = numarray.array([[plus_inf] * dia2] * dia2, type="Float32")
        d2range = range(len(self.tool[0]))
        for x in d2range:
            for y in d2range:
                temptool[x+1,y+1] = self.tool[x,y]

        ty = int(dia2/2)
        for x in range(dia2):
            if not isinf(temptool[ty,x]): break

        tx = x
        if not isinf(temptool[ty,tx-1]) or isinf(temptool[ty,tx]):
            print('Error(3): not found tool!',temptool)
            return
        ty2 = ty
        tx2 = tx-1
        temp[ty,tx] = 0
        tool_cicl[ty2,tx2] = temptool[ty2,tx2]
        Nx = True
        while Nx:
            
            if ty2 == ty and tx2 == tx:
                print('Error(4) logik!')
                return
            elif ty2 < ty and tx2 < tx:
                Nx, ii, ty3, tx3 = self.is_inf(temptool,tool_cicl,ty2,tx2+1,ty2+1,tx2)

            elif ty2 < ty and tx2 == tx:
                Nx, ii, ty3, tx3 = self.is_inf(temptool,tool_cicl,ty2,tx2-1,ty2,tx2+1)
            elif ty2 == ty and tx2 < tx:
                Nx, ii, ty3, tx3 = self.is_inf(temptool,tool_cicl,ty2-1,tx2,ty2+1,tx2)

            elif ty2 > ty and tx2 > tx:
                Nx, ii, ty3, tx3 = self.is_inf(temptool,tool_cicl,ty2-1,tx2,ty2,tx2-1)
            elif ty2 == ty and tx2 > tx:
                Nx, ii, ty3, tx3 = self.is_inf(temptool,tool_cicl,ty2-1,tx2,ty2+1,tx2)
            elif ty2 > ty and tx2 == tx:
                Nx, ii, ty3, tx3 = self.is_inf(temptool,tool_cicl,ty2,tx2-1,ty2,tx2+1)

            elif ty2 < ty and tx2 > tx:
                Nx, ii, ty3, tx3 = self.is_inf(temptool,tool_cicl,ty2,tx2-1,ty2+1,tx2)

            elif ty2 > ty and tx2 < tx:
                Nx, ii, ty3, tx3 = self.is_inf(temptool,tool_cicl,ty2-1,tx2,ty2,tx2+1)
            else:
                print('Error(5) logik!')

            if ii:
                ty2, tx2 = ty3, tx3
                try:
                    d = tool_cicl[ty2,tx2]
                    print('Error(7) logik!',Nx,ii,d,temp[ty2,tx2],ty2,tx2,tool_cicl)
                    break
                except:
                    tool_cicl[ty2,tx2] = temptool[ty2,tx2]
            elif not Nx: break
            else:
                ty, tx = ty3, tx3
                try:
                    d = temp[ty,tx]
                    d = tool_cicl[ty,tx]

                    print('Error(6) logik!',Nx,ii,d,temp[ty,tx],ty,tx,tool_cicl)
                    print(temptool)
                    print(temp)
                    break
                except:
                    temp[ty,tx] = 0

        #print('tool cicle: ',sorted(tool_cicl))

        return tool_cicl

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

        if self.roughing_minus_finishing:
            #lin,pix = self.get_RowCol(y,x)
            if self.rmf[y,x] != -plus_inf:
                #return True
                #if self.rmf[y,x] < (self.rd+self.roughing_depth):
                #    return False
                if self.rmf[y,x] >= (self.rd+self.roughing_depth): # and self.image[y,x] <= self.rd   self.image[y,x]+self.map_tool1[y,x]
                    return True
                else:
                    return False
            else:
                return False

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

        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: 
        #if self.layer ==1 or (not self.optimize_path and not self.roughing_minus_finishing): 
            return True
        elif hhh1 <= (self.rd+self.roughing_depth): # +self.ro
            return True
            #if self.rmf[y,x] >= (self.rd+self.roughing_depth): # and self.image[y,x] <= self.rd   self.image[y,x]+self.map_tool1[y,x]
            #    return True
            #else:
            #    return False
            #return True
        return False

    def update_max_min_rmf(self, hhh1,y,x):
        #update 'min' and 'max' in 'rmf' massiv
        if self.roughing_minus_finishing:
            rmf = self.rmf[y,x]
            #if self.min_delta_rf < rmf = True !!! - alredy cheking in function 'not_background'
            if rmf > 0.: print("Error(96)(",y,x,") rmf>0: ",rmf,"hhh1: ",self.image[y,x])  
            #if self.max_rmf < (self.image[y,x]+rmf): self.max_rmf = self.image[y,x]+rmf
            #if self.min_rmf > self.image[y,x]:      self.min_rmf = self.image[y,x]
            if self.max_rmf < rmf : self.max_rmf = rmf
            if self.min_rmf > hhh1: self.min_rmf = hhh1

    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):
        RowMill = (self.convert_rows and not self.convert_cols)

        listj = sorted(make_gk)
        if not RowMill: listj.reverse()  #for cols
        for j in listj:
            Pixel_entry_f,Pixel_entry_l = make_gk[j]
            
            #can't make g-code from 1 pixel
            d_len = 1
            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 self.ppr: print(" GK> Layer: ",self.layer,"obj: ",object_num," RD[",self.rd,"] row:",j," F:",Pixel_entry_f," L:",Pixel_entry_l,\
                P_f,P_l,self.ts,"min:",self.min_rmf,"max:",self.max_rmf) 

            # 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 RowMill:
                #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)
                        for p in points:
                            self.g.cut(*p[1])
                else:
                    if self.ppr : print(self.layer,"g-kode not make from ",P_f," to ",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 self.ppr : print(self.layer,"g-kode not make from ",P_f," to ",P_l," !!!")
            self.g.flush()

            
        self.g.safety()
        convert_scan.reset()

        if self.ppr: print("( End of object: g.safety, convert_rows.reset )")

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

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

        #direction_left = False
        hhh1 = self.get_z_abs(j,start)
        Curn_P = self.not_background(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 >= 0:
                hhh3 = self.get_z_abs(j,t_i)
                Prev_P  = self.not_background(hhh3,j,t_i)
            else:
                hhh3 = -777
                Prev_P  = False

            if Curn_P != Prev_P: 
                if Prev_P: 
                    if self.ppr: print("  [e.0] first border ",Curn_P,Prev_P,t_i,j,hhh3)
                    verify_prev = False
                    Last__border = True
                elif Curn_P: 
                    if self.ppr: 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:
                hhh2 = self.get_z_abs(j,next_i)
                Next_P  = self.not_background(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 self.ppr: 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 self.ppr: 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
                self.update_max_min_rmf(hhh1,j,i)

                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 self.ppr: print("  [e] dont verify prev laer ",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 = 1
                find_entry = False
            else:
                if Blen > self.max_bg_lan: break
                else: 
                    if self.delineating and Pixel_entry_f == -1 and find_entry: 
                        hhh3 = self.get_z_abs(Prev_line,i)
                        if self.not_background(hhh3,Prev_line,i) or Curn_P:
                            if self.ppr: print("  [e] not backg. cur. or prev. line: ",Curn_P,Next_P,i,Prev_line,Blen,hhh3)
                        else: 
                            find_entry = False
                            Blen = Blen + 1
                            if self.ppr: print("  [e] incr. back.len: ",Curn_P,Next_P,i,Prev_line,Blen,hhh3)
                    else:
                        find_entry = False
                        if Pixel_entry_f != -1: Blen = Blen + 1
                        elif i > stop or (self.roughing_minus_finishing and start != 0): Blen = Blen + 1
            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,max_pix,find_entry,Prev_line):
        Pixel_entry_f = -1
        Pixel_entry_l = -1
        Blen = 1;

        hhh1 = self.get_z_abs(j,start)
        Curn_P = self.not_background(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:
                hhh3 = self.get_z_abs(j,t_i)
                Prev_P  = self.not_background(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 self.ppr: print("  [l.0] first border ",Curn_P,Prev_P,t_i,j,hhh3)
                elif Curn_P: 
                    verify_curn = False
                    Last__border = True
                    if self.ppr: print("  [l.0] last border ",Curn_P,Prev_P,t_i,j,hhh3)

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

            next_i = i-1
            if next_i >= 0:
                hhh2 = self.get_z_abs(j,next_i)
                Next_P = self.not_background(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 self.ppr: 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 self.ppr: 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
                self.update_max_min_rmf(hhh1,j,i)

                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 self.ppr: print("  [l] dont verify prev laer ",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 = 1
                find_entry = False
            else:
                if Blen > self.max_bg_lan: break
                else: 
                    if self.delineating and Pixel_entry_l == -1 and find_entry: 
                        hhh3 = self.get_z_abs(Prev_line,i)
                        if self.not_background(hhh3,Prev_line,i) or Curn_P:
                            if self.ppr: print("  [l] not backg. cur. or prev. line: ",Curn_P,Next_P,i,Prev_line,Blen,hhh3,verify_prev,verify_curn)
                        else: 
                            find_entry = False
                            Blen = Blen + 1
                            if self.ppr: print("  [l] incr. back.len: ",Curn_P,Next_P,i,Prev_line,Blen,hhh3,verify_prev,verify_curn)
                    else:
                        find_entry = False
                        if Pixel_entry_l != -1: Blen = Blen + 1
                        elif i < stop or self.roughing_minus_finishing: Blen = Blen + 1
            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,max_line,max_pix):
        make_gk = {}
        mas_p = (-1,-1)
        object_is_found = False

        RowMill = (self.convert_rows and not self.convert_cols)
        if RowMill:
            jrange = range(line_cache, max_line, self.pixelstep)
            if max_line not in jrange: jrange.append(max_line)
        else:
            jrange = range(0, line_cache, self.pixelstep)
            if line_cache not in jrange: jrange.append(line_cache)
            jrange.reverse()  #for cols

        print("  line_cache ",line_cache)

        Prev_line = -1

        st = 1
        Pixel_entry_f = -1
        Pixel_entry_l = -1
        Pixel_entry_f_prev = -1
        Pixel_entry_l_prev = -1
        First_border = False
        Last__border = False
        for j in jrange:    #lines
            progress(j, max_line)

            #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(0, max_pix, j, processed_cur,max_pix,False,Prev_line)
                #*************************************************************************************************************************************************

                if not object_is_found:
                    if RowMill:
                        if line_cache < j: line_cache = j
                    else:
                        if line_cache > j: line_cache = j

                if self.ppr: print(" f-d entry rezult: ",self.layer,object_num,j,Prev_line,First_border,Last__border,Pixel_entry_f,Pixel_entry_l) 
            else:

                # 3 center
                Pixel_entry_f_next = -1
                Pixel_entry_l_next = -1
                # go from Pixel_entry_f to last

                if First_border:
                    # fist - go left - then - right
                    if self.ppr: print("f-d from First border: ",self.layer,object_num,j,Prev_line,First_border,Last__border,Pixel_entry_f,Pixel_entry_l)    

                    # left *************************************************************************************************************************************************
                    find_entry = True
                    Pixel_entry_f_next,Pixel_entry_l_next, First_border, Last__border = self.get_entry_pixels_go_start(Pixel_entry_f,Pixel_entry_f , j, processed_cur,max_pix,find_entry,Prev_line)
                    #*************************************************************************************************************************************************


                    # go to the left  from Pixel_entry_f
                    # right **************************************************************************************************************************
                    if Pixel_entry_l_next == -1:
                        find_entry = True
                        start_f = Pixel_entry_f
                        if self.ppr: print(" ---Don't f-d: ",self.layer,object_num,j,Pixel_entry_f,Pixel_entry_l, Pixel_entry_f_next, Pixel_entry_l_next,First_border,Last__border,find_entry)
                    else:
                        find_entry = False
                        start_f = Pixel_entry_l_next
                        if self.ppr: print(" +++Yes f-d: ",self.layer,object_num,j,Pixel_entry_f,Pixel_entry_l, Pixel_entry_f_next, Pixel_entry_l_next,First_border,Last__border,find_entry)
                    #if self.ppr: print(" dont_cut_angles-",self.layer,object_num,j,Pixel_entry_f,Pixel_entry_l, Pixel_entry_f_next, Pixel_entry_l_next,Pixel_entry_f_prev,Pixel_entry_l_prev)


                    tPixel_entry_f,Pixel_entry_l_next,tFirst_border,tLast__border = self.get_entry_pixels_go_end(start_f, Pixel_entry_l, j, processed_cur,max_pix,find_entry,Prev_line)

                    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 self.ppr: print("f-d from First border rezult: ",self.layer,object_num,j,First_border,Last__border, Pixel_entry_f_next, Pixel_entry_l_next)
                    #**************************************************************************************************************************
                else:
                    # fist - go right - then - left
                    if self.ppr: print("f-d from Last border: ",self.layer,object_num,j,Prev_line,First_border,Last__border,Pixel_entry_f,Pixel_entry_l)    

                    #*************************************************************************************************************************************************
                    find_entry = True
                    Pixel_entry_f_next,Pixel_entry_l_next,First_border,Last__border = self.get_entry_pixels_go_end(Pixel_entry_f, Pixel_entry_l, j, processed_cur,max_pix,find_entry,Prev_line)
                    #*************************************************************************************************************************************************

                    # go to the left  from Pixel_entry_f
                    #**************************************************************************************************************************
                    if Pixel_entry_f_next == -1:
                        find_entry = True
                        start_f = Pixel_entry_f
                        if self.ppr: print(" ---Don't f-d: ",self.layer,object_num,j,Pixel_entry_f,Pixel_entry_l, Pixel_entry_f_next, Pixel_entry_l_next,First_border,Last__border,find_entry)
                    else:
                        find_entry = False
                        start_f = Pixel_entry_f_next
                        if self.ppr: print(" +++Yes f-d: ",self.layer,object_num,j,Pixel_entry_f,Pixel_entry_l, Pixel_entry_f_next, Pixel_entry_l_next,First_border,Last__border,find_entry)


                    Pixel_entry_f_next, tPixel_entry_l, tFirst_border, tLast__border = self.get_entry_pixels_go_start(start_f,Pixel_entry_f , j, processed_cur,max_pix,find_entry,Prev_line)

                    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 self.ppr: print("f-d from Last border rezult:",self.layer,object_num,j,First_border,Last__border, Pixel_entry_f_next, Pixel_entry_l_next)
                    #**************************************************************************************************************************

                if self.dont_cut_angles:
                    if st % 2:
                        if Pixel_entry_l_prev  > Pixel_entry_l: 
                            if self.ppr: print(" dont_cut_angles r:",Prev_line," l_prev",Pixel_entry_l_prev," -> l:",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 self.ppr: print(" dont_cut_angles r:",Prev_line," f_next",Pixel_entry_f_next," -> f:",Pixel_entry_f)
                            Pixel_entry_f = Pixel_entry_f_next
                    else:
                        if Pixel_entry_l_next  > Pixel_entry_l: 
                            if self.ppr: print(" dont_cut_angles r:",Prev_line," l_next",Pixel_entry_l_next," -> l:",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 self.ppr: print(" dont_cut_angles r:",Prev_line," f_prev",Pixel_entry_f_prev," -> f:",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: 
                        if st % 2:
                            mas_p = (Prev_line,Pixel_entry_l)
                        else:
                            mas_p = (Prev_line,Pixel_entry_f)

                    make_gk[Prev_line] = (Pixel_entry_f,Pixel_entry_l)
                    st = st + 1

                    if self.ppr: print(" Add to dict GK: ",self.layer,object_num,Prev_line,Pixel_entry_f,Pixel_entry_l,self.min_rmf,self.max_rmf)   #,P_f,P_l,self.ts) 

                else:
                    if self.ppr and (Pixel_entry_f != -1 or Pixel_entry_l != -1): print("ERROR *******************",self.layer," row: ",j, Pixel_entry_f, Pixel_entry_l)

                if object_is_found and Pixel_entry_f_next == -1 and Pixel_entry_l_next == -1: 
                    if self.ppr: print("Layer ",self.layer," End of object: ",object_num," row: ",j," entry: ",Pixel_entry_f_next,Pixel_entry_l_next)
                    break

                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

            Prev_line = j

        # make g-code from last entrys
        if Pixel_entry_f != -1 and Pixel_entry_l != -1:
            object_is_found = True
            
            if st == 1: 
                if st % 2:
                    mas_p = (Prev_line,Pixel_entry_l)
                else:
                    mas_p = (Prev_line,Pixel_entry_f)

            make_gk[Prev_line] = (Pixel_entry_f,Pixel_entry_l)
            st = st + 1

            if self.ppr: print(" Add to dict GK: ",self.layer,object_num,Prev_line,Pixel_entry_f,Pixel_entry_l,self.min_rmf,self.max_rmf)   #,P_f,P_l,self.ts) 

        #else:
        #    if self.ppr and (Pixel_entry_f != -1 or Pixel_entry_l != -1): print("ERROR *******************",self.layer," row: ",j, Pixel_entry_f, Pixel_entry_l)
    

        return make_gk,line_cache,object_is_found,mas_p

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

        i = 0
        print("current y: ",y1," x: ",x1)

        while True:

            if i > max_:
                print("BREAK: LOGIK ERROR(): ",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:
                print("BREEEEEK: 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]
                    print(" find next entry: ",y,x," iter: ",i)
                    return y,x
                except KeyError:
                    m = 0

        return y1,x1

    def mill_roughing_minus_finishing(self, convert_scan, primary):

        print("(Start mill roughing mode minus finishing mode {objectiv}.)")
        #return

        max_line,max_pix = self.get_RowCol(self.w1,self.h1)
        mas_make_gk = {}

        #step = roughing_depth
        r = self.image.max()
        imin = self.image.min()
        while r > imin:
            #step = roughing_depth
            r = max(r - self.roughing_depth,imin)
            if (r - imin) < (self.roughing_depth*0.6): r = imin
            self.rd = r
            print("  Layer ",r," min: ",imin)


            processed_cur = {}
            mas_p={}

            RowMill = (self.convert_rows and not self.convert_cols)
            if RowMill: line_cache = 0
            else:       line_cache = self.h1

            object_is_found = True
            object_num = 0
            while object_is_found:

                object_num = object_num + 1
                print("  object_num ",object_num)

                #if object_num >= 20: break
                     
                self.max_rmf = -plus_inf    #self.MaxBackground_down
                self.min_rmf =  plus_inf    #0.

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

                if object_is_found:

                    if self.max_rmf == -plus_inf: 
                        self.max_rmf = 0. #self.MaxBackground_up
                        print("Error[97.1] in object: ",object_num," self.max_rmf not found",self.min_rmf, self.max_rmf)
                    if self.min_rmf ==  plus_inf: 
                        self.min_rmf = self.MaxBackground_down
                        print("Error[97.2] in object: ",object_num," self.min_rmf not found",self.min_rmf, self.max_rmf)
                    if self.max_rmf <= self.min_rmf: 
                        #print("Error(97) in object: ",object_num," self.min_rmf >= self.max_rmf",self.min_rmf, self.max_rmf)
                        print("Error[97] in object: ",object_num," self.min_rmf >= self.max_rmf",self.min_rmf, self.max_rmf,self.MaxBackground_down)
                        #tmt = self.max_rmf
                        #self.max_rmf = self.min_rmf
                        #self.min_rmf = tmt
                        #self.max_rmf = 0.
                        self.min_rmf = self.MaxBackground_down

                    #if len(make_gk) > 1:
                    #    mas_make_gk[y,x] = make_gk
                    #    mas_p[y,x] = (self.min_rmf,self.max_rmf,object_num)
                    mas_make_gk[y,x] = make_gk
                    mas_p[y,x] = (self.min_rmf,self.max_rmf,object_num)
                    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

                ##step = roughing_depth
                #r = self.max_rmf
                #while r > self.min_rmf:
                #    r = max(r - self.roughing_depth,self.min_rmf)
                #    self.rd = r
                #    # make g-code from entrys in dict make_gk
                #    self.gk_maker(make_gk, max_pix, processed_cur, convert_scan, primary,object_num)

            #return

            # sort entrys for minimiz path
            mas_p2={}
            tmm = sorted(mas_p)
            if not RowMill: tmm.reverse()  #for cols
            j = len(tmm)
            print("len ",j)
            ld = j
            #add first point
            if j > 0:
                y,x = tmm[0]
                #if only one
                mas_p2[0] = (y,x,mas_p[y,x])   #self.min_rmf,self.max_rmf
                del mas_p[y,x]
                j = len(mas_p)
                
            #sorting...
            while j > 1:
                progress(j, ld)
                y2,x2 = self.find_next_e(mas_p,y,x,max_line,max_pix)
                if y2 == y and x2 == x: 
                    if j > 1: print("Error logik (it's can't be) ",j, x,y,x2,y2)
                    break
                y = y2
                x = x2
                mas_p2[ld-j] = (y,x,mas_p[y,x])
                del mas_p[y,x]
                j = len(mas_p)

            #add last point(s) - mast be only one! bat...
            #if j == 1: mas_p2[ld-j] = (y,x,mas_p[y,x])
            for y,x in mas_p: 
                mas_p2[ld-j] = (y,x,mas_p[y,x])   #self.min_rmf,self.max_rmf
                j = j - 1

            print("len2 ",len(mas_p2))

            # make g-code from entrys in dict make_gk
            for num in mas_p2:
                progress(num, ld)
                y,x,(self.min_rmf,self.max_rmf,object_num) = mas_p2[num]


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


    def mill_objectiv(self, convert_scan, primary):

        print("Start mill objectiv mode. layer ",self.layer,self.MaxBackground_down,self.MaxBackground_up,self.image.min(),self.background_border,self.rd)  

        processed_cur = {}

        RowMill = (self.convert_rows and not self.convert_cols)
        if RowMill: line_cache = 0
        else:       line_cache = self.h1

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

        object_is_found = True
        object_num = 0
        while object_is_found:


            object_num = object_num + 1
            print("  object_num ",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,max_line,max_pix)

            # 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
            hhh1 = self.get_z_abs(j,i)
            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 = len(jrange)
        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  = len(irange);
            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 = len(irange);
                    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 = len(irange);
                    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 = len(jrange)
        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  = len(irange);
            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 = len(irange);
                    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 = len(irange);
                    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:
            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:
            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),
        ("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,
        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)"),
        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")
            vars['optimize_path'].set(False)
        else:
            if vars['optimize_path'].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")
            widgets['roughing_offset'].configure(state="disabled")
            vars['roughing_offset'].set(0.00)
            vars['roughing_minus_finishing'].set(False)
            trace_roughing_minus_finishing()
        else:
            widgets['roughing_offset'].configure(state="normal")
            #vars['roughing_completed'].set(False)
            if vars['roughing_offset'].get() > 0:
                widgets['roughing_depth'].configure(state="normal")
            else:            
                widgets['roughing_depth'].configure(state="disabled")

    def trace_roughing_completed(*args):
        if vars['roughing_completed'].get():
            if not vars['optimize_path'].get():
                vars['optimize_path'].set(True)
        trace_optimize_path()

    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):
        if vars['roughing_minus_finishing'].get():
            vars['optimize_path'].set(False)
            vars['max_bg_lan'].set(1)
            trace_optimize_path()
            widgets['roughing_depth'].configure(state="normal")
            widgets['min_delta_rf'].configure(state="normal")
            widgets['tool_diameter_roughing'].configure(state="normal")
            widgets['tool_type_roughing'].configure(state="normal")
        else:
            trace_offset()
            widgets['min_delta_rf'].configure(state="disabled")
            widgets['tool_diameter_roughing'].configure(state="disabled")
            widgets['tool_type_roughing'].configure(state="disabled")

    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_completed'].trace('w', trace_roughing_completed)
    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

    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'])

if __name__ == '__main__':
    main()

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