#!/usr/bin/env python3
# We will use the inkex module with the predefined Effect base class.
import inkex
import math
from inkscape_path import *
from lxml import etree

#Constants defined here
WoodHingeSize = 3               #To be multiplied by thickness 
WoodHingeInternalCircle = 2     #To be multiplied by thickness  
WoodHingeRect = 1.5             #To be multiplied by thickness

SteelHingeSpacing = 0.3
RadiusSteelHingeAxis = 1.3      #Use axis about 2.4mm diameter, I use nails 2.3mmx70mm
MinMove = 1e-2                  #Minimum distance betwwen two points (0.01 mm !)

#Global variables used for the whole program
thickness = 0
burn = 0
fDebug = None

def distance2Points(x0, y0, x1, y1):
    return math.sqrt((x1-x0)**2 + (y1-y0)**2)

def DebugMsg(s):
    '''
    Print a debug message into debug file if debug file is defined
    '''
    if fDebug:
        fDebug.write(s)

def OpenDebugFile():
    global fDebug
    try:
        fDebug = open( 'DebugGenericBox.txt', 'w')
    except IOError:
        pass
    DebugMsg("Start processing\n")

def CloseDebugFile():
    global fDebug
    if fDebug:
        fDebug.close()
        fDebug = None

def drawHole(path, x0, y0, dx, dy, burn):
    ''' 
    Add a rectangle starting at x0,y0 and with a length dx and width dy to the current path
    burn is the burn factor, so actual coordinates are modified by burn/2
    '''
    path.MoveTo(x0+burn/2, y0+burn/2)
    path.LineToVRel(dy-burn)
    path.LineToHRel(dx-burn)
    path.LineToVRel(-dy+burn)
    path.LineToHRel(-dx+burn)


class Ellipse:
    '''
    This class defines some functions that will be used with the coffin style lid
    '''
    def __init__(self, a, b):
        '''
        a and b are the ellipse parameters
        '''
        self.a = a
        self.b = b
        self.Start = 0
    
    def length(self):
        '''
        Compute a rather good approwimation of the ellipse length.
        Use the formula Ramanujan 2
        '''
        h = (self.a - self.b)*(self.a - self.b)/((self.a + self.b)*(self.a + self.b))
        
        l = math.pi*(self.a+self.b)*(1.0 + 3*h/(10.0+math.sqrt(4.0-3*h)))
        return l
    
    def length2Angle(self, l):
        '''
        Compute the angle which gives the given length on the ellipse.
        The ellipse perimeter couldn't be computed from known functions, so we use a discrete integral computation
        In order to save time, this function should be called with increasing value of l, i.e. it doesn't recompute values less than previous parameter l
        '''
        CurDistance = PrevDistance = self.LastDistance
        index = self.CurPoint
        while CurDistance < l and index < self.nPoints:
            PrevDistance = CurDistance
            Alpha = (index +0.5)* self.StepAngle + self.Start
            CurDistance += self.StepAngle*math.sqrt((self.a*math.sin(Alpha))**2 + (self.b*math.cos(Alpha))**2)
            index += 1
        #Will stop here, record values
        self.LastDistance = CurDistance
        self.CurPoint = index
        #Interpolate between the last points to increase precision
        if CurDistance > PrevDistance:
            Delta = (l - PrevDistance)/(CurDistance - PrevDistance)*self.StepAngle
            return (index-1)*self.StepAngle + Delta + self.Start
        else:
            return index*self.StepAngle + self.Start

    def Compute_Ellipse_Params(self, Start, End):
        self.length_ellipse = self.length() * (End - Start)/math.pi/2.0
        self.Start = Start
        self.End = End
        #Compute length between notches, each notch is about 2mm wide, total length is 2*( l_between_notches + 1)
        if self.length_ellipse < 80:
            self.l_between_notches = 1
        elif self.length_ellipse < 150:
            self.l_between_notches = 2
        elif self.length_ellipse < 250:
            self.l_between_notches = 3
        else:
            self.l_between_notches = 4
        self.nb_Ellipse_Notch = int(round(self.length_ellipse / (2*( self.l_between_notches + 1) + 2),0)) #Add a notch at the end
        self.size_Ellipse_Notch = self.length_ellipse / (self.nb_Ellipse_Notch *( self.l_between_notches + 1) + 1)
        self.Size_betweenNotches = self.l_between_notches*self.size_Ellipse_Notch
        self.nb_point_between_notches = int(round(self.Size_betweenNotches))
        self.SizeBetweenPoints = self.Size_betweenNotches / self.nb_point_between_notches
        DebugMsg("Ellipse length ="+str(self.length_ellipse)+" nb_Ellipse_Notch="+str(self.nb_Ellipse_Notch)
                 +" size_Ellipse_Notch="+str(self.size_Ellipse_Notch)+" Distance between notches="+str((self.l_between_notches+1)*self.size_Ellipse_Notch)
                 +"mm, Nb point_between nocthes="+str(self.nb_point_between_notches)
                 +" Total Size Notch ="+str(self.size_Ellipse_Notch*self.nb_Ellipse_Notch*(self.l_between_notches+1)+self.size_Ellipse_Notch)+'\n')
        #Compute the number of points used to compute the integration, and init values to be used by length2Angle
        if self.length_ellipse < 500:
            self.nPoints = 20000     #Error will be less than 0.01mm
        elif self.length_ellipse < 2000:
            self.nPoints = 100000
        else:
            self.nPoints = 500000    #Beware compute time will be higher.

    def drawNotchedEllipse(self, path, Start, End, Offset):
        '''
        draw the notched ellipse from Start Angle to End Angle using path path
        '''
        self.Compute_Ellipse_Params(Start, End)
        #Compute offset to be added to coordinates such as the start point matches with start angle
        xOffset = -self.a*math.cos(Start) + Offset[0]
        yOffset = -self.a*math.sin(Start) + Offset[1]
        self.StepAngle = (End - Start)/self.nPoints
        self.CurPoint = 0
        self.LastDistance = 0.0     #At the start point
        DebugMsg("nPoints ="+str(self.nPoints)+" StepAngle="+str(self.StepAngle)+"\n")
        DebugMsg("Offset Ellipse="+str((xOffset, yOffset))+'\n')
        '''
        #TEST
        a1 = self.length2Angle(1.0)
        DebugMsg("length2Angle(1.0) --> "+str(a1*180/math.pi)+'\n')
        a2 = self.length2Angle(2.0)
        DebugMsg("length2Angle(2.0) --> "+str(a2*180/math.pi)+'\n')
        a3 = self.length2Angle(5.0)
        DebugMsg("length2Angle(5.0) --> "+str(a3*180/math.pi)+'\n')
        a4 = self.length2Angle(10.0)
        DebugMsg("length2Angle(10.0) --> "+str(a4*180/math.pi)+'\n')
        a5 = self.length2Angle(self.length_ellipse/2.0)
        DebugMsg("length2Angle(length/2) --> "+str(a5*180/math.pi)+'\n')
        a6 = self.length2Angle(3*self.length_ellipse/4.0)
        DebugMsg("length2Angle(length*0.75) --> "+str(a6*180/math.pi)+'\n')
        a7 = self.length2Angle(self.length_ellipse-2.0)
        DebugMsg("length2Angle(length-2) --> "+str(a7*180/math.pi)+'\n')
        a8 = self.length2Angle(self.length_ellipse-1.0)
        DebugMsg("length2Angle(length-1) --> "+str(a8*180/math.pi)+'\n')
        a9 = self.length2Angle(self.length_ellipse)
        DebugMsg("length2Angle(length) --> "+str(a9*180/math.pi)+'\n')
        self.StepAngle = (End - Start)/self.nPoints
        self.CurPoint = 0
        self.LastDistance = 0.0     #At the start point
        a9 = self.length2Angle(self.length_ellipse)
        DebugMsg("length2Angle(length), fresh start --> "+str(a9*180/math.pi)+'\n')
        self.CurPoint = 0
        self.LastDistance = 0.0     #At the start point
        #End TEST
        '''
        #The side face is "internal", that is the notches are towards the exterior.
        DeltaAngleNotches = -math.pi/2      #Angle from the tangeant
        CurAngle = Start                    #Starting point
        CurDistance = 0                     #Distance on ellipse
        #Now for all notches but the last one
        for i in range(self.nb_Ellipse_Notch):
            #Start with the notch itself, but first compute the tangeant at the current point
            theta = math.atan2(self.b*math.cos(CurAngle), -self.a*math.sin(CurAngle))
            AngleNotch = theta + DeltaAngleNotches
            #Draw notch , start position on ellipse + Notch itself
            x = self.a * math.cos(CurAngle) + thickness * math.cos(AngleNotch) + xOffset
            y = self.b * math.sin(CurAngle) + thickness * math.sin(AngleNotch) + yOffset
            DebugMsg("Notch, Pos without offset="+str((self.a * math.cos(CurAngle) + thickness * math.cos(AngleNotch), self.b * math.sin(CurAngle) + thickness * math.sin(AngleNotch) ))+" WithOffset"+str((x,y))+'\n')
            path.LineTo(x, y)
            #Now the side parralel to the ellipse
            x += self.size_Ellipse_Notch * math.cos(theta)
            y += self.size_Ellipse_Notch * math.sin(theta)
            path.LineTo(x, y)
            #Now back to the ellipse, do not use the angle to come back to the ellipse but compute the position on ellipse. 
            #This will give a better approximation of the ellipse. As ellipse is convex, the interior of the notch will be shorter than the exterior
            CurDistance += self.size_Ellipse_Notch
            CurAngle = self.length2Angle(CurDistance)
            x = self.a * math.cos(CurAngle) + xOffset
            y = self.b * math.sin(CurAngle) + yOffset
            path.LineTo(x, y)
            #Now draw the interior line, but mm by mm to have a good approximation of the ellipse 
            for j in range(self.nb_point_between_notches):
                CurDistance += self.SizeBetweenPoints
                CurAngle = self.length2Angle(CurDistance)
                x = self.a * math.cos(CurAngle) + xOffset
                y = self.b * math.sin(CurAngle) + yOffset
                path.LineTo(x, y)
            #We are now ready to draw the next notch
        #Now draw the last notch, but draw it "backward" to have a symetric view.
        theta = math.atan2(self.b*math.cos(End), -self.a*math.sin(End))
        AngleNotch = theta + DeltaAngleNotches
        #Draw notch , start position on ellipse + Notch itself
        x_end_notch = self.a * math.cos(End) + thickness * math.cos(AngleNotch) + xOffset
        y_end_notch = self.b * math.sin(End) + thickness * math.sin(AngleNotch) + yOffset
        #Now the side parralel to the ellipse
        x_start_notch = x_end_notch - self.size_Ellipse_Notch * math.cos(theta)
        y_start_notch = y_end_notch - self.size_Ellipse_Notch * math.sin(theta)
        path.LineTo(x_start_notch, y_start_notch)
        path.LineTo(x_end_notch, y_end_notch)
        #For the last point, we will use the End Parameter
        x = self.a * math.cos(End) + xOffset
        y = self.b * math.sin(End) + yOffset
        path.LineTo(x,y)
        #We should be arrived at the last point now !
    
    #   Generate vertical lines for flex
    #   Parameters : StartX, StartY, size, nunmber of lines and +1 if lines goes up and -1 down
    def GenLinesFlex(self, StartX, StartY, Size, nLine, UpDown, path):
        DebugMsg("Enter GenLinesFlex, Pos="+str((StartX, StartY))+" nSegment="+str(nLine)+" Size Segment="+str(Size)+" UpDown="+str(UpDown)+" End="+str((StartX, StartY+nLine*(Size+2)-2))+'\n')
        for i in range(nLine):
            path.Line(StartX, StartY, StartX, StartY + UpDown*Size)
            DebugMsg("GenLinesFlex from "+str((StartX, StartY))+" to "+str((StartX, StartY + UpDown*Size))+'\n')
            StartY += UpDown*(Size+2)
    
    def drawFlexEllipse(self, path, height, SkipFlex, Position):
        '''
        draw the flex lines for the ellipse
        After this path will be at the right/bottom corner of the flex line.
        '''
        xpos = Position[0]
        ypos = Position[1]
        #First compute angles of each notch in order to skip unnecessary flex lines
        
        self.StepAngle = (self.End - self.Start)/self.nPoints
        self.CurPoint = 0
        self.LastDistance = 0.0     #At the start point
        CurAngle = self.Start                    #Starting point
        CurDistance = 0                     #Distance on ellipse
        DeltaNotch = (self.l_between_notches+1)*self.size_Ellipse_Notch
        ListDistance = []
        #Now for all notches but the last one
        for i in range(self.nb_Ellipse_Notch):
            #Start with the notch itself, but first compute the tangeant at the current point (x0,y0)
            LastAngle = CurAngle
            # with the line equation in the form alpha*x + beta*y + gamma = 0
            alpha = self.b * math.cos(CurAngle)
            beta = self.a * math.sin(CurAngle)
            gamma = -(self.a*self.b)
            CurDistance += DeltaNotch
            CurAngle = self.length2Angle(CurDistance)
            #Now compute distance between tangeant and next point.
            x1 = self.a * math.cos(CurAngle)    
            y1 = self.b * math.sin(CurAngle)    
            #Distance betwwen line and point is (alpha * pt.x + beta * pt.y + gamma)*(alpha * pt.x + beta * pt.y + gamme)/sqrt(alpha*alpha + beta*beta)
            distance = abs(alpha * x1 + beta * y1 + gamma)/math.sqrt(alpha*alpha + beta*beta)
            ListDistance.append(distance)
            DebugMsg("LastAngle ="+str(round(180*LastAngle/math.pi,2))+" CurAngle="+str(round(180*CurAngle/math.pi,2))+" NewPoint="+str((x1,y1))+" distance="+str(distance)+'\n')
        #and for the last one, repeat the previous
        ListDistance.append(distance)
        
        '''
        Now, this is the real flex line drawing
        '''
        #Compute number of vertical lines. Each long mark should be at most 50mm long to avoid failures
        TotalHeight = height+2*thickness
        nMark = int( TotalHeight / 50) + 1       #Compute number of lines
        nMark = max(nMark, 2)   # At least 2 marks
        #Sizes of short and long lines to make flex
        LongMark = (TotalHeight / nMark) - 2.0          #Long Mark equally divide the height
        ShortMark = LongMark/2                          # And short mark should lay at center of long marks
        DebugMsg("\ndrawFlexEllipse, Pos="+str(Position)+" TotalHeight="+str(TotalHeight)+" nMark="+str(nMark)+" LongMark="+str(LongMark)+" ShortMark="+str(ShortMark)+'\n')
        for i in range(self.nb_Ellipse_Notch):
            '''
            For each set notch + interval between notches, always start with a notch, and we are external in this case
            The path is designed as it will lead to "optimal" move of the laser beam.
            
            First draw the nocth and the line inside the notch
            First edge of the notch, start with a short line, then nMark-1 long lines then a short one. This will cover the entire height+2*thickness
            The the second edge of the notch, the same but drawn backwards (bottom to top)
            and at last the line inside the notch, drawn from top to bottom
            '''
            DebugMsg("Notch("+str(i)+"), SkipFlex="+str(SkipFlex)+" ListDistance[i]="+str(ListDistance[i])+'\n')
            #Draw the edge line from Top to Bottom
            self.GenLinesFlex(xpos, ypos, ShortMark, 1, 1, path)
            #Then nMark-1 long Lines
            self.GenLinesFlex(xpos, ypos+ShortMark+2, LongMark, nMark-1, 1, path)
            #And the last short line
            self.GenLinesFlex(xpos, ypos+TotalHeight-ShortMark, ShortMark, 1, 1, path)
            #Now we are at the bottom of the Flex face, draw the bottom notch
            path.Line(xpos, ypos+height+thickness, xpos+self.size_Ellipse_Notch, ypos+height+thickness)
            #Then draw the same pattern for the other side of the notch, but bottom to top
            self.GenLinesFlex(xpos+self.size_Ellipse_Notch, ypos+TotalHeight, ShortMark, 1, -1, path)
            #Then nMark-1 long Lines
            self.GenLinesFlex(xpos+self.size_Ellipse_Notch, ypos+TotalHeight-ShortMark-2, LongMark, nMark-1, -1, path)
            #And the last short line that will reach the top external edge
            self.GenLinesFlex(xpos+self.size_Ellipse_Notch, ypos+ShortMark, ShortMark, 1, -1, path)
            #then the top notch
            path.Line(xpos+self.size_Ellipse_Notch, ypos + thickness, xpos, ypos+thickness)
            #Then draw the long lines inside the notch, first and last will be shorter by thickness
            #This line is drawn from top to bottom, and start at 1mm from the interior of the notch 
            self.GenLinesFlex(xpos+self.size_Ellipse_Notch/2, ypos+thickness+1, LongMark-thickness, 1, 1, path)
            #Then the remaining inside if any
            if nMark > 2:
                self.GenLinesFlex(xpos+self.size_Ellipse_Notch/2, ypos+3+LongMark, LongMark, nMark-2, 1, path)
            #Then the last one, shorter also, will reach internal bottom + 1mm
            self.GenLinesFlex(xpos+self.size_Ellipse_Notch/2, ypos+TotalHeight-LongMark-1, LongMark-thickness, 1, 1, path)
            '''
            At this point we are near the bottom line.
            First draw the external line up to the next notch
            '''
            xpos += self.size_Ellipse_Notch     #xpos is the other side of the notch
            path.Line(xpos, ypos+TotalHeight, xpos+self.Size_betweenNotches, ypos+TotalHeight)
            
            ''' 
            Then draw the lines between external top and bottom, but only if needed when SkipFlex is true
            They are 2*l_between_notches - 1 lines, so the number is always odd
            Even indexes are made of long lines only and drawn Bottom to Top.
            Odd indexes are made of one short line, nMark-2 long lines then a short line and rawn top to bottom
            As total number is odd, wa always end with long lines drawn from bottom to top, so the last position will be near the top line
            '''
            #If the ellipse is not very round at this point, in order to save laser time, draw only lines inside the notch and the line just before the notch
            drawAllLines = False
            if SkipFlex == False or ListDistance[i] > 0.5:
                drawAllLines = True
            for j in range(2*self.l_between_notches-1):
                if j == 2*self.l_between_notches-2 or drawAllLines:
                    if (j % 2)==0:
                        #even, draw long lines bottom to top
                        self.GenLinesFlex(xpos+(j+1)*self.size_Ellipse_Notch/2, ypos+TotalHeight-1, LongMark, nMark, -1, path)
                    else:
                        #Odd, draw short line, nMark-2 long lines then a short line, top to bottom
                        self.GenLinesFlex(xpos+(j+1)*self.size_Ellipse_Notch/2, ypos+1, ShortMark-1, 1, 1, path)
                        #Then nMark-1 long Lines
                        self.GenLinesFlex(xpos+(j+1)*self.size_Ellipse_Notch/2, ypos+ShortMark+2, LongMark, nMark-1, 1, path)
                        #And the last short line
                        self.GenLinesFlex(xpos+(j+1)*self.size_Ellipse_Notch/2, ypos+TotalHeight-ShortMark, ShortMark-1, 1, 1, path)
            #Now we are near the top line, draw the line up to the next notch
            path.Line(xpos, ypos, xpos+self.Size_betweenNotches, ypos)
            #And we are ready to draw the next flex pattern
            xpos += self.Size_betweenNotches
         
        '''
        Now draw the pattern for the last notch
        '''
        #Draw the edge line from Top to Bottom
        self.GenLinesFlex(xpos, ypos, ShortMark, 1, 1, path)
        #Then nMark-1 long Lines
        self.GenLinesFlex(xpos, ypos+ShortMark+2, LongMark, nMark-1, 1, path)
        #And the last short line
        self.GenLinesFlex(xpos, ypos+TotalHeight-ShortMark, ShortMark, 1, 1, path)
        #Now we are at the bottom of the Flex face, draw the bottom notch
        path.Line(xpos, ypos+height+thickness, xpos+self.size_Ellipse_Notch, ypos+height+thickness)
        #Then draw the same pattern for the other side of the notch, but bottom to top
        self.GenLinesFlex(xpos+self.size_Ellipse_Notch, ypos+TotalHeight, ShortMark, 1, -1, path)
        #Then nMark-1 long Lines
        self.GenLinesFlex(xpos+self.size_Ellipse_Notch, ypos+TotalHeight-ShortMark-2, LongMark, nMark-1, -1, path)
        #And the last short line that will reach the top external edge
        self.GenLinesFlex(xpos+self.size_Ellipse_Notch, ypos+ShortMark, ShortMark, 1, -1, path)
        #then the top notch
        path.Line(xpos+self.size_Ellipse_Notch, ypos + thickness, xpos, ypos+thickness)
        #Then draw the long lines inside the notch, first and last will be shorter by thickness
        #This line is drawn from top to bottom, and start at 1mm from the interior of the notch 
        self.GenLinesFlex(xpos+self.size_Ellipse_Notch/2, ypos+thickness+1, LongMark-thickness, 1, 1, path)
        #Then the remaining inside if any
        if nMark > 2:
            self.GenLinesFlex(xpos+self.size_Ellipse_Notch/2, ypos+3+LongMark, LongMark, nMark-2, 1, path)
        #Then the last one, shorter also, will reach internal bottom + 1mm
        self.GenLinesFlex(xpos+self.size_Ellipse_Notch/2, ypos+TotalHeight-LongMark-1, LongMark-thickness, 1, 1, path)
        xpos += self.size_Ellipse_Notch     #xpos is the other side of the notch
        path.MoveTo(xpos, ypos+TotalHeight)      #Path will be at the end of flex line on the BOTTOM edge. 
        DebugMsg("Path pos ="+str((xpos, ypos+TotalHeight))+'\n')

class CornerPoint:
    '''
    This class stores data about corners, to be used later to draw the faces of the box
    position is a tuple giving the position of the corner
    '''
    def __init__(self, position, radius, x_internal, y_internal, WoodHingeCorner = False):
        self.x_internal = x_internal
        self.y_internal = y_internal
        self.WoodHingeCorner = WoodHingeCorner
        if radius > 0:
            self.radius = radius
        else:
            self.radius = 0
        #Compute position of circle center, do it now because it is always here, even if corner moves (internal/external)
        if  position[0]  <= thickness:
            #Left corner        
            self.xc = position[0] + self.radius
        else:
            #Right corner        
            self.xc = position[0] - self.radius
        if  position[1]  <= thickness:
            #Top corner        
            self.yc = position[1] + self.radius
        else:
            #Bottom corner        
            self.yc = position[1] - self.radius

        #Compute position of corner, given internal or external position of finger joints
        if x_internal:
            self.x_corner = position[0]
        elif position[0] <= thickness:
            self.x_corner = position[0] - thickness
            if self.radius > 0:
                self.radius += thickness            # Change radius accordingly, beware do it only for x direction (only once !)
        else:
            self.x_corner = position[0] + thickness
            if self.radius > 0:
                self.radius += thickness
        if y_internal:
            self.y_corner = position[1] 
        elif position[1] <= thickness:
            self.y_corner = position[1] - thickness
        else:
            self.y_corner = position[1] + thickness
        #Compute position of line of finger joints
        if position[0]  <= thickness and position[1]  <= thickness:
            #Top left corner, compute positions of start/end of corners
            self.quadrant = 0
            self.x_start_joint = position[0] + self.radius      #X direction, do not take into account Internal/External
            self.y_start_joint = self.y_corner
            self.x_end_joint = self.x_corner
            self.y_end_joint = position[1] + self.radius        #Y Direction, do not take into account Internal/External
        elif position[1]  <= thickness:
            #Top right corner
            self.quadrant = 1
            self.x_start_joint = self.x_corner
            self.y_start_joint = position[1] + self.radius
            self.x_end_joint = position[0] - self.radius
            self.y_end_joint = self.y_corner
        elif position[0]  <= thickness:
            #Bottom left corner
            self.quadrant = 3
            self.x_start_joint = self.x_corner
            self.y_start_joint = position[1] - self.radius
            self.x_end_joint = position[0] + self.radius
            self.y_end_joint = self.y_corner
        else:
            #Bottom right corner
            self.quadrant = 2
            self.x_start_joint = position[0] - self.radius
            self.y_start_joint = self.y_corner
            self.x_end_joint = self.x_corner
            self.y_end_joint = position[1] - self.radius
        #Specific case for WoodHingeCorner, "corner" is 3/4 of the circle out of the corner
        if WoodHingeCorner:
            if self.quadrant == 0:
                self.y_end_joint = self.y_corner + WoodHingeSize*thickness
                self.x_start_joint = self.x_corner + WoodHingeSize*thickness                
            elif self.quadrant == 1:
                self.x_end_joint = self.x_corner - WoodHingeSize*thickness
                self.y_start_joint = self.y_corner + WoodHingeSize*thickness
        DebugMsg("End CornerPoint init. Corner="+str((self.x_corner, self.y_corner))+" Circle="+str((self.xc, self.yc))+" StartJoint="+str((self.x_start_joint, self.y_start_joint))+" EndJoint="+str((self.x_end_joint, self.y_end_joint))+" WoodHingeCorner="+str(self.WoodHingeCorner)+'\n')
    
    def drawCorner(self, path):
        '''
        Draw the lines around the corner using path
        Start position of the path should be (x_end_joint, y_end_joint), not checked nor enforced
        End Position is (x_start_joint, y_start_joint)
        '''
        if self.WoodHingeCorner:
            #Specific case, draw 3/4 of a circle of radius WoodHingeSize*thickness + plus a small segment of size thickness
            if self.quadrant == 0:      #Left corner
                DebugMsg("drawCorner_WoodHinge Left: StartPoint="+str((self.x_end_joint, self.y_end_joint))+" Circle="+str((self.xc, self.yc))+ " EndPoint="+str((self.x_start_joint, self.y_start_joint))+'\n')
                path.LineToHRel(-thickness)
                path.drawQuarterCircle(self.x_corner-thickness, self.y_corner, WoodHingeSize*thickness, 3)        #Start Lower Left
                path.drawQuarterCircle(self.x_corner-thickness, self.y_corner, WoodHingeSize*thickness, 0)        #Start Upper Left
                path.drawQuarterCircle(self.x_corner-thickness, self.y_corner, WoodHingeSize*thickness, 1)        #Start Upper Right
            elif self.quadrant == 1:      #Right corner
                DebugMsg("drawCorner_WoodHinge Right: StartPoint="+str((self.x_end_joint, self.y_end_joint))+" Circle="+str((self.xc, self.yc))+ " EndPoint="+str((self.x_start_joint, self.y_start_joint))+'\n')
                path.LineToHRel(thickness)
                path.drawQuarterCircle(self.x_corner+thickness, self.y_corner, WoodHingeSize*thickness, 0)        #Start Upper Left
                path.drawQuarterCircle(self.x_corner+thickness, self.y_corner, WoodHingeSize*thickness, 1)        #Start Upper Right
                path.drawQuarterCircle(self.x_corner+thickness, self.y_corner, WoodHingeSize*thickness, 2)        #Start Lower Right
                path.LineToHRel(-thickness)
        elif self.radius > 0:
            #DebugMsg("drawCorner radius Center"+str((self.xc, self.yc))+" RAdius="+str(self.radius)+ " quadrant="+str(self.quadrant)+'\n')
            path.drawQuarterCircle(self.xc, self.yc, self.radius, self.quadrant)
        else:
            DebugMsg("drawCorner: StartPoint="+str((self.x_end_joint, self.y_end_joint))+" Corner="+str((self.x_corner, self.y_corner))+ " EndPoint="+str((self.x_start_joint, self.y_start_joint))+'\n')
            if distance2Points(self.x_end_joint, self.y_end_joint, self.x_corner, self.y_corner) > MinMove:
                #Draw line up to real corner
                path.LineTo(self.x_corner, self.y_corner)
            if distance2Points(self.x_start_joint, self.y_start_joint, self.x_corner, self.y_corner) > MinMove:
                #Draw line between corner and start of joints
                path.LineTo(self.x_start_joint, self.y_start_joint)


class NotchLine:
    '''
    This class deals with straight lines with or without finger joints
    start and end parameters are actually tuples giving position (x,y) and internal/external status of each point
    The angle give the direction, it couldn't be easily computed from start and ending point because of internal/external status
    If parameter DrawHalf is < 0, only first half of line will be drawn, if > 0 only second half.
    When DrawHalf is null both parts will be drawn
    Beware, DrawHalf could be < 0 or > 0 only when Status (Internal/External) are indentical.
    '''
    def __init__(self, start, end, angle, finger_joint_size, DrawHalf=0):
        self.StartX = start[0]
        self.StartY = start[1]
        self.EndX = end[0]
        self.EndY = end[1]
        self.StartStatus = start[2]
        self.EndStatus = end[2]
        self.JointSize = finger_joint_size
        self.Angle = angle
        self.size_line_joint = 0
        self.start_line_joint_x = self.StartX
        self.start_line_joint_y = self.StartY
        self.end_line_joint_x = self.EndX
        self.end_line_joint_y = self.EndY
        self.DrawHalf = DrawHalf
        DebugMsg("NotchLine_init, StartPoint="+str(start)+" EndPoint="+str(end)+" Joint_size="+str(finger_joint_size)+" DrawHalf="+str(DrawHalf)+'\n')
        # Compute size of all finger joints
        # Compute size as a distance to deal with every direction.
        size = math.sqrt((self.EndX - self.StartX)*(self.EndX - self.StartX) + (self.EndY - self.StartY)*(self.EndY - self.StartY))
        # Compute number of joints
        if finger_joint_size == 0:          #  No finger joint
            self.nb_finger_joint = 0
            if DrawHalf != 0:               #Draw only half of line (specific case for rounded flex)
                self.EndX = (self.StartX + self.EndX) / 2
                self.EndY = (self.StartY + self.EndY) / 2
                self.end_line_joint_x = self.EndX
                self.end_line_joint_y = self.EndY
        elif start[2] == end[2]:
            # Same status, internal/external, the number of notches should be odd (at least 3)
            if size < 3 * finger_joint_size:
                self.nb_finger_joint = 0
            else:
                self.nb_finger_joint = 2*int((size-finger_joint_size) / (2*finger_joint_size)) + 1
            self.size_line_joint = self.nb_finger_joint * finger_joint_size
            # compute start and stop of finger joint line, centered on edge
            delta_pos = (size - self.size_line_joint)/2
            self.start_line_joint_x = self.StartX + delta_pos*math.cos(angle)
            self.start_line_joint_y = self.StartY + delta_pos*math.sin(angle)
            self.end_line_joint_x = self.EndX - delta_pos*math.cos(angle)
            self.end_line_joint_y = self.EndY - delta_pos*math.sin(angle)
            if DrawHalf < 0:
                #Draw only first half of notch line,i.e. end will be at the middle of segment
                self.EndX = (self.StartX + self.EndX) / 2
                self.EndY = (self.StartY + self.EndY) / 2
                self.nb_finger_joint = (self.nb_finger_joint // 2 ) + 1     #Previous number was odd (2n+1), new notch count = n + 1, as last one will be half notch
                self.end_line_joint_x =   self.start_line_joint_x +  ((self.nb_finger_joint-0.5)*finger_joint_size)*math.cos(angle)         #Quit line at end of last notch
                self.end_line_joint_y =   self.start_line_joint_y +  ((self.nb_finger_joint-0.5)*finger_joint_size)*math.sin(angle)         #Quit line at end of last notch
                if (self.nb_finger_joint%2) == 0 and self.StartStatus:
                    #Now nb joint is even, so Internal/External status is changed so end is external
                    self.end_line_joint_x += thickness * math.cos(angle-math.pi/2)
                    self.end_line_joint_y += thickness * math.sin(angle-math.pi/2)
                    self.EndX = self.end_line_joint_x
                    self.EndY = self.end_line_joint_y
                elif (self.nb_finger_joint%2) == 0 and self.StartStatus == 0:
                    #Now nb joint is even, so Internal/External status is changed so end is internal
                    self.end_line_joint_x += thickness * math.cos(angle+math.pi/2)
                    self.end_line_joint_y += thickness * math.sin(angle+math.pi/2)
                    self.EndX = self.end_line_joint_x
                    self.EndY = self.end_line_joint_y
            elif DrawHalf > 0:
                #Draw only second half of notch line,i.e. Start will be at the middle of segment
                self.StartX = (self.StartX + self.EndX) / 2
                self.StartY = (self.StartY + self.EndY) / 2
                self.nb_finger_joint = (self.nb_finger_joint // 2 ) + 1    #Previous number was odd (2n+1), new notch count = n+1 , as first one with half notch for the first one
                #Draw the first half notch as a shift from start position
                self.start_line_joint_x = self.StartX - 0.5*finger_joint_size*math.cos(angle)
                self.start_line_joint_y = self.StartY - 0.5*finger_joint_size*math.sin(angle) 
                if (self.nb_finger_joint%2) == 0 and self.EndStatus:
                    #Now number of joints is even, so switch StartStatus to have different status (Start and End), and keep End Status
                    #In this case, Start is now External
                    self.StartStatus = 0
                    #Move Start point
                    self.start_line_joint_x += thickness * math.cos(angle-math.pi/2)
                    self.start_line_joint_y += thickness * math.sin(angle-math.pi/2)
                else:
                    #Now number of joints is even, so switch StartStatus to have different status (Start and End), and keep End Status
                    #In this case, Start is now Internal
                    self.StartStatus = 1
                    #Move Start point
                    self.start_line_joint_x += thickness * math.cos(angle+math.pi/2)
                    self.start_line_joint_y += thickness * math.sin(angle+math.pi/2)
        else:      #Start and end have different internal/external status. Number of notches should be even
            if size < 2 * finger_joint_size:
                self.nb_finger_joint = 0
            else:
                self.nb_finger_joint = 2*int(size / (2*finger_joint_size))
            self.size_line_joint = self.nb_finger_joint * finger_joint_size
            # compute start and stop of finger joint line, centered on edge
            delta_pos = (size - self.size_line_joint)/2
            self.start_line_joint_x = self.StartX + delta_pos*math.cos(angle)
            self.start_line_joint_y = self.StartY + delta_pos*math.sin(angle)
            self.end_line_joint_x = self.EndX - delta_pos*math.cos(angle)
            self.end_line_joint_y = self.EndY - delta_pos*math.sin(angle)
        DebugMsg("NotchLine_init, size of line joints = "+str(size)+" Nb_Joint="+str(self.nb_finger_joint)+" size_line_joint="+str(self.size_line_joint)+" start_line_joint"+str(( self.start_line_joint_x, self.start_line_joint_y))+" end_line_joint="+str((self.end_line_joint_x, self.end_line_joint_y))+'\n')

    def ModifyNotchLine(self, SizeCut, CutOnStart):
        '''
        This function modify a vertical notch line to take into account cuts needed by WoodHinge
        Beware, only safe to call with vertical lines, unexpected results in other cases
        SizeCut is the Size of cut, last notch will be at last 1.5 thickness far from this cut
        CutOnStart is True when the cut is at the start of the line. Beware could be top or bottom if angle is 90 or 270°
        '''
        DebugMsg("Enter ModifyNotchLine, CutOnStart="+str(CutOnStart)+" angle="+str(self.Angle)+" Start ="+str(self.StartY)+" End="+str(self.EndY)+" nb_finger_joint="+str(self.nb_finger_joint)+" SizeJoint="+str(self.JointSize)+" start_line_joint_y="+str(self.start_line_joint_y)+" end_line_joint_y="+str(self.end_line_joint_y)+'\n')
        Dir = 0     #Bottom to Top if Dir = 0
        SizeCut -= thickness    #In all cases, reduce sizecut because top and bottom lines are always external in Y

        if abs(self.Angle - math.pi/2) < 1e-6:
            Dir = 1     # Top to Bottom
        if Dir == 1 and CutOnStart:
            #Change line, shorten of SizeCut at the start, in this case start at end of line
            nbNotch = 1
            ypos =  self.end_line_joint_y 
            Limit = SizeCut + self.StartY + 1.5*thickness
            DebugMsg("WHC_init_1 : ypos ="+str(ypos)+" nbNotch ="+str(nbNotch)+"  Limit="+str(Limit)+" NewSizeCut="+str(SizeCut)+"\n")
            while ypos > Limit:
                ypos -= 2*self.JointSize
                nbNotch += 2
                DebugMsg("WHC : ypos ="+str(ypos)+" nbNotch ="+str(nbNotch)+"\n")
            #Now change the line
            if nbNotch > 3:
                nbNotch -= 2    #Sub last step which was too far
                self.start_line_joint_y = self.end_line_joint_y - nbNotch*self.JointSize
                self.nb_finger_joint = nbNotch
                self.StartY += SizeCut
            else:
                self.nb_finger_joint = 0        #No more notch
                self.StartY += SizeCut
        elif Dir==1 and CutOnStart == False:
            #Change line, shorten of SizeCut at the end, in this case start at start of line
            nbNotch = 1
            ypos =  self.start_line_joint_y 
            Limit = self.EndY - SizeCut - 1.5*thickness
            DebugMsg("WHC_init_2 : ypos ="+str(ypos)+" nbNotch ="+str(nbNotch)+"  Limit="+str(Limit)+" NewSizeCut="+str(SizeCut)+"\n")
            while ypos < Limit:
                ypos += 2*self.JointSize
                nbNotch += 2
                DebugMsg("WHC : ypos ="+str(ypos)+" nbNotch ="+str(nbNotch)+"\n")
            #Now change the line
            if nbNotch > 3:
                nbNotch -= 2    #Sub last step which was too far
                #Cut on end, so change EndY and end_line_joint_y
                self.end_line_joint_y = self.start_line_joint_y + nbNotch*self.JointSize
                self.nb_finger_joint = nbNotch
                self.EndY -= SizeCut
            else:
                self.nb_finger_joint = 0        #No more notch
                self.EndY -= SizeCut
            if self.EndY < self.end_line_joint_y:           #Limit send_line_joint_y to be lower or equal than EndY
                self.end_line_joint_y = self.EndY
        elif Dir==0 and CutOnStart:
            #Change line, shorten of SizeCut at the start, in this case this the bottom of line because Angle is 270°
            nbNotch = 1
            ypos =  self.end_line_joint_y 
            Limit = self.StartY - SizeCut - 1.5*thickness
            DebugMsg("WHC_init_3 : ypos ="+str(ypos)+" nbNotch ="+str(nbNotch)+"  Limit="+str(Limit)+" NewSizeCut="+str(SizeCut)+"\n")
            while ypos < Limit:
                ypos += 2*self.JointSize
                nbNotch += 2
                DebugMsg("WHC : ypos ="+str(ypos)+" nbNotch ="+str(nbNotch)+"\n")
            #Now change the line
            if nbNotch > 3:
                nbNotch -= 2    #Sub last step which was too far
                #Change at start of line
                self.start_line_joint_y = self.end_line_joint_y + nbNotch*self.JointSize
                self.nb_finger_joint = nbNotch
                self.StartY -= SizeCut
            else:
                self.nb_finger_joint = 0        #No more notch
                self.StartY -= SizeCut
        elif Dir==0 and CutOnStart == 0:
            #Change line, shorten of SizeCut at the end, in this case this the top of line because Angle is 270°
            nbNotch = 1
            ypos =  self.start_line_joint_y 
            Limit = self.EndY + SizeCut + 1.5*thickness
            DebugMsg("WHC_init_4 : ypos ="+str(ypos)+" nbNotch ="+str(nbNotch)+"  Limit="+str(Limit)+" NewSizeCut="+str(SizeCut)+"\n")
            while ypos > Limit:
                ypos -= 2*self.JointSize
                nbNotch += 2
                DebugMsg("WHC : ypos ="+str(ypos)+" nbNotch ="+str(nbNotch)+"\n")
            #Now change the line
            if nbNotch > 3:
                nbNotch -= 2    #Sub last step which was too far
                self.end_line_joint_y = self.start_line_joint_y - nbNotch*self.JointSize
                self.nb_finger_joint = nbNotch
                self.EndY += SizeCut            #New EndY is below the previous one
            else:
                self.nb_finger_joint = 0        #No more notch
                self.EndY += SizeCut
            if self.EndY < self.end_line_joint_y:
                self.end_line_joint_y = self.EndY
        DebugMsg("Exit ModifyNotchLine, angle="+str(self.Angle)+" Start ="+str(self.StartY)+" End="+str(self.EndY)+" nb_finger_joint="+str(self.nb_finger_joint)+" SizeJoint="+str(self.JointSize)+" start_line_joint_y="+str(self.start_line_joint_y)+" end_line_joint_y="+str(self.end_line_joint_y)+'\n')
        
    def drawNotchLine(self, path):
        '''
        Draw the actual line, starting at current position of path.
        The position should be StartX, StartY, this is not checked or enforced to avoid unwanted moves
        Each finger joint is JointSize long but there is a correction to take into account the burn factor (thickness of the cutting line).
        So each external joint is actually JointSize+2*burn long and Internal joints are JointSize-2*burn
        '''
        if self.nb_finger_joint == 0:
            #Easy case, no finger joint, draw a straight line
            path.LineTo(self.EndX, self.EndY)
            return
        #Normal case, there are finger joint(s)
        #First compute angles. 
        #AngleJoint is the angle for drawing the first part of the finger joint
        #If start point is internal, AngleJoint should be Angle - pi/2, else it should be Angle + pi/2
        if self.StartStatus:        #internal
            AngleJoint = self.Angle - math.pi/2
            DeltaBurn = burn
        else:
            AngleJoint = self.Angle + math.pi/2
            DeltaBurn = -burn
        DebugMsg("drawNotchLine, Angle ="+str(round(self.Angle*180/math.pi))+" AngleJoint="+str(round(AngleJoint*180/math.pi))+'\n')
        DebugMsg("start_line_joint="+str((self.start_line_joint_x, self.start_line_joint_y))+"  JointSize="+str(self.JointSize)+" DeltaBurn="+str(DeltaBurn)+'\n')
        #First go up to start of notch line + first joint + burn correction
        xcur = self.start_line_joint_x + (self.JointSize+DeltaBurn)*math.cos(self.Angle)
        ycur = self.start_line_joint_y + (self.JointSize+DeltaBurn)*math.sin(self.Angle)
        path.LineTo(xcur, ycur)
        DebugMsg("First Point="+str((xcur, ycur))+'\n')
        i = self.nb_finger_joint - 1
        while i > 0:
            #The start drawing finger joint
            path.LineToRel(thickness*math.cos(AngleJoint), thickness*math.sin(AngleJoint))
            #Compute next AngleJoint for return move if necessary
            AngleJoint = AngleJoint + math.pi
            if AngleJoint > 2*math.pi:
                AngleJoint -= 2*math.pi         #Keep angle between 0 and 2*pi
            #idem for burn factor
            DeltaBurn = -DeltaBurn
            #Then line which is JointSize long and take into account the burn factor, draw half finger joint when last of first half
            if self.DrawHalf < 0 and i == 1:
                path.LineToRel((self.JointSize/2+DeltaBurn)*math.cos(self.Angle), (self.JointSize/2+DeltaBurn)*math.sin(self.Angle))
            elif i > 1:     #Do not draw last segment, not necessary, will be completed by next path.LIneTo
                path.LineToRel((self.JointSize+DeltaBurn)*math.cos(self.Angle), (self.JointSize+DeltaBurn)*math.sin(self.Angle))
            i -= 1
        #Then draw last part, up to end point
        #Do not check if necessary because of burn factor, last position is not the real end of notch line.
        path.LineTo(self.EndX, self.EndY)
        DebugMsg("Last LineTo End ="+str((self.EndX, self.EndY))+'\n')

class FlexLines:
    '''
    This class deals and draw set of flex lines to round a corner
    '''
    def drawFlexLines(self, Position, Height, Radius, path):
        '''
        First compute how many segment per line. Segment length should be kept short, < 50mm or so, so high boxes means number of lines
        Also compute distance between lines, which depend on radius. Shorter radius means smaller distance between lines
        But keep min distance at about 1mm minimum and 1.5mm max, after this value flex is quite hard to bend !
        '''
        if Height+2*thickness < 30:
            nSegmentFlex = 1
        elif Height+2*thickness < 80:
            nSegmentFlex = 2
        elif Height+2*thickness < 150:
            nSegmentFlex = 3
        else:
            nSegmentFlex = Height+2*thickness // 50
        #Then compute distance between flex lines. The basic idea is to have a minimum of 15 lines per corner, with lines distant at least of 1mm
        #But also ensure that distance between lines is at most at 2mm 
        round_distance = Radius*math.pi/2
        flex_line_spacing = round_distance / 14
        flex_line_spacing = max(flex_line_spacing, 1.0)
        flex_line_spacing = min(flex_line_spacing, 1.5)
        nb_flex_lines =  int(round(round_distance / flex_line_spacing,0))
        DebugMsg("sizeround ="+str(round_distance)+" flex_line_spacing="+str(flex_line_spacing)+" nb_flex_lines="+str(nb_flex_lines)+" size="+str(nb_flex_lines*flex_line_spacing)+"\n")        
        #nb_flex_lines should be odd
        nb_flex_lines |= 1 
        flex_line_spacing = round_distance / (nb_flex_lines-1)  #Real distance between lines 
        length_flex_segment_case1 = (Height+2*thickness - 2*nSegmentFlex) / nSegmentFlex      #Case 1, 1/2 segment starting at top, n-1 segments and 1/2 segment up to bottom
        length_flex_segment_case2 = (Height+2*thickness - 2*(nSegmentFlex+1)) / nSegmentFlex  #Case 2, n segments equally spaced (2mm) from top to bottom
        DebugMsg("nSegmentFlex="+str(nSegmentFlex)+" sizeround ="+str(round_distance)+" flex_line_spacing="+str(flex_line_spacing)+" nb_flex_lines="+str(nb_flex_lines)+" size="+str(nb_flex_lines*flex_line_spacing)+"\n")
        #Now draw set of flex lines
        for i in range(nb_flex_lines):
            if i % 2:
                #In this case draw nSegmentFlex segments which are identical. First segment start at 2 mm above bottom line, segments are 2mm spaced
                for j in range(nSegmentFlex):
                    path.MoveTo(Position + i * flex_line_spacing, Height+thickness-2-j * (length_flex_segment_case2+2) )
                    path.LineToVRel(-length_flex_segment_case2)
            else:
                #In this case draw a first segment starting at -thickness (top), segment is length_flex_segment_even/2 long
                path.MoveTo(Position + i * flex_line_spacing, -thickness )
                path.LineToVRel(length_flex_segment_case1/2)        #One half segment
                #Then nSegmentFlex-1 which are 
                for j in range(nSegmentFlex-1):
                    path.MoveTo(Position + i * flex_line_spacing, j*(length_flex_segment_case1+2) + length_flex_segment_case1/2 + 2 - thickness )
                    path.LineToVRel(length_flex_segment_case1)
                path.MoveTo(Position + i * flex_line_spacing, Height+thickness - length_flex_segment_case1/2)
                path.LineTo(Position + i * flex_line_spacing, Height+thickness )

        
class FlexFace:
    '''
    This class deal with flex faces, which are used as vertical faces when rounded corners are used.
    '''
    def __init__(self, FlexBandList, isLid, zbox, z_joint, InkscapeGroup, PositionInPage):
        '''
        The list FlexBandList contains all elements to be used on top and bottom line of the flex face. 
        Each element is in a tuple 
            item 0 is the path id
            item 1 is Start_Internal
            item 2 is End Internal
            item 3..n are tuple with ( size, size_joints top, radius rounded corner, size_joints bottom, [hasCircle])
            Last item is always with radius = 0
        '''
        self.FlexBandList = FlexBandList
        self.z_joint = z_joint
        self.height = zbox
        self.isLid = isLid
        #Update PositionInPage to take into account finger joints (only OK if it is a simple shape with finger joints).
        PositionInPage[0] -= thickness
        PositionInPage[1] -= thickness
        
        FlexElt = FlexBandList[3]
        if len(FlexElt) == 5 and FlexElt[4] and self.isLid == False:
            #Change path offset to take into account the circle...
            PositionInPage[0] -= WoodHingeSize*thickness
            PositionInPage[1] -= WoodHingeSize*thickness
        elif len(FlexBandList) > 4:
            FlexElt = FlexBandList[len(FlexBandList)-1]
            if len(FlexElt) == 5 and FlexElt[4] and self.isLid == False:
                #Change path offset to take into account the circle... but only on y here
                PositionInPage[1] -= WoodHingeSize*thickness
          
            
        self.BoundingBox = (-PositionInPage[0], -PositionInPage[1], -PositionInPage[0], -PositionInPage[1])
        
        #If needed, create path which will be used to draw the face
        #The path will be in the group InkscapeGroup
        name = FlexBandList[0]
        if isLid:
            name = 'Lid_'+name
        self.path = th_inkscape_path(PositionInPage, InkscapeGroup, name)
        #Remember these 2 parameters for Side Notch lines
        self.InkscapeGroup = InkscapeGroup
        self.BaseName = FlexBandList[0]
        
    def Close(self):
        '''
        Close and write the path after drawing is done
        '''
        self.path.Close()
        self.path.GenPath()

    def drawClip(self, size_clip, UpDown):
        ''' Draw a single clip pattern
            The clip is vertical, with length size_clip and width size_clip/4
            Add clip to current path, use LineTo
            New path position will be end of clip
            If draw up, UpDown should be 1
        '''
        if UpDown != 1:
            UpDown=-1       #Will draw negative
        #First draw vertical line which is .31*size
        self.path.LineToVRel(size_clip*0.3075*UpDown)
        #Then small bezier curve
        self.path.BezierRel(0, size_clip*0.036241333*UpDown, size_clip*0.045356111, size_clip*0.052734333*UpDown, size_clip*0.0685556, size_clip*0.025*UpDown)
        #then line 
        self.path.LineToRel(size_clip*0.132166667, size_clip*-0.157555556*UpDown)
        #then bezier
        self.path.BezierRel(size_clip*0.016710556, size_clip*-0.02*UpDown, size_clip*0.05, size_clip*-0.008*UpDown, size_clip*0.05, size_clip*0.017795167*UpDown)
        #Then vertical line
        self.path.LineToVRel(size_clip*0.615*UpDown)
        #then bezier
        self.path.BezierRel(0, size_clip*0.026*UpDown, size_clip*-0.032335, size_clip*0.037760389*UpDown, size_clip*-0.05, size_clip*0.017795167*UpDown)
        #Then line
        self.path.LineToRel(size_clip*-0.132166667, size_clip*-0.157555556*UpDown)
        #then last bezier
        #c -0.42188,0.5 -1.23438,0.203125 -1.23438,-0.449219
        self.path.BezierRel(size_clip*-0.023437778, size_clip*-0.027777778*UpDown, size_clip*-0.068576667, size_clip*-0.011284722*UpDown, size_clip*-0.068576667, size_clip*0.025*UpDown)
        #then last line
        self.path.LineToVRel(size_clip*0.3075*UpDown)

    def drawFlexFace(self, ClosePath):
        '''
        Draw the flex face into its path, close path if argument is true after drawing
        This method is only valid when the corners are straight.
        When all corners are rounded, drawRoundedFlexFace should be used.
        '''
        ListFlexLines = []
        #Build Top line
        xpos = 0
        if self.isLid: 
            TopJointOff = 3
            BotJointOff = 1
        else:
            TopJointOff = 1
            BotJointOff = 3
        leftCircle = False
        leftCircleCut = False
        leftCirclePos = 0
        rightCircle = False
        rightCircleCut = False
        rightCirclePos = 0
        LastRadius = 0      #Always start with straight corner
        DebugMsg("\nEnter drawFlexFace, isLid="+str(self.isLid)+" Number of elements in list="+str(len(self.FlexBandList))+"Height="+str(self.height)+"\n")
        #Now read all elements (3..N)
        for i in range(3, len(self.FlexBandList)):
            FlexElement = self.FlexBandList[i]
            DebugMsg("Top line, i="+str(i)+" FlexElement="+str(FlexElement)+'\n')
            if i == 3 and len(FlexElement) == 5 and FlexElement[4] and self.isLid == False:
                #Specific case of left wood hinge face, draw circle on top
                leftCircle = True
                leftCirclePos = -thickness        #Remember circle position
                #In this case start position is 0, (WoodHingeSize-1)*thickness
                self.path.MoveTo(0, (WoodHingeSize-1)*thickness)
                self.path.LineToHRel(-thickness)
                self.path.drawQuarterCircle(-thickness, -thickness, WoodHingeSize*thickness, 3)        #Start Lower Left
                self.path.drawQuarterCircle(-thickness, -thickness, WoodHingeSize*thickness, 0)        #Start Upper Left
                self.path.drawQuarterCircle(-thickness, -thickness, WoodHingeSize*thickness, 1)        #Start Upper Right
                #After this position should be WoodHingeSize*thickness-thickness, -thickness
                self.path.LineTo(FlexElement[0] - FlexElement[2], -thickness)
                xpos += FlexElement[0] - LastRadius - FlexElement[2]
            elif i == 3 and len(FlexElement) == 5 and FlexElement[4] and self.isLid == True:
                leftCircleCut = True
                if i == 3:
                    #Draw path start 
                    if self.FlexBandList[1]:                       #First item : Start point if internal
                        self.path.MoveTo(0, -thickness)            # Start position (0, -thickness) because flex band is always external in Y direction
                    else:
                        self.path.MoveTo(-thickness, -thickness)  # Start position (-thickness, -thickness) because x external and flex band is always external in Y direction
                        self.path.LineTo(0, -thickness)
                DebugMsg("Element "+str(i)+": xpos="+str(xpos)+' Size ='+str(FlexElement[0])+' radius ='+str(FlexElement[2])+' --> '+str(FlexElement[2]*math.pi/2)+'\n')
                #First Notch Line, with length SizeEdge - SizeOfRoundedCorners
                hLine = NotchLine((xpos, -thickness, 0), (xpos+FlexElement[0]-(LastRadius+FlexElement[2]), -thickness, 0), 0.0, FlexElement[TopJointOff])
                hLine.drawNotchLine(self.path)
                xpos += FlexElement[0] - LastRadius - FlexElement[2]
            elif i == len(self.FlexBandList) - 1 and  len(FlexElement) == 5 and FlexElement[4] and self.isLid == False:
                #Specific case of right wood hinge face, draw circle on top
                rightCircle = True
                rightCirclePos = xpos        #Remember circle position
                #In this case start position is 0, (WoodHingeSize-1)*thickness
                self.path.LineTo( xpos + FlexElement[0] - LastRadius - (WoodHingeSize-1)*thickness, -thickness)
                xpos += FlexElement[0] - LastRadius
                rightCirclePos = xpos + thickness       #Remember circle position
                self.path.drawQuarterCircle(rightCirclePos, -thickness, WoodHingeSize*thickness, 0)        #Start Upper Left
                self.path.drawQuarterCircle(rightCirclePos, -thickness, WoodHingeSize*thickness, 1)        #Start Upper Right
                self.path.drawQuarterCircle(rightCirclePos, -thickness, WoodHingeSize*thickness, 2)        #Start Lower Right
                self.path.LineToHRel(-thickness)
            elif i == len(self.FlexBandList) - 1 and  len(FlexElement) == 5 and FlexElement[4] and self.isLid == True:
                rightCircleCut = True
                DebugMsg("Element "+str(i)+": xpos="+str(xpos)+' Size ='+str(FlexElement[0])+' radius ='+str(FlexElement[2])+' --> '+str(FlexElement[2]*math.pi/2)+'\n')
                #First Notch Line, with length SizeEdge - SizeOfRoundedCorners
                hLine = NotchLine((xpos, -thickness, 0), (xpos+FlexElement[0]-(LastRadius+FlexElement[2]), -thickness, 0), 0.0, FlexElement[TopJointOff])
                hLine.drawNotchLine(self.path)
                xpos += FlexElement[0] - LastRadius - FlexElement[2]
            else:
                if i == 3:
                    #Draw path start 
                    if self.FlexBandList[1]:                       #First item : Start point if internal
                        self.path.MoveTo(0, -thickness)            # Start position (0, -thickness) because flex band is always external in Y direction
                    else:
                        self.path.MoveTo(-thickness, -thickness)  # Start position (-thickness, -thickness) because x external and flex band is always external in Y direction
                        self.path.LineTo(0, -thickness)
                DebugMsg("Element "+str(i)+": xpos="+str(xpos)+' Size ='+str(FlexElement[0])+' radius ='+str(FlexElement[2])+' --> '+str(FlexElement[2]*math.pi/2)+'\n')
                #First Notch Line, with length SizeEdge - SizeOfRoundedCorners
                hLine = NotchLine((xpos, -thickness, 0), (xpos+FlexElement[0]-(LastRadius+FlexElement[2]), -thickness, 0), 0.0, FlexElement[TopJointOff])
                hLine.drawNotchLine(self.path)
                xpos += FlexElement[0] - LastRadius - FlexElement[2]
            #Then the line corresponding to rounded corner, also add coordinates for Flex lines
            if FlexElement[2] > 0:
                self.path.LineTo(xpos + FlexElement[2]*math.pi/2, -thickness)
                ListFlexLines.append((xpos, FlexElement[2]))
            xpos += FlexElement[2]*math.pi/2
            LastRadius = FlexElement[2]         #For the next edge
        if rightCircle == 0:
            if self.FlexBandList[2] == 0:           #External end ?
                self.path.LineTo(xpos + thickness, -thickness)
                xpos += thickness
            self.path.LineTo(xpos, 0)
        DebugMsg('Vertical Line 1, xpos='+str(xpos)+'\n')
        #Then Vertical notch line, 
        vLine = NotchLine((xpos, 0, self.FlexBandList[2]), (xpos, self.height, self.FlexBandList[2]), math.pi/2, self.z_joint)
        if rightCircle:
            #In this case modify the line just created
            #Specific case, shorten Right line of notches to take into account the wood hinge circle. Delete some notches on top
            SizeCut = WoodHingeSize*thickness
            vLine.ModifyNotchLine(SizeCut, True)        #Last parameter, CutOnStart = True
        elif rightCircleCut:
            #In this case modify the line just created
            #Specific case, shorten Right line of notches to take into account the wood hinge circle cut. Delete some notches on bottom
            SizeCut = WoodHingeSize*thickness
            vLine.ModifyNotchLine(SizeCut, False)        #Last parameter, CutOnStart = False
        vLine.drawNotchLine(self.path)      #Draw the line of notches
        if rightCircleCut:
            #Then the cut. Choose 0.95*SizeCut because the actual circle is NOT centered of this vertical edge but shifted by thickness
            self.path.LineTo(xpos, self.height+thickness-SizeCut*0.95)
            #Then the rounded cut, almost a quarter of circle, radius SizeCut
            self.path.Bezier(xpos-SizeCut*0.23, self.height+thickness-SizeCut*0.90, 
                        xpos-SizeCut+thickness, self.height+thickness-SizeCut*0.551916, 
                        xpos-SizeCut+thickness, self.height+thickness)
        else:
            self.path.LineTo(xpos, self.height+thickness)
        DebugMsg("Start bottom line, reverse\n")
        #Then Bottom line (reverse from top line)
        if self.FlexBandList[2] == 0:           #External end ?
            self.path.LineTo(xpos - thickness, self.height+thickness)
            xpos -= thickness
        for i in range(len(self.FlexBandList)-1, 2, -1):        #Start at end up to third element
            #For reverse drawing, should have the radius of the next corner
            if i > 3:
                NextRadius = self.FlexBandList[i-1][2]
            else:
                NextRadius = 0
            FlexElement = self.FlexBandList[i]
            DebugMsg("Element "+str(i)+": xpos="+str(xpos)+' Size ='+str(FlexElement[0])+' radius ='+str(FlexElement[2])+' --> '+str(FlexElement[2]*math.pi/2)+' Next Radius='+str(NextRadius)+'\n')
            #First the line corresponding to rounded corner (reverse from previous)
            DebugMsg("Draw line for rounded corner, size ="+str(FlexElement[2]*math.pi/2)+" New xpos="+str(xpos - FlexElement[2]*math.pi/2)+'\n')
            if FlexElement[2] > 0:
                self.path.LineTo(xpos - FlexElement[2]*math.pi/2, self.height+thickness)
                xpos -= FlexElement[2]*math.pi/2
            #Then Notch Line
            if i == 3 and leftCircleCut:
                #specific case, draw up to start of cut
                self.path.LineTo(SizeCut - thickness , self.height+thickness)
                xpos = 0    #Not true yet, but needed to place the vertical line at the right position
                DebugMsg("leftCircleCut True, pathto "+str((SizeCut - thickness , self.height+thickness))+" xpos ="+str(xpos)+"\n")
            else:
                hLine = NotchLine((xpos, self.height+thickness, 0), (xpos-(FlexElement[0]-FlexElement[2]-NextRadius), self.height+thickness, 0), math.pi, FlexElement[BotJointOff])
                hLine.drawNotchLine(self.path)
                xpos -= FlexElement[0] - NextRadius - FlexElement[2]
        if leftCircleCut == False:
            if self.FlexBandList[1] == 0:           #External Start ?
                self.path.LineTo(xpos - thickness, self.height+thickness)
                xpos -= thickness
            self.path.LineTo(xpos, self.height)
        #Then Vertical notch line for left edge
        vLine = NotchLine((xpos, self.height, self.FlexBandList[1]), (xpos, 0, self.FlexBandList[1]), -math.pi/2, self.z_joint)
        if leftCircle:
            SizeCut = WoodHingeSize*thickness
            vLine.ModifyNotchLine(SizeCut, False)        #Last parameter, CutOnStart = False, because we start at bottom
        elif  leftCircleCut:    
            #In this case, shorten the notch line on bottom because of the circle cut
            SizeCut = WoodHingeSize*thickness
            vLine.ModifyNotchLine(SizeCut, True)        #Last parameter, CutOnStart = True, because we start at bottom
            #Draw the rounded cut, almost a quarter of circle, radius ExtRadius
            self.path.Bezier(SizeCut-thickness, self.height+thickness-SizeCut*0.551916
                             , SizeCut*0.23, self.height+thickness-SizeCut*0.90
                             , 0, self.height+thickness-SizeCut*0.95)


        vLine.drawNotchLine(self.path)
        DebugMsg('Vertical Line 2, xpos='+str(xpos)+'\n')
        
        #Draw up to -thickness because external in Y direction
        if leftCircle:
            self.path.LineTo(xpos, SizeCut - thickness)
        else:
            self.path.LineTo(xpos, -thickness)

        # If circle, draw interior and rectangles
        #Case with WoodHingeCorner, draw circle and rectangle
        if leftCircle:        
            #Draw the circle internal to the hinge, radius is 2*thickness mm
            CircleRadius = WoodHingeInternalCircle*thickness
            self.path.drawCircle(leftCirclePos, -thickness, CircleRadius)
            #Then the internal rectangle, rectangle height is 1.5*thickness
            RectHeight = WoodHingeRect*thickness
            self.path.MoveTo(leftCirclePos, -thickness)     #Starting point Ext/Bottom
            self.path.LineToVRel(-RectHeight)            #Ext/Top
            self.path.LineToHRel(thickness)              #Int/Top
            self.path.LineToVRel(RectHeight)             #Int Bottom 
            self.path.LineToHRel(-thickness)             #Return to start
        if rightCircle:        
            #Draw the circle internal to the hinge, radius is 2*thickness mm
            CircleRadius = WoodHingeInternalCircle*thickness
            self.path.drawCircle(rightCirclePos, -thickness, CircleRadius)
            #Then the internal rectangle, rectangle height is 1.5*thickness
            RectHeight = WoodHingeRect*thickness
            self.path.MoveTo(rightCirclePos, -thickness)     #Starting point Ext/Bottom
            self.path.LineToVRel(-RectHeight)             #Ext/Top
            self.path.LineToHRel(-thickness)             #Int/Top
            self.path.LineToVRel(RectHeight)            #Int Bottom 
            self.path.LineToHRel(thickness)              #Return to start


        #Now draw flex lines
        
        for FlexLinePos in ListFlexLines:
            Flex = FlexLines()
            Flex.drawFlexLines(FlexLinePos[0], self.height, FlexLinePos[1], self.path)
        #Get bounding box of path
        self.BoundingBox = (self.path.xmin, self.path.ymin, self.path.xmax, self.path.ymax)

        if ClosePath:
            self.path.Close()
            self.path.GenPath()

    def drawRoundedFlexFace(self, ClosePath):
        '''
        Draw a Flex band when all corners are rounded. This is a specific case because there are clips at the center of back face
        Back face should be the first in list
        '''
        
        #Compute clips number and position, zone with clips will be between thickness and zbox - thickness 
        zoneclips = self.height - 2*thickness
        #Size of clips is dependant to size of zoneclips
        if zoneclips < 50:
            sizeclips = 10
        else:
            sizeclips = 18
        nbclips = int(zoneclips // sizeclips)
        if nbclips == 0:
            inkex.errormsg('Box is not high enough, no rrom for clips')
            return
        DebugMsg("\ndrawRoundedFlexFace, sizeclips="+str(sizeclips)+" nbclips="+str(nbclips)+'\n')
        ListFlexLines = []
        LastRadius = self.FlexBandList[6][2]       # Radius of left back corner
        xpos = 0
        FlexElement = self.FlexBandList[3]
        DebugMsg("First Half notch line, size ="+str(FlexElement[0])+" Size Round BackLeft="+str(LastRadius)+" Size Round BackRight="+str(FlexElement[2])+'\n')
        #The notch line will be centered on xpos (0), so should start at -(SizeNotchLine-SizeRadius_BackLeft-SizeRadius_BackRight)/2
        First_hLine = NotchLine((-(FlexElement[0]-FlexElement[2] - LastRadius)/2, -thickness, 0), ((FlexElement[0]-FlexElement[2] - LastRadius)/2, -thickness, 0), 0.0, FlexElement[1], 1)      #Draw only second half
        if First_hLine.StartStatus == 0:
            self.path.MoveTo(0, -thickness)   # Start position (0, -thickness) because flex band is external in Y direction, and this side start internal in X
        else:
            self.path.MoveTo(0, 0)   # Start position (0, 0) because flex band is internal in Y direction, and this side start internal in X
        First_hLine.drawNotchLine(self.path)
        xpos = (FlexElement[0]-FlexElement[2]-LastRadius)/2
        DebugMsg("After drawing first half of notch line, xpos ="+str(xpos)+'\n')
        ListFlexLines.append((xpos, FlexElement[2]))            #Add this position to draw flex lines.
        #Then the line corresponding to rounded corner
        if FlexElement[2] > 0:
            self.path.LineTo(xpos + FlexElement[2]*math.pi/2, -thickness)
        xpos += FlexElement[2]*math.pi/2
        DebugMsg("Line corresponding to back right corner, l="+str(FlexElement[2]*math.pi/2)+" xpos="+str(xpos)+'\n')
        LastRadius = FlexElement[2]
        #Now read all elements (4..N-1) --> 4..6 here
        for i in range(4, 7):
            FlexElement = self.FlexBandList[i]
            DebugMsg("Element "+str(i)+": xpos="+str(xpos)+' Size ='+str(FlexElement[0])+' radius ='+str(FlexElement[2])+" LastRadius="+str(LastRadius)+"--> "+str(FlexElement[0] - LastRadius - FlexElement[2]) +'\n')
            #First Notch Line
            hLine = NotchLine((xpos, -thickness, 0), (xpos+FlexElement[0] - LastRadius - FlexElement[2] , -thickness, 0), 0.0, FlexElement[1], 0)
            hLine.drawNotchLine(self.path)
            xpos += FlexElement[0] - LastRadius - FlexElement[2]
            #Then the line corresponding to rounded corner
            if FlexElement[2] > 0:
                self.path.LineTo(xpos + FlexElement[2]*math.pi/2, -thickness)
                ListFlexLines.append((xpos, FlexElement[2]))
            xpos += FlexElement[2]*math.pi/2
            LastRadius = FlexElement[2]
            DebugMsg("After drawing line for rounded corner, xpos="+str(xpos)+'\n')

        #Last element 
        FlexElement = self.FlexBandList[7]
        DebugMsg("Last Element (7): xpos="+str(xpos)+' Size ='+str(FlexElement[0])+' radius ='+str(FlexElement[2])+" LastRadius="+str(LastRadius)+"--> "+str(FlexElement[0] - LastRadius - FlexElement[2]) +'\n')
        #Last Notch Line, at last half of it ! First half indeed.
        hLine = NotchLine((xpos, -thickness, 0), (xpos+FlexElement[0] - LastRadius - FlexElement[2], -thickness, 0), 0.0, FlexElement[1], -1)
        hLine.drawNotchLine(self.path)
        xpos += (FlexElement[0] - LastRadius - FlexElement[2])/2
        
        self.path.LineTo(xpos, thickness)
        DebugMsg('Clip Line 1, xpos='+str(xpos)+'\n')
        #Then Vertical clip line
        self.path.LineToVRel((zoneclips - nbclips*sizeclips)/2)
        for i in range(nbclips):
            self.drawClip(sizeclips, 1)
        
        DebugMsg("Bottom line, reverse, start at xpos="+str((xpos, self.height+thickness))+'\n')
        #Then Bottom line (reverse from top line)
        FlexElement = self.FlexBandList[7]
        #Element 7 is the last one, with radius of Back Right corner
        NextRadius = self.FlexBandList[6][2]        #This is the radius of the left right corner
        DebugMsg("Element 7: xpos="+str(xpos)+' Size ='+str(FlexElement[0])+' radius ='+str(FlexElement[2])+' --> '+str(FlexElement[0]-FlexElement[2]-NextRadius)+'\n')
        #Last Notch Line, half line. Center line on xpos
        hLine = NotchLine((xpos + (FlexElement[0] - NextRadius - FlexElement[2])/2, self.height+thickness, 0), (xpos-(FlexElement[0] - NextRadius - FlexElement[2])/2, self.height+thickness, 0), math.pi, FlexElement[3], 1)
        if hLine.StartStatus == 0:
            self.path.LineTo(xpos, self.height+thickness)
        else:
            self.path.LineTo(xpos, self.height)
        hLine.drawNotchLine(self.path)
        xpos -= (FlexElement[0] - NextRadius - FlexElement[2])/2
        for i in range(6, 3, -1):        #Start at end up to third element
            FlexElement = self.FlexBandList[i]
            NextRadius = self.FlexBandList[i-1][2]
            DebugMsg("Element "+str(i)+": xpos="+str(xpos)+' Size ='+str(FlexElement[0])+" radius ="+str(FlexElement[2])+" NextRadius="+str(NextRadius)+' --> '+str(FlexElement[0] - FlexElement[2] - NextRadius)+'\n')
            #First the line corresponding to rounded corner (reverse from previous)
            if FlexElement[2] > 0:
                self.path.LineTo(xpos - FlexElement[2]*math.pi/2, self.height+thickness)
                xpos -= FlexElement[2]*math.pi/2
            DebugMsg("After line for rounded corner, l="+str(FlexElement[2]*math.pi/2)+" Pos="+str((xpos, self.height+thickness))+'\n')
            #Then Notch Line
            hLine = NotchLine((xpos, self.height+thickness, 0), (xpos-(FlexElement[0] - FlexElement[2] - NextRadius), self.height+thickness, 0), math.pi, FlexElement[3], 0)
            hLine.drawNotchLine(self.path)
            xpos -= FlexElement[0] - FlexElement[2] - NextRadius

        NextRadius = self.FlexBandList[7][2]
        FlexElement = self.FlexBandList[3]
        DebugMsg("First Element (3): xpos="+str(xpos)+' Size ='+str(FlexElement[0])+" radius ="+str(FlexElement[2])+" NextRadius="+str(NextRadius)+' --> '+str(FlexElement[0] - FlexElement[2] - NextRadius)+'\n')
        #Then Last round corner
        self.path.LineTo(xpos - FlexElement[2]*math.pi/2, self.height+thickness)
        xpos -= FlexElement[2]*math.pi/2
        DebugMsg("Last Round corner, l="+str(FlexElement[2]*math.pi/2)+" new pos="+str((xpos, self.height+thickness))+'\n')
        #Then Notch Line, half of it
        hLine = NotchLine((xpos, self.height+thickness, 0), (xpos-(FlexElement[0]-FlexElement[2]-LastRadius), self.height+thickness, 0), math.pi, FlexElement[3], -1)      #Draw only first half
        hLine.drawNotchLine(self.path)
        xpos -= (FlexElement[0]-FlexElement[2] - LastRadius)/2
        self.path.LineTo(xpos, self.height)
        #Then Vertical clip line
        DebugMsg('Vertical Clip 2, pos='+str((xpos, self.height))+'\n')
        #and vertical trip (reverse)
        self.path.LineToVRel(-1.0*((zoneclips - nbclips*sizeclips)/2) - thickness)
        for i in range(nbclips):
            self.drawClip(sizeclips, -1)
        if First_hLine.StartStatus == 0:    #If StartStatus is external, move to (0,-Thickness)
            self.path.LineTo(0, -thickness)
        else:
            self.path.LineTo(0, 0)

        #Now draw flex lines
        
        for FlexLinePos in ListFlexLines:
            Flex = FlexLines()
            Flex.drawFlexLines(FlexLinePos[0], self.height, FlexLinePos[1], self.path)
        #Get bounding box of path
        self.BoundingBox = (self.path.xmin, self.path.ymin, self.path.xmax, self.path.ymax)

        if ClosePath:
            self.path.Close()
            self.path.GenPath()

    def drawSideLineNotches(self):
        '''
        Draw the side line notches used with sliding lid. These lines are on left and right (if flex) lines
        These lines are created whenever the top notches are not null
        '''
        n_side_line = 0
        ypos = -self.BoundingBox[1]
        LastRadius = 0
        for i in range(3, len(self.FlexBandList)):
            FlexElement = self.FlexBandList[i]
            if FlexElement[1] > 0:          #Notches are present, draw SideLine with notches
                n_side_line += 1
                DebugMsg("\nDraw "+self.BaseName+'LidJoint'+str(n_side_line)+" Radius="+str(FlexElement[2])+" LastRadius="+str(LastRadius)+" Size ="+str(FlexElement[0] - FlexElement[2] - LastRadius)+'\n')
                Line = BoxFace(self.BaseName+'LidJoint'+str(n_side_line), 
                               CornerPoint((0,0), 0, 1, 1), 0,                                  #Start point, no notch
                               CornerPoint((FlexElement[0] - FlexElement[2] - LastRadius,0), 0, 1, 1), 0,    #Size is up to rounded corner, no notch for the small side 
                               CornerPoint((FlexElement[0] - FlexElement[2] - LastRadius,thickness), 0, 1, 1), FlexElement[1],    #Same x, height = 2*thickness, joints up to next
                               CornerPoint((0, thickness), 0, 1, 1), 0,
                               self.InkscapeGroup, [-self.BoundingBox[2]-2, ypos])
                ypos -= 2*thickness + 2
                Line.drawSimpleFace(True)
            LastRadius = FlexElement[2]


class BoxFace:
    '''
    This class deals with faces
    Each face is defined with 4 corners and the size of the finger joints between the corners
    finger joint size = 0 means no finger joints (straight line)
    The InkscapeGroup parameter is used to bind the path in this group
    The PositionInPage parameter is used to fix the path within the inkscape document
    '''
    def __init__(self, name, top_left, top_finger_joint, top_right, right_finger_joint, bottom_right, bottom_finger_joint, bottom_left, left_finger_joint, InkscapeGroup, PositionInPage, Path=None):
        #First set up the corners
        self.top_left_corner = top_left
        self.top_right_corner = top_right
        self.bottom_right_corner = bottom_right
        self.bottom_left_corner = bottom_left
        #then the lines between the corners
        self.TopLine = NotchLine((top_left.x_start_joint, top_left.y_start_joint, top_left.y_internal), (top_right.x_end_joint, top_right.y_end_joint, top_right.y_internal), 0, top_finger_joint) 

        self.RightLine = NotchLine((top_right.x_start_joint, top_right.y_start_joint, top_right.x_internal), (bottom_right.x_end_joint, bottom_right.y_end_joint, bottom_right.x_internal), math.pi/2, right_finger_joint) 
        
        self.BottomLine = NotchLine((bottom_right.x_start_joint, bottom_right.y_start_joint, bottom_right.y_internal), (bottom_left.x_end_joint, bottom_left.y_end_joint, bottom_left.y_internal), math.pi, bottom_finger_joint) 
        
        self.LeftLine = NotchLine((bottom_left.x_start_joint, bottom_left.y_start_joint, bottom_left.x_internal), (top_left.x_end_joint, top_left.y_end_joint, top_left.x_internal), -math.pi/2, left_finger_joint) 

        #Update PositionInPage to take into account external corners or notches
        if self.top_left_corner.WoodHingeCorner:
            PositionInPage[0] -= (WoodHingeSize+1)*thickness
            PositionInPage[1] -= WoodHingeSize*thickness
        elif self.top_right_corner.WoodHingeCorner:
            PositionInPage[1] -= WoodHingeSize*thickness
            PositionInPage[0] -= thickness
        elif self.top_left_corner.x_internal == 0 or self.bottom_left_corner.x_internal == 0 or self.LeftLine.nb_finger_joint > 0:
            PositionInPage[0] -= thickness
        if self.top_left_corner.y_internal == 0 or self.top_right_corner.y_internal == 0 or self.TopLine.nb_finger_joint > 0:
            PositionInPage[1] -= thickness
        self.BoundingBox = (-PositionInPage[0], -PositionInPage[1], -PositionInPage[0], -PositionInPage[1])
        self.name = name
        self.InkscapeGroup = InkscapeGroup
        #If needed, create path which will be used to draw the face
        #The path will be in the group InkscapeGroup
        if Path == None:
            self.path = th_inkscape_path(PositionInPage, InkscapeGroup, name)
            DebugMsg("Creating path("+name+") Position ="+str(PositionInPage)+'\n')
        else:
            self.path = Path
        #DebugMsg("Create path "+str(name)+ " PositionInPage="+str(PositionInPage)+'\n')

    def Close(self):
        '''
        Close and write the path after drawing is done
        '''
        self.path.Close()
        self.path.GenPath()
        

    def drawSimpleFace(self, ClosePath):
        '''
        Draw the face, when there are no other elements in the perimeter 
        If ClosePath is true the path is closed
        '''
        if self.top_left_corner.WoodHingeCorner:
            #Specific case, shorten Left line of notches to take into account the wood hinge circle
            #But first copy values from right edge, because WoodHingeCorner has modified the notch line
            self.LeftLine.start_line_joint_y = self.RightLine.end_line_joint_y
            self.LeftLine.JointSize = self.RightLine.JointSize
            self.LeftLine.nb_finger_joint = self.RightLine.nb_finger_joint
            self.LeftLine.EndY = self.RightLine.StartY
            SizeCut = WoodHingeSize * thickness
            #Start from bottom (because reverse on left line) up to sizecut
            self.LeftLine.ModifyNotchLine(SizeCut, False)        #Last parameter, CutOnStart = False, because we start at bottom
        if self.top_right_corner.WoodHingeCorner:
            #Specific case, shorten Right line of notches to take into account the wood hinge circle
            #But first copy values from Left edge, because WoodHingeCorner has modified the notch line
            self.RightLine.start_line_joint_y = self.LeftLine.end_line_joint_y
            self.RightLine.end_line_joint_y = self.LeftLine.start_line_joint_y
            self.RightLine.JointSize = self.LeftLine.JointSize
            self.RightLine.nb_finger_joint = self.LeftLine.nb_finger_joint
            self.RightLine.StartY = self.LeftLine.EndY
            SizeCut = WoodHingeSize * thickness
            self.RightLine.ModifyNotchLine(SizeCut, True)        #Last parameter, CutOnStart = False, because we start at Top
        # Go To starting point
        self.path.MoveTo(self.top_left_corner.x_end_joint, self.top_left_corner.y_end_joint)
        #DebugMsg("StartPoint, PathPos ="+str((self.path.x, self.path.y))+" Bounding Box="+str(self.path.GetBoundingBox())+'\n')
        #first (top left) corner
        self.top_left_corner.drawCorner(self.path)
        #DebugMsg("TopLeft, PathPos ="+str((self.path.x, self.path.y))+" Bounding Box="+str(self.path.GetBoundingBox())+'\n')
        #then top edge
        self.TopLine.drawNotchLine(self.path)
        #DebugMsg("Top Edge, PathPos ="+str((self.path.x, self.path.y))+" Bounding Box="+str(self.path.GetBoundingBox())+'\n')
        #Top right corner
        self.top_right_corner.drawCorner(self.path)
        #DebugMsg("Top Right corner, PathPos ="+str((self.path.x, self.path.y))+" Bounding Box="+str(self.path.GetBoundingBox())+'\n')
        #Right edge
        self.RightLine.drawNotchLine(self.path)
        #DebugMsg("Right Edge, PathPos ="+str((self.path.x, self.path.y))+" Bounding Box="+str(self.path.GetBoundingBox())+'\n')
        #Bottom right corner
        self.bottom_right_corner.drawCorner(self.path)
        #DebugMsg("Bottom Right corner, PathPos ="+str((self.path.x, self.path.y))+" Bounding Box="+str(self.path.GetBoundingBox())+'\n')
        #Bottom edge
        self.BottomLine.drawNotchLine(self.path)
        #DebugMsg("Bottom Edge, PathPos ="+str((self.path.x, self.path.y))+" Bounding Box="+str(self.path.GetBoundingBox())+'\n')
        #Bottom left corner
        self.bottom_left_corner.drawCorner(self.path)
        #DebugMsg("Bottom Left corner, PathPos ="+str((self.path.x, self.path.y))+" Bounding Box="+str(self.path.GetBoundingBox())+'\n')
        #Left edge
        self.LeftLine.drawNotchLine(self.path)
        #DebugMsg("Left Edge, PathPos ="+str((self.path.x, self.path.y))+" Bounding Box="+str(self.path.GetBoundingBox())+'\n')
        #The position is now (top_left_corner.x_end_joint, top_left_corner.y_end_joint), it is the starting point

        #Case with WoodHingeCorner, draw circle and rectangle
        if self.top_left_corner.WoodHingeCorner:        
            #Draw the circle internal to the hinge, radius is 2*thickness mm
            CircleRadius = WoodHingeInternalCircle*thickness
            self.path.drawCircle(-thickness, -thickness, CircleRadius)
            #Then the internal rectangle, rectangle height is 1.5*thickness
            RectHeight = WoodHingeRect*thickness
            self.path.MoveTo(-thickness, -thickness)     #Starting point Ext/Bottom
            self.path.LineToVRel(-RectHeight)            #Ext/Top
            self.path.LineToHRel(thickness)              #Int/Top
            self.path.LineToVRel(RectHeight)             #Int Bottom 
            self.path.LineToHRel(-thickness)             #Return to start
        if self.top_right_corner.WoodHingeCorner:        
            #Draw the circle internal to the hinge, radius is 2*thickness mm
            CircleRadius = WoodHingeInternalCircle*thickness
            self.path.drawCircle(self.top_right_corner.x_corner+thickness, -thickness, CircleRadius)
            #Then the internal rectangle, rectangle height is 1.5*thickness
            RectHeight = WoodHingeRect*thickness
            self.path.MoveTo(self.top_right_corner.x_corner+thickness, -thickness)     #Starting point Ext/Bottom
            self.path.LineToVRel(-RectHeight)             #Ext/Top
            self.path.LineToHRel(-thickness)             #Int/Top
            self.path.LineToVRel(RectHeight)            #Int Bottom 
            self.path.LineToHRel(thickness)              #Return to start

        #Get bounding box of path
        self.BoundingBox = (self.path.xmin, self.path.ymin, self.path.xmax, self.path.ymax)

        #Close the path if asked
        if ClosePath:
            self.path.Close()
            self.path.GenPath()

    def drawSimpleFaceHinge(self, HingeList, ClosePath):
        '''
        Draw the face, and the cut for the hinge
        If ClosePath is true the path is closed
        '''
        # Go To starting point
        self.path.MoveTo(self.top_left_corner.x_end_joint, self.top_left_corner.y_end_joint)
        #DebugMsg("StartPoint, PathPos ="+str((self.path.x, self.path.y))+" Bounding Box="+str(self.path.GetBoundingBox())+'\n')
        #first (top left) corner
        self.top_left_corner.drawCorner(self.path)
        #DebugMsg("TopLeft, PathPos ="+str((self.path.x, self.path.y))+" Bounding Box="+str(self.path.GetBoundingBox())+'\n')
        #then top edge, no notch in this case, but cut for the hinge(s)
        for Hinge in HingeList:
            HingePos = Hinge[2] - 1
            self.path.LineTo(HingePos, 0)
            #Then cut for the Hinge
            self.path.LineToVRel(4.5*thickness+1) 
            self.path.LineToHRel(5*thickness + 2.5*SteelHingeSpacing + 2)
            self.path.LineToVRel(-4.5*thickness-1)
        #Then line up to length
        self.path.LineTo(self.top_right_corner.x_corner, 0)  #Up to end of top line
        #Top right corner
        self.top_right_corner.drawCorner(self.path)
        #DebugMsg("Top Right corner, PathPos ="+str((self.path.x, self.path.y))+" Bounding Box="+str(self.path.GetBoundingBox())+'\n')
        #Right edge
        self.RightLine.drawNotchLine(self.path)
        #DebugMsg("Right Edge, PathPos ="+str((self.path.x, self.path.y))+" Bounding Box="+str(self.path.GetBoundingBox())+'\n')
        #Bottom right corner
        self.bottom_right_corner.drawCorner(self.path)
        #DebugMsg("Bottom Right corner, PathPos ="+str((self.path.x, self.path.y))+" Bounding Box="+str(self.path.GetBoundingBox())+'\n')
        #Bottom edge
        self.BottomLine.drawNotchLine(self.path)
        #DebugMsg("Bottom Edge, PathPos ="+str((self.path.x, self.path.y))+" Bounding Box="+str(self.path.GetBoundingBox())+'\n')
        #Bottom left corner
        self.bottom_left_corner.drawCorner(self.path)
        #DebugMsg("Bottom Left corner, PathPos ="+str((self.path.x, self.path.y))+" Bounding Box="+str(self.path.GetBoundingBox())+'\n')
        #Left edge
        self.LeftLine.drawNotchLine(self.path)
        #DebugMsg("Left Edge, PathPos ="+str((self.path.x, self.path.y))+" Bounding Box="+str(self.path.GetBoundingBox())+'\n')
        #The position is now (top_left_corner.x_end_joint, top_left_corner.y_end_joint), it is the starting point

        #Get bounding box of path
        self.BoundingBox = (self.path.xmin, self.path.ymin, self.path.xmax, self.path.ymax)

        #Close the path if asked
        if ClosePath:
            self.path.Close()
            self.path.GenPath()
        #DebugMsg("Closing path, BoundingBox="+str(self.BoundingBox)+'\n')

    def drawFaceWithHoles(self, n_slot, slot_size, DeltaHolePosition, z_joint_size, ClosePath, HingeList = None):
        '''
        Draw a face with holes (for internal walls)
        The holes positions are given in a list (see CalcNotchPos), and an offset will be added if necessary (shorten face)
        '''
        if HingeList == None:
            #No cut for hinge, call regular function to draw face
            self.drawSimpleFace(False)           #First draw the face itself, without closing path
        else:
            self.drawSimpleFaceHinge(HingeList, False)           #First draw the face itself, without closing path
        #now the holes used to fix the walls
        #This line  will be used to draw the holes
        l_NotchLine = NotchLine((0, 0, 1), (self.bottom_right_corner.y_end_joint, 0, 1), math.pi/2, z_joint_size) 

        StartHole = l_NotchLine.start_line_joint_y + l_NotchLine.JointSize
        Spacing = 2*l_NotchLine.JointSize
        DebugMsg("drawFaceWithHoles, Hole Start ="+str(StartHole)+" Spacing="+str(Spacing)+" n_holes"+str(l_NotchLine.nb_finger_joint//2)
                 +' n_slot='+str(n_slot)+'  slot_size='+str(slot_size)+" Delta_Pos="+str(DeltaHolePosition)+'\n')
        for i in range(1, n_slot):
            #For each wall, draw holes corresponding at each notch on zbox
            for j in range((l_NotchLine.nb_finger_joint)//2):
                drawHole(self.path, i*(slot_size+thickness) - DeltaHolePosition -thickness, StartHole + j*Spacing, thickness, l_NotchLine.JointSize, burn)

        #Close the path if asked
        if ClosePath:
            self.path.Close()
            self.path.GenPath()  
    
    def drawSideLineNotches(self, xpos, ypos):
        '''
        Draw the side line notches used with sliding lid. These lines are on left and right lines
        These lines are created whenever the top notches are not null
        '''
        n_side_line = 0
        Line = BoxFace(self.name+'LidJoint', 
                       CornerPoint((0,0), 0, 1, 1), 0,                                  #Start point, no notch
                       CornerPoint((self.top_right_corner.xc - self.top_left_corner.xc,0), 0, 1, 1), 0,    #Size is up to rounded corner, no notch for the small side 
                       CornerPoint((self.top_right_corner.xc - self.top_left_corner.xc,thickness), 0, 1, 1), self.TopLine.JointSize,    #Same x, height = 2*thickness, joints up to next
                       CornerPoint((0, thickness), 0, 1, 1), 0,
                       self.InkscapeGroup, [xpos, ypos])
        Line.drawSimpleFace(True)
        

    def drawExternalBackSlidingLid(self, ClosePath):
        '''
        Draw the face, specific case for sliding lid back face
        If ClosePath is true the path is closed
        '''
        # Go To starting point
        self.path.MoveTo(self.top_left_corner.x_end_joint, self.top_left_corner.y_end_joint)
        #first (top left) corner
        self.top_left_corner.drawCorner(self.path)
        #DebugMsg("TopLeft, PathPos ="+str((self.path.x, self.path.y))+" Bounding Box="+str(self.path.GetBoundingBox())+'\n')
        #Then draw below thickness
        self.path.LineToVRel(thickness)
        #then top edge, without notches
        self.path.LineToHRel(self.top_right_corner.x_end_joint)
        #Then Up thickness
        self.path.LineToVRel(-thickness)
        #Top right corner
        self.top_right_corner.drawCorner(self.path)
        #DebugMsg("Top Right corner, PathPos ="+str((self.path.x, self.path.y))+" Bounding Box="+str(self.path.GetBoundingBox())+'\n')
        #Right edge
        self.RightLine.drawNotchLine(self.path)
        #DebugMsg("Right Edge, PathPos ="+str((self.path.x, self.path.y))+" Bounding Box="+str(self.path.GetBoundingBox())+'\n')
        #Bottom right corner
        self.bottom_right_corner.drawCorner(self.path)
        #DebugMsg("Bottom Right corner, PathPos ="+str((self.path.x, self.path.y))+" Bounding Box="+str(self.path.GetBoundingBox())+'\n')
        #Bottom edge
        self.BottomLine.drawNotchLine(self.path)
        #DebugMsg("Bottom Edge, PathPos ="+str((self.path.x, self.path.y))+" Bounding Box="+str(self.path.GetBoundingBox())+'\n')
        #Bottom left corner
        self.bottom_left_corner.drawCorner(self.path)
        #DebugMsg("Bottom Left corner, PathPos ="+str((self.path.x, self.path.y))+" Bounding Box="+str(self.path.GetBoundingBox())+'\n')
        #Left edge
        self.LeftLine.drawNotchLine(self.path)
        #DebugMsg("Left Edge, PathPos ="+str((self.path.x, self.path.y))+" Bounding Box="+str(self.path.GetBoundingBox())+'\n')
        #The position is now (top_left_corner.x_end_joint, top_left_corner.y_end_joint), it is the starting point
        
        #Get bounding box of path
        self.BoundingBox = (self.path.xmin, self.path.ymin, self.path.xmax, self.path.ymax)

        #Close the path if asked
        if ClosePath:
            self.path.Close()
            self.path.GenPath()
        #DebugMsg("Closing path, BoundingBox="+str(self.BoundingBox)+'\n')

    def drawExternalBackWoodHingeLid(self, ClosePath):
        '''
        Draw the face, specific case for wood hinge lid back face
        This face will use a specific vertical notch lines, which are shorter by the circle of the hinge 
        If ClosePath is true the path is closed
        '''
        DebugMsg("\n enter drawExternalBackWoodHingeLid\n")
        #Size of wood hinge cut
        SizeCut = WoodHingeSize*thickness + 2*burn
        #Modify right line to accomodate this cut
        self.RightLine.ModifyNotchLine(SizeCut, True)
        #Do the same for left line, but reverse
        self.LeftLine.ModifyNotchLine(SizeCut, False)
        # Go To starting point
        self.path.MoveTo(0, -thickness)
        self.path.LineTo(self.top_right_corner.x_end_joint, -thickness)  #Space for cut
        #Then go to cut
        self.path.LineToVRel(SizeCut)
        self.path.LineToHRel(thickness)
        #Right edge
        self.RightLine.drawNotchLine(self.path)
        #Bottom right corner
        self.bottom_right_corner.drawCorner(self.path)
        #Bottom edge
        self.BottomLine.drawNotchLine(self.path)
        #Bottom left corner
        self.bottom_left_corner.drawCorner(self.path)
        #Left edge
        self.LeftLine.drawNotchLine(self.path)
        #Then cut
        self.path.LineToHRel(thickness)
        self.path.LineTo(0, -thickness)
        #The position is now (top_left_corner.x_end_joint, top_left_corner.y_end_joint), it is the starting point
        
        #Get bounding box of path
        self.BoundingBox = (self.path.xmin, self.path.ymin, self.path.xmax, self.path.ymax)

        #Close the path if asked
        if ClosePath:
            self.path.Close()
            self.path.GenPath()
        #DebugMsg("Closing path, BoundingBox="+str(self.BoundingBox)+'\n')

    def drawLidBackWoodHinge(self, ClosePath):
        '''
        Draw the lid back when Wood hinge is chosen, specific case for wood hinge lid back face
        This face will use a specific vertical notch lines, which are shorter by the circle of the hinge 
        If ClosePath is true the path is closed
        '''
        #Size of wood hinge cut
        SizeCut = WoodHingeSize*thickness + 2*burn
        DebugMsg("\n enter drawLidBackWoodHinge, SizeCut = "+str(SizeCut)+"\n")
        DebugMsg("Joint size ="+str(self.RightLine.JointSize)+" Top_Right="+str((self.top_right_corner.x_corner, self.top_right_corner.y_corner))+" Bottom Right="+str((self.bottom_right_corner.x_corner, self.bottom_right_corner.y_corner))+"\n")
        #Change right line, from top to bottom RightLine
        self.RightLine.ModifyNotchLine(SizeCut, False)       #Last Parameter false because we start on Top and cut is on bottom
        #The left line will be the same but reverse
        self.LeftLine.ModifyNotchLine(SizeCut, True)       #Last Parameter false because we start on Bottom and cut is on bottom
        # Go To starting point
        self.path.MoveTo(self.top_left_corner.x_end_joint, self.top_left_corner.y_end_joint)
        #DebugMsg("StartPoint, PathPos ="+str((self.path.x, self.path.y))+" Bounding Box="+str(self.path.GetBoundingBox())+'\n')
        #first (top left) corner
        self.top_left_corner.drawCorner(self.path)
        #DebugMsg("TopLeft, PathPos ="+str((self.path.x, self.path.y))+" Bounding Box="+str(self.path.GetBoundingBox())+'\n')
        #then top edge
        self.TopLine.drawNotchLine(self.path)
        #DebugMsg("Top Edge, PathPos ="+str((self.path.x, self.path.y))+" Bounding Box="+str(self.path.GetBoundingBox())+'\n')
        #Top right corner
        self.top_right_corner.drawCorner(self.path)
        #Right edge
        self.RightLine.drawNotchLine(self.path)
        #Then the cut with the notch for the circle
        StartNotchCircle = 1.5 * thickness
        self.path.LineToHRel(-thickness)
        self.path.LineTo(self.bottom_right_corner.x_end_joint - thickness, self.bottom_right_corner.y_corner - StartNotchCircle)
        self.path.LineToHRel(thickness)
        self.path.LineToVRel(StartNotchCircle)
        self.path.LineTo(self.bottom_right_corner.x_end_joint - thickness, self.bottom_right_corner.y_corner)
        #Bottom edge
        self.path.LineTo(-thickness, self.bottom_left_corner.y_corner)
        #Then Cut
        self.path.LineToVRel(-StartNotchCircle)
        self.path.LineToHRel(thickness)
        self.path.LineTo(0, self.bottom_left_corner.y_corner - SizeCut)
        self.path.LineToHRel(-thickness)
        #Left edge
        self.LeftLine.drawNotchLine(self.path)
        self.path.LineTo(-thickness, -thickness)
       
        #Get bounding box of path
        self.BoundingBox = (self.path.xmin, self.path.ymin, self.path.xmax, self.path.ymax)

        #Close the path if asked
        if ClosePath:
            self.path.Close()
            self.path.GenPath()
        #DebugMsg("Closing path, BoundingBox="+str(self.BoundingBox)+'\n')

    def drawExternalBackSteelHingeLid(self, HingeList, ClosePath):
        '''
        Draw the face, specific case for lid with 'steel hinge' back face
        This face will have cuts to place the real hinge elements 
        HingeList is a list of Hinge position
        If ClosePath is true the path is closed
        '''
        DebugMsg("\n enter drawExternalBackSteelHingeLid\n")
        # Go To starting point
        self.path.MoveTo(-thickness, -thickness)
        #The top line will have cut for the hinge
        for Hinge in HingeList:
            HingePos = Hinge[2]
            self.path.LineTo(HingePos + thickness, -thickness)       #add thickness in x because hinge pos is internal, and sub thickness in y because always external
            #Then Hinge
            self.path.LineToVRel(2.5*thickness)
            self.path.LineToHRel(thickness)
            self.path.LineToVRel(-thickness + 0.5*SteelHingeSpacing)
            self.path.LineToHRel(thickness + SteelHingeSpacing)
            self.path.LineToVRel(thickness - 0.5*SteelHingeSpacing)
            self.path.LineToHRel(thickness)
            self.path.LineToVRel(-thickness + 0.5*SteelHingeSpacing)
            self.path.LineToHRel(thickness + SteelHingeSpacing)
            self.path.LineToVRel(thickness - 0.5*SteelHingeSpacing)
            self.path.LineToHRel(thickness)
            self.path.LineToVRel(-2.5*thickness)
        #Then line up to length
        self.path.LineTo(self.top_right_corner.x_corner, -thickness)  #Up to end of top line
   
        #Right edge
        self.RightLine.drawNotchLine(self.path)
        #Bottom right corner
        self.bottom_right_corner.drawCorner(self.path)
        #Bottom edge
        self.BottomLine.drawNotchLine(self.path)
        #Bottom left corner
        self.bottom_left_corner.drawCorner(self.path)
        #Left edge
        self.LeftLine.drawNotchLine(self.path)
        #Then return to Start
        self.path.LineTo(-thickness, -thickness)

        #Now draw holes for the hinge(s)
        for Hinge in HingeList:
            self.path.MoveTo(Hinge[2]+thickness, 3.5*thickness)
            self.path.LineToHRel(thickness)
            self.path.LineToVRel(-thickness)
            self.path.LineToHRel(-thickness)
            self.path.LineToVRel(thickness)
            self.path.MoveTo(Hinge[2] + 3*thickness + SteelHingeSpacing, 3.5*thickness)
            self.path.LineToHRel(thickness)
            self.path.LineToVRel(-thickness)
            self.path.LineToHRel(-thickness)
            self.path.LineToVRel(thickness)
            self.path.MoveTo(Hinge[2] + 5*thickness + 2*SteelHingeSpacing, 3.5*thickness)
            self.path.LineToHRel(thickness)
            self.path.LineToVRel(-thickness)
            self.path.LineToHRel(-thickness)
            self.path.LineToVRel(thickness)
        
        #Get bounding box of path
        self.BoundingBox = (self.path.xmin, self.path.ymin, self.path.xmax, self.path.ymax)

        #Close the path if asked
        if ClosePath:
            self.path.Close()
            self.path.GenPath()
        #DebugMsg("Closing path, BoundingBox="+str(self.BoundingBox)+'\n')


    def drawLidBackSteelHinge(self, HingeList, ClosePath):
        '''
        Draw the lid back, specific case for lid with 'steel hinge' back face
        This face will have cuts to place the real hinge elements 
        HingeList is a list of Hinge position
        If ClosePath is true the path is closed
        '''
        DebugMsg("\n enter drawLidBackSteelHinge\n")
        # Go To starting point
        self.path.MoveTo(self.top_left_corner.x_end_joint, self.top_left_corner.y_end_joint)
        #DebugMsg("StartPoint, PathPos ="+str((self.path.x, self.path.y))+" Bounding Box="+str(self.path.GetBoundingBox())+'\n')
        #first (top left) corner
        self.top_left_corner.drawCorner(self.path)
        #DebugMsg("TopLeft, PathPos ="+str((self.path.x, self.path.y))+" Bounding Box="+str(self.path.GetBoundingBox())+'\n')
        #then top edge
        self.TopLine.drawNotchLine(self.path)
        #DebugMsg("Top Edge, PathPos ="+str((self.path.x, self.path.y))+" Bounding Box="+str(self.path.GetBoundingBox())+'\n')
        #Top right corner
        self.top_right_corner.drawCorner(self.path)
        #Right edge
        self.RightLine.drawNotchLine(self.path)
        #Bottom right corner
        self.bottom_right_corner.drawCorner(self.path)
        #Bottom edge, this one has cut for the hinge(s).
        z = self.bottom_right_corner.y_corner
        #Now draw holes for the hinge(s), reverse because draw from right to left
        for Hinge in reversed(HingeList):
            HingePos = Hinge[2] + thickness
            #First H line up to end of 2nd hinge
            self.path.LineTo(HingePos + 5*thickness + 2.5*SteelHingeSpacing, z)
            #Then Hinge
            self.path.LineToVRel(-1.5*thickness - 0.5*SteelHingeSpacing)
            self.path.LineToHRel(-thickness - SteelHingeSpacing)
            self.path.LineToVRel(-thickness + 0.5*SteelHingeSpacing)
            self.path.LineToHRel(-thickness)
            self.path.LineToVRel(thickness - 0.5*SteelHingeSpacing)
            self.path.LineToHRel(-thickness - SteelHingeSpacing)
            self.path.LineToVRel(-thickness + 0.5*SteelHingeSpacing)
            self.path.LineToHRel(-thickness)
            self.path.LineToVRel(thickness - 0.5*SteelHingeSpacing)
            self.path.LineToHRel(-thickness - SteelHingeSpacing)
            self.path.LineToVRel(1.5*thickness + 0.5*SteelHingeSpacing)
        #Then draw up to corner            
        self.path.LineTo(self.bottom_left_corner.x_end_joint, self.bottom_left_corner.y_end_joint)
        #Bottom left corner
        self.bottom_left_corner.drawCorner(self.path)
        #Left edge
        self.LeftLine.drawNotchLine(self.path)
        #Then return to Start
        self.path.LineTo(-thickness, -thickness)

        #Then draw holes for the hinge(s)
        for Hinge in HingeList:
            HingePos = Hinge[2] + 2*thickness
            self.path.MoveTo(HingePos + 0.5*SteelHingeSpacing, z - 3.5*thickness)
            self.path.LineToHRel(thickness)
            self.path.LineToVRel(-thickness)
            self.path.LineToHRel(-thickness)
            self.path.LineToVRel(thickness)
            self.path.MoveTo(HingePos + 2*thickness + 1.5*SteelHingeSpacing, z - 3.5*thickness)
            self.path.LineToHRel(thickness)
            self.path.LineToVRel(-thickness)
            self.path.LineToHRel(-thickness)
            self.path.LineToVRel(thickness)
        #Get bounding box of path
        self.BoundingBox = (self.path.xmin, self.path.ymin, self.path.xmax, self.path.ymax)

        #Close the path if asked
        if ClosePath:
            self.path.Close()
            self.path.GenPath()
        #DebugMsg("Closing path, BoundingBox="+str(self.BoundingBox)+'\n')

    def drawLidSideWoodHinge(self, FlagRight, ClosePath):
        '''
        Generate lid side with integrated hinge. This is a rectangle with a rounded cut for the hinge and notches on 3 edges
        No notch on the bottom edge
        '''
        SizeCut = WoodHingeSize*thickness + 2*burn
        DebugMsg("\n enter drawLidSideWoodHinge, SizeCut="+str(SizeCut)+" FlagRight ="+str(FlagRight)+"\n")
        #Because of the cut on the lid, we have to change either the right of left line of notches
        if FlagRight > 0:
            self.RightLine.ModifyNotchLine(SizeCut, False)
        else:
            self.LeftLine.ModifyNotchLine(SizeCut, True)
        # Go To starting point
        self.path.MoveTo(self.top_left_corner.x_end_joint, self.top_left_corner.y_end_joint)
        #DebugMsg("StartPoint, PathPos ="+str((self.path.x, self.path.y))+" Bounding Box="+str(self.path.GetBoundingBox())+'\n')
        #first (top left) corner
        self.top_left_corner.drawCorner(self.path)
        #DebugMsg("TopLeft, PathPos ="+str((self.path.x, self.path.y))+" Bounding Box="+str(self.path.GetBoundingBox())+'\n')
        #then top edge
        self.TopLine.drawNotchLine(self.path)
        #DebugMsg("Top Edge, PathPos ="+str((self.path.x, self.path.y))+" Bounding Box="+str(self.path.GetBoundingBox())+'\n')
        #Top right corner
        self.top_right_corner.drawCorner(self.path)
        #Right edge, first start with normal notch line
        self.RightLine.drawNotchLine(self.path)
        #If right side, special case. Start with notches, but then switch to a circle
        if FlagRight > 0:
            #Then the cut. Choose 0.95*SizeCut because the actual circle is NOT centered of this vertical edge but shifted by thickness
            self.path.LineTo(self.top_right_corner.x_corner, self.bottom_right_corner.y_corner-SizeCut*0.95)
            #Then the rounded cut, almost a quarter of circle, radius SizeCut
            self.path.Bezier(self.top_right_corner.x_corner-SizeCut*0.23, self.bottom_right_corner.y_corner-SizeCut*0.90, 
                        self.top_right_corner.x_corner-SizeCut+thickness, self.bottom_right_corner.y_corner-SizeCut*0.551916, 
                        self.top_right_corner.x_corner-SizeCut+thickness, self.bottom_right_corner.y_corner)
            #No notches on bottom line, just go to next corner
            self.path.LineTo(0, self.bottom_left_corner.y_corner)
        else:
            self.path.LineTo(self.top_right_corner.x_corner, self.bottom_right_corner.y_corner)     #Up to corner
            self.path.LineTo(SizeCut-thickness, self.bottom_left_corner.y_corner)                   #Bottom line up to circle cut
            #Draw the rounded cut, almost a quarter of circle, radius ExtRadius
            self.path.Bezier(SizeCut-thickness, self.bottom_left_corner.y_corner-SizeCut*0.551916
                             , SizeCut*0.23, self.bottom_left_corner.y_corner-SizeCut*0.90
                             , 0, self.bottom_left_corner.y_corner-SizeCut*0.95)
        #Left edge
        self.LeftLine.drawNotchLine(self.path)
        self.path.LineTo(0, -thickness)         #Up to starting point

        #Get bounding box of path
        self.BoundingBox = (self.path.xmin, self.path.ymin, self.path.xmax, self.path.ymax)

        #Close the path if asked
        if ClosePath:
            self.path.Close()
            self.path.GenPath()
        #DebugMsg("Closing path, BoundingBox="+str(self.BoundingBox)+'\n')



    
class GenericBox(inkex.Effect):
    """
    Creates a new layer with the drawings for a parametrically generated box.
    """
    def __init__(self):
        ''' 
        init for all parameters
        '''
        inkex.Effect.__init__(self)
        self.knownUnits = ['in', 'pt', 'px', 'mm', 'cm', 'm', 'km', 'pc', 'yd', 'ft']

        self.arg_parser.add_argument('--unit', action = 'store',
          type = str, dest = 'unit', default = 'mm',
          help = 'Unit, should be one of ')

        self.arg_parser.add_argument('--thickness', action = 'store',
          type = float, dest = 'thickness', default = '3.0',
          help = 'Material thickness')

        self.arg_parser.add_argument('--lid_type', action = 'store',
          type = str, dest = 'lid_type', default = 'Simple',
          help = 'Box lid style ')

        self.arg_parser.add_argument('--n_slot_x', action = 'store',
          type = int, dest = 'n_slot_x', default = '2',
          help = 'Number of columns of slots')

        self.arg_parser.add_argument('--n_slot_y', action = 'store',
          type = int, dest = 'n_slot_y', default = '2',
          help = 'Number of rows of slots')

        self.arg_parser.add_argument('--z', action = 'store',
          type = float, dest = 'z', default = '40.0',
          help = "box height")

        self.arg_parser.add_argument('--y', action = 'store',
          type = float, dest = 'y', default = '60.0',
          help = "box depth")

        self.arg_parser.add_argument('--x', action = 'store',
          type = float, dest = 'x', default = '40.0',
          help = "box width")

        self.arg_parser.add_argument('--z_lid', action = 'store',
          type = float, dest = 'z_lid', default = '20.0',
          help = 'lid height')

        self.arg_parser.add_argument('--z_dome_lid', action = 'store',
          type = float, dest = 'z_dome_lid', default = '20.0',
          help = 'dome lid height')

        self.arg_parser.add_argument('--SkipFlexLines', action = 'store',
          type = inkex.Boolean, dest = 'SkipFlexLines', default = 'true',
          help = 'Skip flex lines when possible')

        self.arg_parser.add_argument('--burn', action = 'store',
          type = float, dest = 'burn', default = '0.1',
          help = 'laser burn size')

        self.arg_parser.add_argument('--StraigthCorners', action = 'store',
          type = inkex.Boolean, dest = 'StraigthCorners', default = 'true',
          help = 'Straight corners')

        self.arg_parser.add_argument('--back_left_radius', action = 'store',
          type = float, dest = 'back_left_radius', default = '10.0',
          help = 'Radius of top left rounded corner')

        self.arg_parser.add_argument('--back_right_radius', action = 'store',
          type = float, dest = 'back_right_radius', default = '10.0',
          help = 'Radius of top right rounded corner')

        self.arg_parser.add_argument('--front_left_radius', action = 'store',
          type = float, dest = 'front_left_radius', default = '10.0',
          help = 'Radius of bottom left rounded corner')

        self.arg_parser.add_argument('--front_right_radius', action = 'store',
          type = float, dest = 'front_right_radius', default = '10.0',
          help = 'Radius of bottom right rounded corner')

        self.arg_parser.add_argument('--AutoSize', action = 'store',
          type = inkex.Boolean, dest = 'AutoSizeJoints', default = 'true',
          help = 'Size of finger joints computed from box dimlensions')

        self.arg_parser.add_argument('--x_joint', action = 'store',
          type = float, dest = 'x_joint', default = '10.0',
          help = 'Size of finger joints in X direction')

        self.arg_parser.add_argument('--y_joint', action = 'store',
          type = float, dest = 'y_joint', default = '10.0',
          help = 'Size of finger joints in Y direction')

        self.arg_parser.add_argument('--z_joint', action = 'store',
          type = float, dest = 'z_joint', default = '10.0',
          help = 'Size of finger joints in Z direction')

        self.arg_parser.add_argument('--Topic', action = 'store',
          type = str, dest = 'TopicPage', 
          help = 'Size of finger joints in Z direction')


        self.BoundingBox = [0, 0, 0, 0]
        self.HingeList = []
        
    try:
        inkex.Effect.unittouu   # unitouu has moved since Inkscape 0.91
    except AttributeError:
        try:
            def unittouu(self, unit):
                return inkex.unittouu(unit)
        except AttributeError:
            pass

    def UpdateBoundingBox(self, Face):
        if Face.BoundingBox[0] < self.BoundingBox[0]:
            self.BoundingBox[0] = Face.BoundingBox[0]
        if Face.BoundingBox[1] < self.BoundingBox[1]:
            self.BoundingBox[1] = Face.BoundingBox[1]
        if Face.BoundingBox[2] > self.BoundingBox[2] - 2:
            self.BoundingBox[2] = Face.BoundingBox[2] + 2
        if Face.BoundingBox[3] > self.BoundingBox[3] - 2:
            self.BoundingBox[3] = Face.BoundingBox[3] + 2
    
    def CalcNotchPos(self, n_slot, size_slot):
        ''' 
        Compute the position of notches for a vertical or horizontal line
        No offset, i.e. position is relative to internal side
        Return a list of positions, each position is a tuple with 3 elements, giving start, size of notch and group number
        These positions are NOT sensitive to burn factor. The burn factor should be added later if needed
        '''
        NPos = []
        if size_slot < 25:
            #Small size, only one notch 
            i_notch_number = 1
            notch_size = size_slot / 3 # Notch is center aligned
        elif size_slot < 80:
            #Medium size, draw 5mm notches
            notch_number = size_slot / 5
            if (notch_number % 2) == 0:
                notch_number -= 1           #should be odd
            notch_size = size_slot / notch_number
            i_notch_number = int(notch_number // 2)
        else:
            #Large size, draw 10mm notches
            notch_number = size_slot / 10
            if (notch_number % 2) == 0:
                notch_number -= 1           #should be odd
            notch_size = size_slot / notch_number
            i_notch_number = int(notch_number // 2)
        for j in range(n_slot):
            #For each slot
            for i in range(i_notch_number):
                NPos.append((j*(size_slot+thickness)+notch_size+2*i*notch_size, notch_size, j))        #Add a tuple with 3 elements for start, size of notch and group number
        return NPos

    def ComputeJointSize(self, xbox, ybox, zbox, back_left_radius, back_right_radius, front_right_radius, front_left_radius):
        '''
        This function compute finger joint size
        It will try to have identical finger joint, but if not possible we will have different joint sizes
        Basic joint size : if l < 100, size = 5mm, when l > 100 --> size = 0.5*sqrt(l)
        '''
        #First take into account radius
        x = min(xbox - back_left_radius - back_right_radius, xbox - front_right_radius - front_left_radius)
        if x < 18:
            inkex.errormsg('Error: box length too small, should be at least 18mm + round radius')
            exit()
        y = min(ybox - back_left_radius - front_left_radius, ybox - front_right_radius - back_right_radius)
        if y < 18:
            inkex.errormsg('Error: box depth too small, should be at least 18mm + round radius')
            exit()
        if x  <= 100:
            basic_size_x = 5.0
        else:
            basic_size_x = 5.0*math.pow(x/100,0.8)
        if y <= 100:
            basic_size_y = 5.0
        else:
            basic_size_y = 5.0*math.pow(y/100,0.8)
        if zbox <= 100:
            basic_size_z = 5.0
        else:
            basic_size_z = 5.0*math.pow(zbox/100,0.8)
        #DebugMsg("Basic joint sizes (1) :"+str((basic_size_x, basic_size_y, basic_size_z))+' \n')
        #Now try to converge towards a single size
        # First with x and y
        if basic_size_x > basic_size_y and y >= 3.0*basic_size_x + 1:
            #x is greater, but at least 3 joints in y direction (one notch)
            basic_size_y = basic_size_x
        if basic_size_y > basic_size_x and x >= 3.0*basic_size_y + 1:
            #y is greater, but at least 3 joints in x direction (one notch)
            basic_size_x = basic_size_y
        # For z direction, should have at least 3 joint size (one notch)
        if basic_size_x > basic_size_y:
            if zbox > 3*basic_size_x + 1:
                basic_size_z = basic_size_x
            else:
                basic_size_z = (zbox-1) / 3         #If not possible, set max finger size
        else:
            if zbox > 3*basic_size_y + 1:
                basic_size_z = basic_size_y
            else:
                basic_size_z = (zbox-1) / 3         #If not possible, set max finger size
        return(basic_size_x, basic_size_y, basic_size_z)

    def drawSteelHingeElement(self, idx, thickness, xOffset, yOffset, parent):
        StartOffset = (xOffset, yOffset)
        xOffset -= 2*thickness

        path = th_inkscape_path((xOffset, yOffset), parent, 'HingeElt_'+str(idx))
        path.MoveTo(0, 0)
        #Start at upper right
        path.LineToVRel(thickness)
        path.LineToHRel(-thickness)
        path.LineToVRel(thickness)
        path.LineToHRel(thickness)
        path.LineToVRel(thickness)
        #Now draw half circle (radius is 1.5*thickness)
        #Position is now 0,3*thickness
        path.Bezier(1.5*thickness*0.551916, 3*thickness, 1.5*thickness, 3*thickness+1.5*thickness*0.551916, 1.5*thickness, 4.5*thickness) 
        path.Bezier(1.5*thickness, 4.5*thickness+1.5*thickness*0.551916, 1.5*thickness*(1-0.551916), 6*thickness, 0, 6*thickness) 
        #Second part of circle has a radius of 2*thickness
        path.Bezier(-2*thickness*0.551916, 6*thickness, -2*thickness, 6*thickness-2*thickness*0.551916, -2*thickness, 4*thickness) 
        path.LineTo(-2*thickness, thickness)
        path.Bezier(-2*thickness, thickness*(1-0.551916), thickness*-1.551916, 0, -thickness, 0)
        path.LineTo(0,0)
        #and last the circle at center for this axis, radius is RadiusSteelHingeAxis mm 
        path.drawCircle(0, 4.5*thickness, RadiusSteelHingeAxis)
        path.Close()
        path.GenPath()
        if path.xmin < self.BoundingBox[0]:
            self.BoundingBox[0] = path.xmin
        if path.ymin < self.BoundingBox[1]:
            self.BoundingBox[1] = path.ymin
        if path.xmax > self.BoundingBox[2] - 2:
            self.BoundingBox[2] = path.xmax + 2
        if  path.ymax > self.BoundingBox[3] - 2:
            self.BoundingBox[3] =  path.ymax + 2

    def BuildTop(self, xbox, ybox, back_left_radius, back_right_radius, front_right_radius, front_left_radius):
        '''
        Draw the top of the box. It depends on the lid style
        '''
        if self.options.lid_type == 'Without':
            return      # Nothing in this case
        if self.options.lid_type == 'Sliding':
            #Specific case, top is a rectangle which is xbox long and ybox  wide with finger joints on top
            #Not compatible with rounded cornerson back, so radius is set to 0
            #On top, corner are internal on x and external on y
            #Position is set at 0,0 (first element)
            #There is also a line of finger joints which is xbox long and thickness wide, begin with this one
            TopLineBottomRight = CornerPoint((xbox+2*thickness,thickness), 0, 1, 1)
            TopLineBottomLeft = CornerPoint((0,thickness), 0, 1, 1)
            #Modify Bottom right corner to change start of line
            TopLineBottomRight.x_start_joint -= thickness
            #idem for bottom left
            TopLineBottomLeft.x_end_joint += thickness
            TopLine = BoxFace('Lid_Joints', CornerPoint((0,0), 0, 1, 1), 
                          0, CornerPoint((xbox+2*thickness,0), 0, 1, 1), 
                          0, TopLineBottomRight,
                          self.x_joint, TopLineBottomLeft,
                          0, self.group, [0.0,0.0])
            TopLine.drawSimpleFace(True)
            self.UpdateBoundingBox(TopLine)
            Top = BoxFace('Lid_Top', CornerPoint((0,0), back_left_radius, 1, 0), 
                          self.x_joint, CornerPoint((xbox,0), back_right_radius, 1, 0), 
                          0, CornerPoint((xbox,ybox), front_right_radius, 1, 1),
                          0, CornerPoint((0,ybox), front_left_radius, 1, 1),
                          0, self.group, [-thickness,-self.BoundingBox[3]])
            Top.drawSimpleFace(True)
            self.UpdateBoundingBox(Top)
            return
        if self.options.lid_type != 'Coffin':
            #For all cases except coffin, draw a rounded rectangle with internal corners
            Top = BoxFace('Lid_Top', CornerPoint((0,0), back_left_radius, 1, 1), 
                          self.x_joint, CornerPoint((xbox,0), back_right_radius, 1, 1), 
                          self.y_joint, CornerPoint((xbox,ybox), front_right_radius, 1, 1),
                          self.x_joint, CornerPoint((0,ybox), front_left_radius, 1, 1),
                          self.y_joint, self.group, [0.0, 0.0])
            Top.drawSimpleFace(False)
            if self.options.lid_type == 'Simple':
                #Add a hole in the top, which the same rounded rectangle, but with thickness less in each direction 
                TopHole = BoxFace('Lid_Int', CornerPoint((thickness,thickness), back_left_radius-thickness, 1, 1), 
                              0, CornerPoint((xbox-thickness,thickness), back_right_radius-thickness, 1, 1), 
                              0, CornerPoint((xbox-thickness,ybox-thickness), front_right_radius-thickness, 1, 1),
                              0, CornerPoint((thickness,ybox-thickness), front_left_radius-thickness, 1, 1),
                              0, self.group, [0.0, 0.0], Top.path)
                TopHole.drawSimpleFace(False)
            Top.Close()     #Close and generate path (both if simple lid)
            self.UpdateBoundingBox(Top)
            if self.options.lid_type == 'Simple':
                #In this case, draw a simple face without notches, external at all corners in both directions
                Top = BoxFace('Lid', CornerPoint((0,0), back_left_radius, 0, 0), 
                              0, CornerPoint((xbox,0), back_right_radius, 0, 0), 
                              0, CornerPoint((xbox,ybox), front_right_radius, 0, 0),
                              0, CornerPoint((0,ybox), front_left_radius, 0, 0),
                              0, self.group, [-self.BoundingBox[2]-thickness-2, 0.0])
                Top.drawSimpleFace(True)
                self.UpdateBoundingBox(Top)

            return

    def BuildBottom(self, xbox, ybox, back_left_radius, back_right_radius, front_right_radius, front_left_radius):
        '''
        Draw the bottom of the box. It is a rounded rectangle 
        Also draw the holes used to secure the internal walls
        Should exchange left and right from top to draw the external face
        '''
        Bottom = BoxFace('Bottom', CornerPoint((0,0), back_right_radius, 1, 1), 
                      self.x_joint, CornerPoint((xbox,0), back_left_radius, 1, 1), 
                      self.y_joint, CornerPoint((xbox,ybox), front_left_radius, 1, 1),
                      self.x_joint, CornerPoint((0,ybox), front_right_radius, 1, 1),
                      self.y_joint, self.group, [-self.BoundingBox[2], 0.0])            #Draw it right of top, same Y
        Bottom.drawSimpleFace(False)
        #now the holes used to fix the walls
        #Start with columns, compute holes position
        self.ListNotchColumns = self.CalcNotchPos(self.n_slot_y, self.y_slot_size)
        DebugMsg("List Column Notches:"+str( self.ListNotchColumns)+'\n')
        for i in range(1, self.n_slot_x):
            #For each wall, draw holes corresponding at each notch_y
            for notch in self.ListNotchColumns:
                drawHole(Bottom.path, i*(self.x_slot_size+thickness), notch[0] + thickness, thickness, notch[1], burn)
            
        #Then rows
        self.ListNotchRows = self.CalcNotchPos(self.n_slot_x, self.x_slot_size)
        DebugMsg("List Row Notches:"+str( self.ListNotchRows)+'\n')
        
        for i in range(1, self.n_slot_y):
            #For each wall, draw holes corresponding at each notch_y
            for notch in self.ListNotchRows:
                drawHole(Bottom.path, notch[0] + thickness, i*(self.y_slot_size+thickness), notch[1], thickness, burn)
        
        Bottom.Close()
        self.UpdateBoundingBox(Bottom)
        return

    def drawColumWall(self, index, n_slot_y, y_slot_size, ListNotchPos, length, zbox, xOffset, yOffset, parent):
        '''
        Draw the face, specific case for columns walls
        This is a specific face with cuts for row walls on top
        '''
        DebugMsg("\nDrawColumWall, index="+str(index)+" n_Slot="+str(n_slot_y)+" Slot_Size="+str(y_slot_size)+" Length="+str(length)+" Height="+str(zbox)+" Offset="+str((xOffset, yOffset))+'\n')
        path = th_inkscape_path((xOffset-thickness, yOffset), parent, 'COL_WALL_'+str(index+1))
        
        VNotchLine1 = NotchLine((length,0,1), (length, zbox, 1), math.pi/2, self.z_joint )        #Vertical Notch line
        VNotchLine2 = NotchLine((0,zbox,1), (0, 0, 1), -math.pi/2, self.z_joint )       #Vertical Notch line, reverse


        path.MoveTo(0,0)
        #first H line with cut to accomodate with row walls
        for i in range(1, n_slot_y):
            path.LineToHRel(y_slot_size)
            path.LineToVRel(zbox/2)
            path.LineToHRel(thickness)
            path.LineToVRel(-zbox/2)
        path.LineTo(length, 0)
     
        #Second line (V), this is a notch line
        path.LineTo(length, thickness)
        VNotchLine1.drawNotchLine(path)
        path.LineTo(length, zbox)
        #Third line (H) with notches, but at specific positions. Use reversed because, draw from right to left
        for Notch in reversed(ListNotchPos):
            path.LineTo(Notch[0]+Notch[1], zbox)
            path.LineToVRel(thickness)
            path.LineToHRel(-Notch[1])
            path.LineToVRel(-thickness)
        path.LineTo(0, zbox)
        #and last one
        path.LineTo(0, zbox-thickness)
        VNotchLine2.drawNotchLine(path)
        path.LineTo(0, 0)
        
        #Apply bounding box of path
        DebugMsg("Path Bounding box="+str(((path.xmin, path.ymin), (path.xmax, path.ymax)))+'\n')
        if path.xmin < self.BoundingBox[0]:
            self.BoundingBox[0] = path.xmin
        if path.ymin < self.BoundingBox[1]:
            self.BoundingBox[1] = path.ymin
        if path.xmax > self.BoundingBox[2] - 2:
            self.BoundingBox[2] = path.xmax + 2
        if  path.ymax > self.BoundingBox[3] - 2:
            self.BoundingBox[3] =  path.ymax + 2

        #Close the path
        path.Close()
        path.GenPath()
        #DebugMsg("Closing path, BoundingBox="+str(self.BoundingBox)+'\n')


    def drawRowWall(self, index, n_slot_x, x_slot_size, ListNotchPos, length, zbox, xOffset, yOffset, parent):
        '''
        Draw the face, specific case for row walls
        This is a specific face with cuts for columns walls on bottom
        '''
        DebugMsg("\nDrawRowWall, index="+str(index)+" n_Slot="+str(n_slot_x)+" Slot_Size="+str(x_slot_size)+" Length="+str(length)+" Height="+str(zbox)+" Offset="+str((xOffset, yOffset))+'\n')
        path = th_inkscape_path((xOffset-thickness, yOffset), parent, 'ROW_WALL_'+str(index+1))
        
        VNotchLine1 = NotchLine((length,0,1), (length, zbox, 1), math.pi/2, self.z_joint )        #Vertical Notch line
        VNotchLine2 = NotchLine((0,zbox,1), (0, 0, 1), -math.pi/2, self.z_joint )       #Vertical Notch line, reverse


        path.MoveTo(0,0)
        
        #first H line without cur, so up to length
        path.LineTo(length, 0)

        #Second line (V), this is a notch line
        path.LineTo(length, thickness)
        VNotchLine1.drawNotchLine(path)
        path.LineTo(length, zbox)
        #Third line (H) with notches, but at specific positions. Use reversed because, draw from right to left, also cut openings for columns
        # At each change of group, draw a cut
        group_num = n_slot_x - 1
        for Notch in reversed(ListNotchPos):
            if group_num != Notch[2]:           #   Change of group, draw cut
                path.LineTo(group_num * (x_slot_size + thickness) , zbox)
                path.LineToVRel(-zbox/2)
                path.LineToHRel(-thickness)
                path.LineToVRel(zbox/2)
                group_num = Notch[2]            #Change group for next pass
            path.LineTo(Notch[0]+Notch[1], zbox)
            path.LineToVRel(thickness)
            path.LineToHRel(-Notch[1])
            path.LineToVRel(-thickness)
        path.LineTo(0, zbox)
        #and last one
        path.LineTo(0, zbox-thickness)
        VNotchLine2.drawNotchLine(path)
        path.LineTo(0, 0)
        
        #Apply bounding box of path
        DebugMsg("Path Bounding box="+str(((path.xmin, path.ymin), (path.xmax, path.ymax)))+'\n')
        if path.xmin < self.BoundingBox[0]:
            self.BoundingBox[0] = path.xmin
        if path.ymin < self.BoundingBox[1]:
            self.BoundingBox[1] = path.ymin
        if path.xmax > self.BoundingBox[2] - 2:
            self.BoundingBox[2] = path.xmax + 2
        if  path.ymax > self.BoundingBox[3] - 2:
            self.BoundingBox[3] =  path.ymax + 2

        #Close the path
        path.Close()
        path.GenPath()
        #DebugMsg("Closing path, BoundingBox="+str(self.BoundingBox)+'\n')

    def drawCoffinSide(self, FlagRight, ybox, zlid, z_dome_lid, xOffset, yOffset, parent):
        ''' 
        Draw the sides of the coffin style lid.
        This is a rectangle ybox x zlid with an ellipse (ybox, z_dome_lid) on top of the rectangle
        There a "normal notches on the rectangle, then small notches on the ellipse, because the "front/top/Back" part will be flex
        '''
        DebugMsg("\ndrawCoffinSide, FlagRight="+str(FlagRight)+" ybox="+str(ybox)+" zlid="+str(zlid)+" z_dome_lid="+str(z_dome_lid)+'\n')
        name = 'Lid_Left'
        if FlagRight=='Right':
            name = 'Lid_Right'
        #Change offset in y direction because this one will be drawn from bottom left.
        path = th_inkscape_path((xOffset-thickness, yOffset-zlid - z_dome_lid-thickness), parent, name)
        #First build the notch lines for the rectangle
        VNotchLine1 = NotchLine((0,0,1), (0, -zlid, 1), -math.pi/2, self.z_joint )          #Vertical Notch line (left side)
        VNotchLine2 = NotchLine((ybox,-zlid,1), (ybox, 0, 1), math.pi/2, self.z_joint )     #Vertical Notch line, right side
        
        #First point on (0,0) : bottom/left of the lid
        path.MoveTo(0, 0)
        #The draw left notch line
        VNotchLine1.drawNotchLine(path)
        #The draw the notched ellipse, this ellipse has parameters ybox/2 and z_dome_lid       
        TopLid = Ellipse(ybox/2.0, z_dome_lid)
        TopLid.drawNotchedEllipse(path, math.pi, 2*math.pi, (0, -zlid))
        #Now the second Notch line
        VNotchLine2.drawNotchLine(path)
        #And end with bottom line (straight)
        path.LineTo(0,0)
        #Apply bounding box of path
        DebugMsg("Path Bounding box="+str(((path.xmin, path.ymin), (path.xmax, path.ymax)))+'\n')
        if path.xmin < self.BoundingBox[0]:
            self.BoundingBox[0] = path.xmin
        if path.ymin < self.BoundingBox[1]:
            self.BoundingBox[1] = path.ymin
        if path.xmax > self.BoundingBox[2] - 2:
            self.BoundingBox[2] = path.xmax + 2
        if  path.ymax > self.BoundingBox[3] - 2:
            self.BoundingBox[3] =  path.ymax + 2

        path.Close()
        path.GenPath()
        
    def drawCoffinTop(self, xbox, ybox, zlid, z_dome_lid, xOffset, yOffset, parent):
        ''' 
        Draw the top of the coffin style lid.
        This is 2 rectangle xbox x zlid separated by a flex pattern which has the length half of the ellipse (ybox, z_dome_lid), flex height is xbox 
        '''
        DebugMsg("\ndrawCoffinTop, xbox="+str(xbox)+" ybox="+str(ybox)+" zlid="+str(zlid)+" z_dome_lid="+str(z_dome_lid)+'\n')
        #Change offset in y direction because this one will be drawn from bottom left.
        path = th_inkscape_path((xOffset, yOffset - thickness), parent, 'Coffin_Top')
        DebugMsg("Offset ="+str((xOffset, yOffset))+" Path_Offset="+str((path.offsetX, path.offsetY))+'\n')
        #Create the ellipse object used to draw the flex
        FlexBand = Ellipse(ybox/2.0, z_dome_lid)
        FlexBand.Compute_Ellipse_Params(math.pi, 2*math.pi)
        l = FlexBand.length_ellipse
        #First build the notch lines for the rectangle
        HNotchLine1 = NotchLine((zlid, xbox+thickness, 0), (0, xbox+thickness, 0), math.pi, self.z_joint )     #Horizontal Notch line, bottom left
        HNotchLine2 = NotchLine((0,-thickness,0), (zlid, -thickness, 0), 0, self.z_joint )                     #Horizontal Notch line, top left
        HNotchLine3 = NotchLine((zlid+l, xbox+thickness, 1), (2*zlid+l, xbox+thickness, 1), 0, self.z_joint )  #Horizontal Notch line, bottom right        
        HNotchLine4 = NotchLine((2*zlid+l, -thickness, 1), (zlid+l, -thickness, 1), math.pi, self.z_joint )    #Horizontal Notch line, bottom left

        #In order to minimize move effects, draw the holes for the hinge first
        #Draw holes for the hinge(s)
        for Hinge in self.HingeList:
            HingePos = Hinge[2] + 2*thickness
            path.MoveTo(3.5*thickness, HingePos + 0.5*SteelHingeSpacing)
            path.LineToVRel(thickness)
            path.LineToHRel(thickness)
            path.LineToVRel(-thickness)
            path.LineToHRel(-thickness)
            path.MoveTo(3.5*thickness, HingePos + 2*thickness + 1.5*SteelHingeSpacing)
            path.LineToVRel(thickness)
            path.LineToHRel(thickness)
            path.LineToVRel(-thickness)
            path.LineToHRel(-thickness)

        #First point on (zlid,0) : bottom/left of the lid
        path.MoveTo(zlid, xbox+thickness)
        #The draw left notch line
        HNotchLine1.drawNotchLine(path)
        #The draw the bottom line with cuts for the hinge(s)
        #Now draw holes for the hinge(s), reverse because draw from right to left
        for Hinge in reversed(self.HingeList):
            HingePos = Hinge[2] + thickness
            #First H line up to end of 2nd hinge
            path.LineTo(0, HingePos + 5*thickness + 2.5*SteelHingeSpacing)
            #Then Hinge
            path.LineToHRel(1.5*thickness + 0.5*SteelHingeSpacing)
            path.LineToVRel(-thickness - SteelHingeSpacing)
            path.LineToHRel(thickness - 0.5*SteelHingeSpacing)
            path.LineToVRel(-thickness)
            path.LineToHRel(-thickness + 0.5*SteelHingeSpacing)
            path.LineToVRel(-thickness - SteelHingeSpacing)
            path.LineToHRel(thickness - 0.5*SteelHingeSpacing)
            path.LineToVRel(-thickness)
            path.LineToHRel(-thickness + 0.5*SteelHingeSpacing)
            path.LineToVRel(-thickness - SteelHingeSpacing)
            path.LineToHRel(-1.5*thickness - 0.5*SteelHingeSpacing)
        #Then draw up to corner
        path.LineTo(0, -thickness)
        #Now the second Notch line
        HNotchLine2.drawNotchLine(path)
        #Then the flex band
        FlexBand.drawFlexEllipse(path, xbox, self.options.SkipFlexLines, (zlid, -thickness))
        #Then the third notch line
        HNotchLine3.drawNotchLine(path)
        #Then the straight line up to the next corner (top right)
        path.LineTo(2*zlid+l, -thickness)
        #And the last line
        HNotchLine4.drawNotchLine(path)
        #Apply bounding box of path
        DebugMsg("Path Bounding box="+str(((path.xmin, path.ymin), (path.xmax, path.ymax)))+'\n')
        if path.xmin < self.BoundingBox[0]:
            self.BoundingBox[0] = path.xmin
        if path.ymin < self.BoundingBox[1]:
            self.BoundingBox[1] = path.ymin
        if path.xmax > self.BoundingBox[2] - 2:
            self.BoundingBox[2] = path.xmax + 2
        if  path.ymax > self.BoundingBox[3] - 2:
            self.BoundingBox[3] =  path.ymax + 2
        path.GenPath()
        
    def effect(self):
        """
        Draws a card box box, based on provided parameters
        """
        global burn, thickness
        
        # input sanity check
        error = False
        if self.options.thickness <  1 or self.options.thickness >  10:
            inkex.errormsg('Error: thickness should be at least 1mm and less than 10mm')
            error = True

        if error:
            exit()

        self.n_slot_x = self.options.n_slot_x
        self.n_slot_y = self.options.n_slot_y


        # convert units
        unit = self.options.unit

        xbox = self.svg.unittouu(str(self.options.x) + unit)
        ybox = self.svg.unittouu(str(self.options.y) + unit)
        zbox = self.svg.unittouu(str(self.options.z) + unit)
        zlid = self.svg.unittouu(str(self.options.z_lid) + unit)
        z_dome_lid = self.svg.unittouu(str(self.options.z_dome_lid) + unit)

        if self.options.StraigthCorners:
            back_left_radius = 0
            back_right_radius = 0
            front_right_radius = 0
            front_left_radius = 0
        else:
            back_left_radius = self.svg.unittouu(str(self.options.back_left_radius) + unit)
            back_right_radius = self.svg.unittouu(str(self.options.back_right_radius) + unit)
            front_right_radius = self.svg.unittouu(str(self.options.front_right_radius) + unit)
            front_left_radius = self.svg.unittouu(str(self.options.front_left_radius) + unit)
        
        max_radius = max(back_left_radius, back_right_radius, front_right_radius, front_left_radius)
        thickness = self.svg.unittouu(str(self.options.thickness) + unit)
        burn = self.svg.unittouu(str(self.options.burn) + unit)

        self.x_joint = self.svg.unittouu(str(self.options.x_joint) + unit) 
        self.y_joint = self.svg.unittouu(str(self.options.y_joint) + unit) 
        self.z_joint = self.svg.unittouu(str(self.options.z_joint) + unit) 

        self.x_slot_size = (xbox  - (1+self.n_slot_x)*thickness)/self.n_slot_x 
        self.y_slot_size = (ybox - (1+self.n_slot_y)*thickness)/self.n_slot_y 
        
        if self.x_slot_size < 18 or self.y_slot_size < 18:
            inkex.errormsg('Error: each slot should be at least 18mm large, here x_slot_size='+str(self.x_slot_size)+ ' y_slot_size='+str(self.y_slot_size))
            exit()
        if self.x_slot_size < max_radius or self.y_slot_size < max_radius: 
            inkex.errormsg('Error: slot size should be greater than rounded corner radius, here x_slot_size='+str(self.x_slot_size)+ ' y_slot_size='+str(self.y_slot_size))
            exit()

                
        svg = self.document.getroot()
        docWidth = self.svg.unittouu(svg.get('width'))
        docHeigh = self.svg.unittouu(svg.attrib['height'])

        layer = etree.SubElement(svg, 'g')
        layer.set(inkex.addNS('label', 'inkscape'), 'Generic Box')
        layer.set(inkex.addNS('groupmode', 'inkscape'), 'layer')
        self.group = etree.SubElement(layer, 'g')
        OpenDebugFile()
        
        HasLid = False
        HasNormalLid = False
        #Compute joint size if auto is chosen
        if self.options.AutoSizeJoints:
            self.x_joint, self.y_joint, self.z_joint = self.ComputeJointSize(xbox, ybox, zbox, back_left_radius, back_right_radius, front_right_radius, front_left_radius)
        #Default case, for the top lines, front and back joints are x_joint in size, left and right joints are y_joint in size
        self.front_joint = self.x_joint
        self.back_joint = self.x_joint
        self.right_joint = self.y_joint
        self.left_joint = self.y_joint
        
        DebugMsg("Joints size ="+str((self.x_joint, self.y_joint, self.z_joint))+'\n')
        DebugMsg("Slots X N="+str(self.n_slot_x)+" size="+str(self.x_slot_size)+'\n')
        DebugMsg("Slots Y N="+str(self.n_slot_y)+" size="+str(self.y_slot_size)+'\n')
        
        #Now, check if internal walls should be drawn
        self.InternalWalls_LR = False
        self.InternalWalls_FB = False
        zbox_internal_walls = zbox          #Height of internal walls
        
        #If there are slots inside the box, also draw internal walls
        if self.n_slot_x  > 1:
            self.InternalWalls_FB = True
        if self.n_slot_y  > 1:
            self.InternalWalls_LR = True

        # If lid is sliding, there are always internal walls left and right
        if self.options.lid_type == 'Sliding':
            self.InternalWalls_LR = True
            zbox += thickness               #Also increase box height to take into account the sliding top, but NOT internal walls height
            zbox_internal_walls -= 0.2      #Indeed, reduce internal wall height to ease sliding
            if back_left_radius > 0 or back_right_radius > 0:
                inkex.errormsg('Error: Sliding lid is incompatible with rounded corners on back')
                exit()
            self.front_joint = 0        #No joint on front top
        
        # If there is no lid, no notches on top
        if self.options.lid_type == 'Without':
            self.front_joint = 0        #No joint on front top
            self.back_joint = 0         #No joint on back top
            self.right_joint = 0        #No joint on right top
            self.left_joint = 0         #No joint on left top
            #As top edges are external, but without notches, just decrease height by thickness
            zbox -= thickness
        
        # If this is a real lid, no round corners allowed on back
        if self.options.lid_type == 'WoodHinge' or self.options.lid_type == 'SteelHinge' or self.options.lid_type == 'Coffin':
            if back_left_radius > 0 or back_right_radius > 0:
                inkex.errormsg('Error: real lid option is incompatible with rounded corners on back')
                exit()
            self.front_joint = 0        #No joint on front top
            self.back_joint = 0         #No joint on back top
            self.right_joint = 0        #No joint on right top
            self.left_joint = 0         #No joint on left top
            #As top edges are external, but without notches, just decrease height by thickness
            zbox -= thickness
            zlid -= thickness
            if self.options.lid_type == 'Coffin':
                if front_left_radius > 0 or front_right_radius > 0:
                    inkex.errormsg('Error: coffin lid option is incompatible with rounded corners')
                    exit()
                HasCoffinlid = True
                HasLid = False
            else:
                HasCoffinlid = False
                HasLid = True

        if self.options.lid_type == 'SteelHinge' or self.options.lid_type == 'Coffin':
            #Compute placement of hinges
            #First compute hinge width. Each hinge has 5 elements with thickness width whiche should be slighly spaced for the main box elements (3)
            hingeWidth = 5*thickness + 3*SteelHingeSpacing
            if ( hingeWidth > self.x_slot_size - 3 ):
                inkex.errormsg('Error: no space for hinge within slots, slots should be at least '+str(hingeWidth+3)+'mm wide')
                exit(1)
            #if the box is small with only one slot in x direction try with only one hinge
            if self.n_slot_x == 1 and self.x_slot_size < 2 * hingeWidth + 30:
                self.HingeList.append = (0, (self.x_slot_size - hingeWidth)/2.0, (self.x_slot_size - hingeWidth)/2.0)      # One hinge, starting at the middle of slot 0 (the only one)
            elif self.n_slot_x == 2:
                #in this case place hinge in first and last slot.
                # Exact position depend on slot width, try to place hinge at about 1/3 of the slot
                HingePos = max(self.x_slot_size/3 -  hingeWidth/2, 2)
                if HingePos < 8:
                    HingePos = max(self.x_slot_size/2.5 -  hingeWidth/2, 2)         #1/3 is very close from start, so change to 1/2.5
                self.HingeList.append((0, HingePos, HingePos))
                self.HingeList.append((self.n_slot_x-1, self.x_slot_size - HingePos, (self.n_slot_x-1)*(self.x_slot_size+thickness) + (self.x_slot_size - HingePos - hingeWidth) ))
            elif self.n_slot_x <= 6:
                #in this case place hinge in first and last slot.
                # Exact position depend on slot width, try to place hinge at about 1/2 of the slot
                HingePos = (self.x_slot_size -  hingeWidth)/2.0
                self.HingeList.append((0, HingePos, HingePos))
                self.HingeList.append((self.n_slot_x-1, self.x_slot_size - HingePos, (self.n_slot_x-1)*(self.x_slot_size+thickness) + (self.x_slot_size - HingePos - hingeWidth) ))
            else:
                #a lot of slots, place hinge in second and before last slot, at center of slots 
                HingePos = (self.x_slot_size -  hingeWidth)/2.0
                self.HingeList.append((1, HingePos, self.x_slot_size + thickness + HingePos ))
                self.HingeList.append((self.n_slot_x-2, HingePos, (self.n_slot_x-2)*(self.x_slot_size+thickness) + (self.x_slot_size - HingePos - hingeWidth)))
            DebugMsg("Lid with steel hinge\n")
            DebugMsg("Hinge width="+str(hingeWidth)+", Hinge pos="+str(self.HingeList)+"\n")


        #Draw external faces which are planes, begin with top
        
        self.BuildTop(xbox, ybox, back_left_radius, back_right_radius, front_right_radius, front_left_radius)

        self.BuildBottom(xbox, ybox, back_left_radius, back_right_radius, front_right_radius, front_left_radius)
        
        ''' Draw sides, which could be rounded (with flex)
            For boxes with lid, draw also the lid, just above the side.
            There are 16 cases
            TL	TR	BR	BL	Flex	                        Straight
            0	0	0	0	NO	                            ALL                 OK
            0	0	0	1	Left → Front	                Back, Right         OK
            0	0	1	0	Front → Right	                Back, Left          OK
            0	0	1	1	Left → Front → Right	        Back                OK
            0	1	0	0	Right → Back	                Front, Left         OK
            0	1	0	1	Right → Back, Left → Front  	No                  OK
            0	1	1	0	Front --> Right --> Back	    Left                OK
            0	1	1	1	Left → Front → Right → Back	    No                  OK
            1	0	0	0	Back → Left	                    Right, Front        OK
            1	0	0	1	Back → Left → Front	            Right               OK
            1	0	1	0	Back → Left, Front → Right	    No                  OK
            1	0	1	1	Back → Left → Front → Right	    No                  OK
            1	1	0	0	Right → Back → Left	            Front               OK
            1	1	0	1	Right → Back → Left  → Front	No                  OK
            1	1	1	0	Front → Right → Back → Left	    No                  OK
            1	1	1	1	All Flex    		            No
        '''
        FlexBandList = []        #empty list at init
        RightFace = None
        LeftFace = None
        ypos = -self.BoundingBox[3]
        yposface = ypos
        xpos = 0.0
        LidFace = None
        if front_left_radius == 0 and front_right_radius == 0:
            if HasLid:
                DebugMsg("Draw font lid\n")
                LidFace = BoxFace('Lid_Front', CornerPoint((0,0), 0, 0, 0), 
                              self.x_joint, CornerPoint((xbox,0), 0, 0, 0), 
                              self.z_joint, CornerPoint((xbox,zlid), 0, 0, 0),
                              self.front_joint, CornerPoint((0,zlid), 0, 0, 0),
                              self.z_joint, self.group, [xpos, ypos])        #Draw face just below previous drawings
                LidFace.drawSimpleFace(True)
                self.UpdateBoundingBox(LidFace)    #Now update bounding box, to place back face just below
                yposface = -self.BoundingBox[3]
            DebugMsg("\nStraight face for front\n")
            #No round, front is straight
            #Front is xbox * zbox, all corners are external in each direction
            Face = BoxFace('Front', CornerPoint((0,0), 0, 0, 0), 
                          self.front_joint, CornerPoint((xbox,0), 0, 0, 0), 
                          self.z_joint, CornerPoint((xbox,zbox), 0, 0, 0),
                          self.x_joint, CornerPoint((0,zbox), 0, 0, 0),
                          self.z_joint, self.group, [xpos, yposface])        #Draw face just below previous drawings
            Face.drawSimpleFace(True)
            xpos = -Face.BoundingBox[2]-2
            self.UpdateBoundingBox(Face)    #Now update bounding box
        elif front_left_radius == 0:
            #Rounded corner on Front right
            #Straight corner on Front/left, there is a flex band starting on front left
            if back_right_radius == 0:
                #Straight corner on Front / right, Flex on front --> right, BL to TR
                DebugMsg("\nFlex on front --> Right\n")
                FlexBand = ('Flex_Front_Right', 0, 1,                           #Draw Front then Right so first element is external and last internal
                            (xbox, self.front_joint, front_right_radius, self.x_joint),       #Then Front Notch Line and round corner r= front_right_radius
                            #Then Right notch line withount rounded corner, the last parameter is used when WoodHinge to draw the top circle
                            (ybox, self.right_joint, 0, self.y_joint, self.options.lid_type == 'WoodHinge')) 
                FlexBandList.append(FlexBand)
            elif back_left_radius == 0:
                #Straight corner on back left, flex band is front + right + back
                DebugMsg("\nFlex on front --> right --> back\n")
                FlexBand = ('Flex_Front_Right_Back', 0, 0,                      #Draw Front then Right and Back so first element is external and last external
                            (xbox, self.front_joint, front_right_radius, self.x_joint),       #Then Front Notch Line and round corner r= front_right_radius
                            (ybox, self.right_joint, back_right_radius, self.y_joint),        #Then Right notch line and Back/Right rounded corner
                            (xbox, self.back_joint, 0, self.x_joint))                         #Then Back notch line withount rounded corner
                FlexBandList.append(FlexBand)
            else:
                #flex band is front + right + back + left
                DebugMsg("\nFlex on front --> right --> back --> left\n")
                FlexBand = ('Flex_Front_Right_Back_Left', 0, 1,                 #Draw Front then Right, Back and left so first element is external and last internal
                            (xbox, self.front_joint, front_right_radius, self.x_joint),       #Then Front Notch Line and round corner r= front_right_radius
                            (ybox, self.right_joint, back_right_radius, self.y_joint),        #Then Right notch line and Back/Right rounded corner
                            (xbox, self.back_joint, back_left_radius, self.x_joint),          #Then Back notch line with Back/Left rounded corner
                            (ybox, self.left_joint, 0))                         #At last, Left line without rounded corner
                FlexBandList.append(FlexBand)
            
        if back_left_radius == 0 and back_right_radius == 0:
            if HasLid:
                DebugMsg("Draw back lid\n")
                LidFace = BoxFace('Lid_Back', CornerPoint((0,0), 0, 0, 0), 
                              self.x_joint, CornerPoint((xbox,0), 0, 0, 0), 
                              self.z_joint, CornerPoint((xbox,zlid), 0, 0, 0),
                              self.back_joint, CornerPoint((0,zlid), 0, 0, 0),
                              self.z_joint, self.group, [xpos, ypos])        #Draw face just right previous drawings
                if self.options.lid_type == 'WoodHinge':
                    LidFace.drawLidBackWoodHinge(True)
                else:       #This is SteelHinge or Coffin
                    LidFace.drawLidBackSteelHinge(self.HingeList, True)
                if yposface == ypos:
                    self.UpdateBoundingBox(LidFace)    #Now update bounding box, if not already done, to place back face just below
                    yposface = -self.BoundingBox[3]

            #Back is xbox * zbox, all corners are external in each direction
            DebugMsg("\nStraight face for Back\n")
            if self.options.lid_type == 'Sliding':
                #In this case, not a simple face, so we use a specific function. Also, top line is internal in y and external in x
                Face = BoxFace('Back', CornerPoint((0,0), 0, 0, 1), 
                              self.back_joint, CornerPoint((xbox,0), 0, 0, 1), 
                              self.z_joint, CornerPoint((xbox,zbox), 0, 0, 0),
                              self.x_joint, CornerPoint((0,zbox), 0, 0, 0),
                              self.z_joint, self.group, [xpos, yposface])        #Draw face just right from previous one 
                Face.drawExternalBackSlidingLid(True)
            elif self.options.lid_type == 'WoodHinge':
                #In this case, not a simple face, so we use a specific function.
                Face = BoxFace('Back', CornerPoint((0,0), 0, 0, 0), 
                              0, CornerPoint((xbox,0), 0, 0, 0),            #No joint here !
                              self.z_joint, CornerPoint((xbox,zbox), 0, 0, 0),
                              self.x_joint, CornerPoint((0,zbox), 0, 0, 0),
                              self.z_joint, self.group, [xpos, yposface])        #Draw face just right from previous one 
                Face.drawExternalBackWoodHingeLid(True)
            elif self.options.lid_type == 'SteelHinge' or self.options.lid_type == 'Coffin':
                #In this case, not a simple face, so we use a specific function.
                Face = BoxFace('Back', CornerPoint((0,0), 0, 0, 0), 
                              0, CornerPoint((xbox,0), 0, 0, 0),            #No joint here !
                              self.z_joint, CornerPoint((xbox,zbox), 0, 0, 0),
                              self.x_joint, CornerPoint((0,zbox), 0, 0, 0),
                              self.z_joint, self.group, [xpos, yposface])        #Draw face just right from previous one 
                Face.drawExternalBackSteelHingeLid(self.HingeList, True)
            else:
                Face = BoxFace('Back', CornerPoint((0,0), 0, 0, 0), 
                              self.back_joint, CornerPoint((xbox,0), 0, 0, 0), 
                              self.z_joint, CornerPoint((xbox,zbox), 0, 0, 0),
                              self.x_joint, CornerPoint((0,zbox), 0, 0, 0),
                              self.z_joint, self.group, [xpos, yposface])        #Draw face just right from previous one 
                Face.drawSimpleFace(True)
            self.UpdateBoundingBox(Face)    #Now update bounding box
            xpos = -Face.BoundingBox[2]-2
        elif back_right_radius == 0:
            #Rounded corner on Back left
            #Straight corner on Back/right, there is a flex band starting on back right
            if front_left_radius == 0:
                #Straight corner on front / left, flex band is back + left
                DebugMsg("\nFlex on back --> left\n")
                FlexBand = ('Flex_Back_Left', 0, 1,                             #Draw Back then Left so first element is external and last internal
                            (xbox, self.back_joint, back_left_radius, self.x_joint),          #Back Notch Line and round corner r= front_right_radius
                            (ybox, self.left_joint, 0, self.y_joint))                         #Then Left notch line without rounded corner
                FlexBandList.append(FlexBand)

            elif front_right_radius == 0:
                #Straight corner on bottom right, flex band is back + left + front
                DebugMsg("\nFlex on back --> left --> front\n")
                FlexBand = ('Flex_Back_Left_Front', 0, 0,                       #Draw Back then Left then Front so first element is external and last External
                            (xbox, self.back_joint, back_left_radius, self.x_joint),          #Back Notch Line and round corner r= front_right_radius
                            (ybox, self.left_joint, front_left_radius, self.y_joint),         #Then Left notch line and Front/Left rounded corner
                            (xbox, self.front_joint, 0, self.x_joint))                        #At last, Front line without rounded corner
                FlexBandList.append(FlexBand)
            else:
                #flex band is back + left + front + right
                DebugMsg("\nFlex on back --> left --> front --> right\n")
                FlexBand = ('Flex_Back_Left_Front_Right', 0, 1,                 #Draw Back then Left then Front Then Right so first element is external and last Inetrnal
                            (xbox, self.back_joint, back_left_radius, self.x_joint),          #Back Notch Line and round corner r= front_right_radius
                            (ybox, self.left_joint, front_left_radius, self.y_joint),         #Then Left notch line and Front/Left rounded corner
                            (xbox, self.front_joint, front_right_radius, self.x_joint),       #Then Front line with Front/Right rounded corner
                            (ybox, self.right_joint, 0, self.y_joint))                        #At last Right line without rounded corner
                FlexBandList.append(FlexBand)


        if back_left_radius == 0 and front_left_radius == 0:
            if HasLid:
                DebugMsg("Draw left lid\n")
                LidFace = BoxFace('Lid_Left', CornerPoint((0,0), 0, 1, 0), 
                              self.y_joint, CornerPoint((ybox,0), 0, 1, 0), 
                              self.z_joint, CornerPoint((ybox,zlid), 0, 1, 0),
                              self.left_joint, CornerPoint((0,zlid), 0, 1, 0),
                              self.z_joint, self.group, [xpos, ypos])        #Draw face just right previous drawings
                if self.options.lid_type == 'WoodHinge':
                    LidFace.drawLidSideWoodHinge(0, True)
                elif self.options.lid_type == 'SteelHinge':       #This is SteelHinge
                    LidFace.drawSimpleFace(True)
            # No round for left face
            # Left is ybox * zbox, corners are external in y but internal in x
            DebugMsg("\nStraight face for Left\n")
            if self.options.lid_type == 'Sliding':
                delta_yposface = 2*thickness + 2
            else:
                delta_yposface = 0
            LeftFace = BoxFace('Left', CornerPoint((0,0), 0, 1, 0, self.options.lid_type == 'WoodHinge'), 
                          self.left_joint, CornerPoint((ybox,0), 0, 1, 0), 
                          self.z_joint, CornerPoint((ybox,zbox), 0, 1, 0),
                          self.y_joint, CornerPoint((0,zbox), 0, 1, 0),
                          self.z_joint, self.group, [xpos, yposface-delta_yposface])        #Draw face just right from previous drawings
            LeftFace.drawSimpleFace(True)
            self.UpdateBoundingBox(LeftFace)    #Now update bounding box
            if self.options.lid_type == 'Sliding':
                LeftFace.drawSideLineNotches(xpos, yposface)         #Right face is straight
            xpos = -LeftFace.BoundingBox[2]-2
        elif back_left_radius == 0:
            #Rounded corner on Front left
            #Straight corner on Back/left, there is a flex band starting on Back left
            if front_right_radius == 0:
                #Straight corner on Front / Right, flex band is Left + Front
                DebugMsg("\nFlex on Left --> Front\n")
                FlexBand = ('Flex_Left_Front', 1, 0,                            #Draw Left then Front so first element is internal and last external
                             #Left Notch Line and round corner r= front_left_radius, last parameter used when WoodHing to draw top circles
                            (ybox, self.left_joint, front_left_radius, self.y_joint, self.options.lid_type == 'WoodHinge'),       
                            (xbox, self.front_joint, 0, self.x_joint))                        #Then Front notch line without rounded corner
                FlexBandList.append(FlexBand)
            elif back_right_radius == 0:
                #Straight corner on Back right, flex band is Left + Back + Right
                DebugMsg("\nFlex on Left --> Front --> Right\n")
                FlexBand = ('Flex_Left_Front_Right', 1, 1,                      #Draw Left then Front and Right so first element is internal and last internal
                             #Left Notch Line and round corner r= front_left_radius, last parameter used when WoodHing to draw top circles
                            (ybox, self.left_joint, front_left_radius, self.y_joint, self.options.lid_type == 'WoodHinge'),
                            (xbox, self.front_joint, front_right_radius, self.x_joint),       #Then Front notch line with Front/Right rounded corner
                            (ybox, self.right_joint, 0, self.y_joint, self.options.lid_type == 'WoodHinge'))     #And Right notch line without rounded corner                 
                FlexBandList.append(FlexBand)
            else:
                #flex band on Left --> front --> right --> Back
                DebugMsg("\nFlex on Left --> front --> right --> Back\n")
                FlexBand = ('Flex_Left_Front_Right_Back', 1, 0,                 #Draw Left then Front, Right and Back so first element is internal and last external
                            (ybox, self.left_joint, front_left_radius, self.y_joint),         #Left Notch Line and round corner r= front_left_radius
                            (xbox, self.front_joint, front_right_radius, self.x_joint),       #Then Front notch line with Front/Right rounded corner
                            (ybox, self.right_joint, back_right_radius, self.y_joint),        #And Right notch line with Back/Right rounded corner    
                            (xbox, self.back_joint, 0, self.x_joint))
                FlexBandList.append(FlexBand)


        if back_right_radius == 0 and front_right_radius == 0:
            #Right is the same
            if HasLid:
                DebugMsg("Draw Right lid\n")
                LidFace = BoxFace('Lid_Right', CornerPoint((0,0), 0, 1, 0), 
                              self.y_joint, CornerPoint((ybox,0), 0, 1, 0), 
                              self.z_joint, CornerPoint((ybox,zlid), 0, 1, 0),
                              self.right_joint, CornerPoint((0,zlid), 0, 1, 0),
                              self.z_joint, self.group, [xpos, ypos])        #Draw face just right previous drawings
                if self.options.lid_type == 'WoodHinge':
                    LidFace.drawLidSideWoodHinge(1, True)
                elif self.options.lid_type == 'SteelHinge':       #This is SteelHinge
                    LidFace.drawSimpleFace(True)

            # Right is ybox * zbox, corners are external in y but internal in x
            DebugMsg("\nStraight face for Right\n")
            if self.options.lid_type == 'Sliding':
                delta_yposface = 2*thickness + 2
            else:
                delta_yposface = 0
            RightFace = BoxFace('Right', CornerPoint((0,0), 0, 1, 0), 
                          self.right_joint, CornerPoint((ybox,0), 0, 1, 0, self.options.lid_type == 'WoodHinge'), 
                          self.z_joint, CornerPoint((ybox,zbox), 0, 1, 0),
                          self.y_joint, CornerPoint((0,zbox), 0, 1, 0),
                          self.z_joint, self.group, [xpos, yposface-delta_yposface])        #Draw face just below previous drawings
            RightFace.drawSimpleFace(True)
            if self.options.lid_type == 'Sliding':
                RightFace.drawSideLineNotches(xpos, yposface)         #Right face is straight
            self.UpdateBoundingBox(RightFace)    #Now update bounding box
            xpos = -RightFace.BoundingBox[2]-2
        elif front_right_radius == 0:
            #Rounded corner on Back right
            #Straight corner on Front right
            if back_left_radius == 0:
                #Straight corner on top / left, flex band is Left + Back
                DebugMsg("\nFlex on Right --> Back\n")
                FlexBand = ( 'Flex_Right_Back', 1, 0,                           #Draw Right then Back so first element is internal and last external
                            (ybox, self.right_joint, back_right_radius, self.y_joint),       #Left Notch Line and round corner r= back_right_radius
                            (xbox, self.back_joint, 0, self.x_joint))                        #Then Back notch line without rounded corner
                FlexBandList.append(FlexBand)
            elif front_left_radius == 0:
                #Straight corner on Front left, flex band is Right --> Back --> Left
                DebugMsg("\nFlex on Right --> Back --> Left\n")
                FlexBand = ('Flex_Right_Back_Left', 1, 1,                       #Draw Right then Back and left so first element is internal and last internal
                            (ybox, self.right_joint, back_right_radius, self.y_joint),        #Left Notch Line and round corner r= back_right_radius
                            (xbox, self.back_joint, back_left_radius, self.x_joint),          #Then Back notch line with Back/Left rounded corner
                            (ybox, self.left_joint, 0, self.y_joint))                         #And left Notch line without rounded corner
                FlexBandList.append(FlexBand)
            else:
                #flex band on Left --> front --> right --> Back --> Front
                DebugMsg("\nFlex on Right --> Back --> Left --> Front\n")
                FlexBand = ('Flex_Right_Back_Left_Front', 1, 1,                 #Draw Right then Back and left so first element is internal and last internal
                            (ybox, self.right_joint, back_right_radius, self.y_joint),        #Left Notch Line and round corner r= back_right_radius
                            (xbox, self.back_joint, back_left_radius, self.x_joint),          #Then Back notch line with Back/Left rounded corner
                            (ybox, self.left_joint, front_left_radius, self.y_joint),         #Then left Notch line with Front/Left rounded corner
                            (xbox, self.front_joint, 0, self.x_joint))                        #And Front notch line without rounded corner
                FlexBandList.append(FlexBand)

        if front_right_radius > 0 and back_right_radius > 0 and back_left_radius > 0 and front_left_radius > 0:
            #Specific case, all corners are rounded
            FlexBand = ('Flex_All', 1, 1,                                   #Draw flex all around the box with clips
                        (xbox, self.back_joint, back_right_radius, self.x_joint),         #(Half) Back Notch Line and round corner r= back_right_radius
                        (ybox, self.right_joint, front_right_radius, self.y_joint),       #Then Right notch line with Front/Right rounded corner
                        (xbox, self.front_joint, front_left_radius, self.x_joint),        #Then front notch line with Front/Left rounded corner
                        (ybox, self.left_joint, back_left_radius, self.y_joint),          #Then Laft notch line with Back/Left rounded corner
                        (xbox, self.back_joint, back_right_radius, self.x_joint))         #And another back line, (half)
            Face = FlexFace(FlexBand, 0, zbox, self.z_joint, self.group, [xpos, ypos])
            Face.drawRoundedFlexFace(True)
            self.UpdateBoundingBox(Face)    #Now update bounding box
        else:
            for FlexBand in FlexBandList:
                if HasLid:
                    FaceLid = FlexFace(FlexBand, 1, zlid, self.z_joint, self.group, [xpos, ypos])
                    FaceLid.drawFlexFace(True)
                    if yposface == ypos:
                        yposface = ypos - FaceLid.BoundingBox[3] - 2
                Face = FlexFace(FlexBand, 0, zbox, self.z_joint, self.group, [xpos, yposface])
                Face.drawFlexFace(True)
                xpos -= Face.BoundingBox[2] + 2

        #if sliding top, generate specific elements to let the lid slide
        if self.options.lid_type == 'Sliding':
            if len(FlexBandList) > 0:
                #Case with flex 
                #This code works because with sliding top, there is AT MOST one flex band.
                Face.drawSideLineNotches()
        self.UpdateBoundingBox(Face)    #Now update bounding box
        
        ypos = -self.BoundingBox[3]
        xpos = 0.0

        # If coffin draw the lid here
        if self.options.lid_type == 'Coffin':
            self.drawCoffinSide('Left', ybox, zlid+thickness, z_dome_lid, xpos, ypos, self.group)
            xpos -= ybox + 2*thickness + 2
            self.drawCoffinSide('Right', ybox, zlid+thickness, z_dome_lid, xpos, ypos, self.group)
            xpos -= ybox + 2*thickness + 2
            self.drawCoffinTop(xbox, ybox, zlid+thickness, z_dome_lid, xpos, ypos, self.group)

        ypos = -self.BoundingBox[3]
        xpos = 0.0
        # Then draw internal walls
        # First Back and front
        # Rectangle with joints on sides, not on top and bottom
        # 
        if self.InternalWalls_FB:
            DebugMsg("\nDrawing Internal Back\n")
            if self.InternalWalls_LR:
                left_joint = self.z_joint
            else:
                left_joint = 0          #Only draw joints if there is a left side !                
            #If rounded corner, shorten size by radius, if not by thickness
            if back_left_radius > 0:
                left_joint = 0
                d1 = back_left_radius
            else:
                d1 = thickness
            if self.InternalWalls_LR:
                right_joint = self.z_joint
            else:
                right_joint = 0          #Only draw joints if there is a right side !                
            #If rounded corner, shorten size by radius, if not by thickness
            if back_right_radius > 0:
                right_joint = 0
                d2 = back_right_radius
            else:
                d2 = thickness

            InternalBack = BoxFace('Int_Back', CornerPoint((0,0), 0, 0, 1),         #First corner, external on X, internal on Y sides 
                              0, CornerPoint((xbox-d1-d2,0), 0, 0, 1), 
                              right_joint, CornerPoint((xbox-d1-d2, zbox_internal_walls), 0, 0, 1),
                              0, CornerPoint((0,zbox_internal_walls), 0, 0, 1),
                              left_joint, self.group, [xpos, ypos])        #Draw face just below previous drawings
            if self.options.lid_type == 'SteelHinge' or self.options.lid_type == 'Coffin':
                #Special case, should cut some spce for the hinge in the back, so add the last parameter
                InternalBack.drawFaceWithHoles(self.n_slot_x, self.x_slot_size, d1 - thickness, self.z_joint, True, self.HingeList)
            else:
                InternalBack.drawFaceWithHoles(self.n_slot_x, self.x_slot_size, d1 - thickness, self.z_joint, True)
            xpos = -InternalBack.BoundingBox[2]-2


            if self.InternalWalls_LR:
                left_joint = self.z_joint
            else:
                left_joint = 0          #Only draw joints if there is a left side !                
            if front_left_radius > 0:
                left_joint = 0
                d1 = front_left_radius
            else:
                d1 = thickness
            if self.InternalWalls_LR:
                right_joint = self.z_joint
            else:
                right_joint = 0          #Only draw joints if there is a right side !                
            if front_right_radius > 0:
                right_joint = 0
                d2 = front_right_radius
            else:
                d2 = thickness
            DebugMsg("\nDrawing Internal Front\n")
            InternalFront = BoxFace('Int_Front', CornerPoint((0,0), 0, 0, 1),         #First corner, external on X, internal on Y sides 
                              0, CornerPoint((xbox-d1-d2,0), 0, 0, 1), 
                              right_joint, CornerPoint((xbox-d1-d2, zbox_internal_walls), 0, 0, 1),
                              0, CornerPoint((0,zbox_internal_walls), 0, 0, 1),
                              left_joint, self.group, [xpos, ypos])        #Draw face just below previous drawings
            InternalFront.drawFaceWithHoles(self.n_slot_x, self.x_slot_size, d1 - thickness, self.z_joint, True)
            xpos = -InternalFront.BoundingBox[2]-2

        # Then Left and right internal walls if needed.
        if self.InternalWalls_LR:
            DebugMsg("\nDrawing Internal Left\n")
            if self.InternalWalls_FB:
                left_joint = self.z_joint
            else:
                left_joint = 0          #Only draw joints if there is a left side !                
            if back_left_radius > 0:
                left_joint = 0
                d1 = back_left_radius
            else:
                d1 = thickness
            if self.InternalWalls_FB:
                right_joint = self.z_joint
            else:
                right_joint = 0          #Only draw joints if there is a right side !                
            if front_left_radius > 0:
                right_joint = 0
                d2 = front_left_radius
            else:
                d2 = thickness

            InternalLeft = BoxFace('Int_Left', CornerPoint((0,0), 0, 1, 1),         #First corner, internal on both sides 
                              0, CornerPoint((ybox-d1-d2,0), 0, 1, 1), 
                              right_joint, CornerPoint((ybox-d1-d2, zbox_internal_walls), 0, 1, 1),
                              0, CornerPoint((0,zbox_internal_walls), 0, 1, 1),
                              left_joint, self.group, [xpos, ypos])        #Draw face just below previous drawings
            InternalLeft.drawFaceWithHoles(self.n_slot_y, self.y_slot_size, d1 - thickness, self.z_joint, True)
            xpos = -InternalLeft.BoundingBox[2]-2


            if self.InternalWalls_FB:
                left_joint = self.z_joint
            else:
                left_joint = 0          #Only draw joints if there is a left side !                
            if front_right_radius > 0:
                left_joint = 0
                d1 = front_right_radius
            else:
                d1 = thickness
            if self.InternalWalls_FB:
                right_joint = self.z_joint
            else:
                right_joint = 0          #Only draw joints if there is a right side !                
            if back_right_radius > 0:
                right_joint = 0
                d2 = back_right_radius
            else:
                d2 = thickness
            DebugMsg("\nDrawing Internal Right\n")
            InternalRight = BoxFace('Int_Right', CornerPoint((0,0), 0, 1, 1),         #First corner, internal on both sides 
                              0, CornerPoint((ybox-d1-d2,0), 0, 1, 1), 
                              right_joint, CornerPoint((ybox-d1-d2, zbox_internal_walls), 0, 1, 1),
                              0, CornerPoint((0,zbox_internal_walls), 0, 1, 1),
                              left_joint, self.group, [xpos, ypos])        #Draw face just below previous drawings
            InternalRight.drawFaceWithHoles(self.n_slot_y, self.y_slot_size, d1 - thickness, self.z_joint, True)

            self.UpdateBoundingBox(InternalRight)    #Now update bounding box
        elif self.InternalWalls_FB:
            #Udate bounding box with front and back value
            self.UpdateBoundingBox(InternalFront)    #Now update bounding box


        #Then internal walls
        #Columns first
        xpos = 0
        ypos = -self.BoundingBox[3]

        for i in range(self.n_slot_x-1):
            self.drawColumWall(i, self.n_slot_y, self.y_slot_size, self.ListNotchColumns, ybox-2*thickness, zbox_internal_walls, xpos, ypos, self.group) 
            xpos -= ybox + 2                  #Next position for drawing

        #Then rows, nearly the same, but opening at the bottom edge        
        
        xpos = 0
        ypos -= zbox_internal_walls + thickness + 2
        for i in range(self.n_slot_y-1):
            self.drawRowWall(i, self.n_slot_x, self.x_slot_size, self.ListNotchRows, xbox-2*thickness, zbox_internal_walls, xpos, ypos, self.group) 
            xpos -= xbox + 2                  #Next position for drawing
        
        #
        if self.options.lid_type == 'SteelHinge' or self.options.lid_type == 'Coffin':
            #the elements of the hinge
            HingeNum = 0
            ypos = -self.BoundingBox[3] - 2
            xpos = -2
            for Hinge in self.HingeList:
                for i in range(5):
                    self.drawSteelHingeElement(HingeNum*5+i, thickness, xpos, ypos, self.group)
                    xpos -= 3.5*thickness + 2
                HingeNum += 1
                xpos -= 3


        CloseDebugFile()


if __name__ == '__main__':
    GenericBox().run()