#! /usr/bin/env python3 ''' Generates Inkscape SVG file containing box components needed to CNC (laser/mill) cut a box with tabbed joints taking kerf and clearance into account Original Tabbed Box Maker Copyright (C) 2011 elliot white Changelog: 19/12/2014 Paul Hutchison: - Ability to generate 6, 5, 4, 3 or 2-panel cutouts - Ability to also generate evenly spaced dividers within the box including tabbed joints to box sides and slots to slot into each other 23/06/2015 by Paul Hutchison: - Updated for Inkscape's 0.91 breaking change (unittouu) v0.93 - 15/8/2016 by Paul Hutchison: - Added Hairline option and fixed open box height bug v0.94 - 05/01/2017 by Paul Hutchison: - Added option for keying dividers into walls/floor/none v0.95 - 2017-04-20 by Jim McBeath - Added optional dimples v0.96 - 2017-04-24 by Jim McBeath - Refactored to make box type, tab style, and layout all orthogonal - Added Tab Style option to allow creating waffle-block-style tabs - Made open box size correct based on inner or outer dimension choice - Fixed a few tab bugs v0.99 - 2020-06-01 by Paul Hutchison - Preparatory release with Inkscape 1.0 compatibility upgrades (further fixes to come!) - Removed Antisymmetric option as it's broken, kinda pointless and looks weird - Fixed divider issues with Rotate Symmetric - Made individual panels and their keyholes/slots grouped v1.0 - 2020-06-17 by Paul Hutchison - Removed clearance parameter, as this was just subtracted from kerf - pointless? - Corrected kerf adjustments for overall box size and divider keyholes - Added dogbone cuts: CNC mills now supported! - Fix for floor/ceiling divider key issue (#17) - Increased max dividers to 20 (#35) v1.1 - 2021-08-09 by Paul Hutchison - Fixed for current Inkscape release version 1.1 - thanks to PR from https://github.com/roastedneutrons This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . ''' __version__ = "1.0" ### please report bugs, suggestions etc at https://github.com/paulh-rnd/TabbedBoxMaker ### import os,sys,inkex,simplestyle,gettext,math from copy import deepcopy _ = gettext.gettext linethickness = 1 # default unless overridden by settings def log(text): if 'SCHROFF_LOG' in os.environ: f = open(os.environ.get('SCHROFF_LOG'), 'a') f.write(text + "\n") def newGroup(canvas): # Create a new group and add element created from line string panelId = canvas.svg.get_unique_id('panel') group = canvas.svg.get_current_layer().add(inkex.Group(id=panelId)) return group def getLine(XYstring): line = inkex.PathElement() line.style = { 'stroke': '#000000', 'stroke-width' : str(linethickness), 'fill': 'none' } line.path = XYstring #inkex.etree.SubElement(parent, inkex.addNS('path','svg'), drw) return line # jslee - shamelessly adapted from sample code on below Inkscape wiki page 2015-07-28 # http://wiki.inkscape.org/wiki/index.php/Generating_objects_from_extensions def getCircle(r, c): (cx, cy) = c log("putting circle at (%d,%d)" % (cx,cy)) circle = inkex.PathElement.arc((cx, cy), r) circle.style = { 'stroke': '#000000', 'stroke-width': str(linethickness), 'fill': 'none' } # ell_attribs = {'style':simplestyle.formatStyle(style), # inkex.addNS('cx','sodipodi') :str(cx), # inkex.addNS('cy','sodipodi') :str(cy), # inkex.addNS('rx','sodipodi') :str(r), # inkex.addNS('ry','sodipodi') :str(r), # inkex.addNS('start','sodipodi') :str(0), # inkex.addNS('end','sodipodi') :str(2*math.pi), # inkex.addNS('open','sodipodi') :'true', #all ellipse sectors we will draw are open # inkex.addNS('type','sodipodi') :'arc', # 'transform' :'' } #inkex.etree.SubElement(parent, inkex.addNS('path','svg'), ell_attribs ) return circle def dimpleStr(tabVector,vectorX,vectorY,dirX,dirY,dirxN,diryN,ddir,isTab): ds='' if not isTab: ddir = -ddir if dimpleHeight>0 and tabVector!=0: if tabVector>0: dimpleStart=(tabVector-dimpleLength)/2-dimpleHeight tabSgn=1 else: dimpleStart=(tabVector+dimpleLength)/2+dimpleHeight tabSgn=-1 Vxd=vectorX+dirxN*dimpleStart Vyd=vectorY+diryN*dimpleStart ds+='L '+str(Vxd)+','+str(Vyd)+' ' Vxd=Vxd+(tabSgn*dirxN-ddir*dirX)*dimpleHeight Vyd=Vyd+(tabSgn*diryN-ddir*dirY)*dimpleHeight ds+='L '+str(Vxd)+','+str(Vyd)+' ' Vxd=Vxd+tabSgn*dirxN*dimpleLength Vyd=Vyd+tabSgn*diryN*dimpleLength ds+='L '+str(Vxd)+','+str(Vyd)+' ' Vxd=Vxd+(tabSgn*dirxN+ddir*dirX)*dimpleHeight Vyd=Vyd+(tabSgn*diryN+ddir*dirY)*dimpleHeight ds+='L '+str(Vxd)+','+str(Vyd)+' ' return ds def side(group,root,startOffset,endOffset,tabVec,length,direction,isTab,isDivider,numDividers,dividerSpacing): rootX, rootY = root startOffsetX, startOffsetY = startOffset endOffsetX, endOffsetY = endOffset dirX, dirY = direction notTab=0 if isTab else 1 if (tabSymmetry==1): # waffle-block style rotationally symmetric tabs divisions=int((length-2*thickness)/nomTab) if divisions%2: divisions+=1 # make divs even divisions=float(divisions) tabs=divisions/2 # tabs for side else: divisions=int(length/nomTab) if not divisions%2: divisions-=1 # make divs odd divisions=float(divisions) tabs=(divisions-1)/2 # tabs for side if (tabSymmetry==1): # waffle-block style rotationally symmetric tabs gapWidth=tabWidth=(length-2*thickness)/divisions elif equalTabs: gapWidth=tabWidth=length/divisions else: tabWidth=nomTab gapWidth=(length-tabs*nomTab)/(divisions-tabs) if isTab: # kerf correction gapWidth-=kerf tabWidth+=kerf first=halfkerf else: gapWidth+=kerf tabWidth-=kerf first=-halfkerf firstholelenX=0 firstholelenY=0 s=[] h=[] firstVec=0; secondVec=tabVec dividerEdgeOffsetX = dividerEdgeOffsetY = thickness notDirX=0 if dirX else 1 # used to select operation on x or y notDirY=0 if dirY else 1 if (tabSymmetry==1): dividerEdgeOffsetX = dirX*thickness; #dividerEdgeOffsetY = ; vectorX = rootX + (startOffsetX*thickness if notDirX else 0) vectorY = rootY + (startOffsetY*thickness if notDirY else 0) s='M '+str(vectorX)+','+str(vectorY)+' ' vectorX = rootX+(startOffsetX if startOffsetX else dirX)*thickness vectorY = rootY+(startOffsetY if startOffsetY else dirY)*thickness if notDirX: endOffsetX=0 if notDirY: endOffsetY=0 else: (vectorX,vectorY)=(rootX+startOffsetX*thickness,rootY+startOffsetY*thickness) dividerEdgeOffsetX=dirY*thickness dividerEdgeOffsetY=dirX*thickness s='M '+str(vectorX)+','+str(vectorY)+' ' if notDirX: vectorY=rootY # set correct line start for tab generation if notDirY: vectorX=rootX # generate line as tab or hole using: # last co-ord:Vx,Vy ; tab dir:tabVec ; direction:dirx,diry ; thickness:thickness # divisions:divs ; gap width:gapWidth ; tab width:tabWidth for tabDivision in range(1,int(divisions)): if ((tabDivision%2) ^ (not isTab)) and numDividers>0 and not isDivider: # draw holes for divider tabs to key into side walls w=gapWidth if isTab else tabWidth if tabDivision==1 and tabSymmetry==0: w-=startOffsetX*thickness holeLenX=dirX*w+notDirX*firstVec+first*dirX holeLenY=dirY*w+notDirY*firstVec+first*dirY if first: firstholelenX=holeLenX firstholelenY=holeLenY for dividerNumber in range(1,int(numDividers)+1): Dx=vectorX+-dirY*dividerSpacing*dividerNumber+notDirX*halfkerf+dirX*dogbone*halfkerf-dogbone*first*dirX Dy=vectorY+dirX*dividerSpacing*dividerNumber-notDirY*halfkerf+dirY*dogbone*halfkerf-dogbone*first*dirY if tabDivision==1 and tabSymmetry==0: Dx+=startOffsetX*thickness h='M '+str(Dx)+','+str(Dy)+' ' Dx=Dx+holeLenX Dy=Dy+holeLenY h+='L '+str(Dx)+','+str(Dy)+' ' Dx=Dx+notDirX*(secondVec-kerf) Dy=Dy+notDirY*(secondVec+kerf) h+='L '+str(Dx)+','+str(Dy)+' ' Dx=Dx-holeLenX Dy=Dy-holeLenY h+='L '+str(Dx)+','+str(Dy)+' ' Dx=Dx-notDirX*(secondVec-kerf) Dy=Dy-notDirY*(secondVec+kerf) h+='L '+str(Dx)+','+str(Dy)+' ' group.add(getLine(h)) if tabDivision%2: if tabDivision==1 and numDividers>0 and isDivider: # draw slots for dividers to slot into each other for dividerNumber in range(1,int(numDividers)+1): Dx=vectorX+-dirY*dividerSpacing*dividerNumber-dividerEdgeOffsetX+notDirX*halfkerf Dy=vectorY+dirX*dividerSpacing*dividerNumber-dividerEdgeOffsetY+notDirY*halfkerf h='M '+str(Dx)+','+str(Dy)+' ' Dx=Dx+dirX*(first+length/2) Dy=Dy+dirY*(first+length/2) h+='L '+str(Dx)+','+str(Dy)+' ' Dx=Dx+notDirX*(thickness-kerf) Dy=Dy+notDirY*(thickness-kerf) h+='L '+str(Dx)+','+str(Dy)+' ' Dx=Dx-dirX*(first+length/2) Dy=Dy-dirY*(first+length/2) h+='L '+str(Dx)+','+str(Dy)+' ' Dx=Dx-notDirX*(thickness-kerf) Dy=Dy-notDirY*(thickness-kerf) h+='L '+str(Dx)+','+str(Dy)+' ' group.add(getLine(h)) # draw the gap vectorX+=dirX*(gapWidth+(isTab&dogbone&1 ^ 0x1)*first+dogbone*kerf*isTab)+notDirX*firstVec vectorY+=dirY*(gapWidth+(isTab&dogbone&1 ^ 0x1)*first+dogbone*kerf*isTab)+notDirY*firstVec s+='L '+str(vectorX)+','+str(vectorY)+' ' if dogbone and isTab: vectorX-=dirX*halfkerf vectorY-=dirY*halfkerf s+='L '+str(vectorX)+','+str(vectorY)+' ' # draw the starting edge of the tab s+=dimpleStr(secondVec,vectorX,vectorY,dirX,dirY,notDirX,notDirY,1,isTab) vectorX+=notDirX*secondVec vectorY+=notDirY*secondVec s+='L '+str(vectorX)+','+str(vectorY)+' ' if dogbone and notTab: vectorX-=dirX*halfkerf vectorY-=dirY*halfkerf s+='L '+str(vectorX)+','+str(vectorY)+' ' else: # draw the tab vectorX+=dirX*(tabWidth+dogbone*kerf*notTab)+notDirX*firstVec vectorY+=dirY*(tabWidth+dogbone*kerf*notTab)+notDirY*firstVec s+='L '+str(vectorX)+','+str(vectorY)+' ' if dogbone and notTab: vectorX-=dirX*halfkerf vectorY-=dirY*halfkerf s+='L '+str(vectorX)+','+str(vectorY)+' ' # draw the ending edge of the tab s+=dimpleStr(secondVec,vectorX,vectorY,dirX,dirY,notDirX,notDirY,-1,isTab) vectorX+=notDirX*secondVec vectorY+=notDirY*secondVec s+='L '+str(vectorX)+','+str(vectorY)+' ' if dogbone and isTab: vectorX-=dirX*halfkerf vectorY-=dirY*halfkerf s+='L '+str(vectorX)+','+str(vectorY)+' ' (secondVec,firstVec)=(-secondVec,-firstVec) # swap tab direction first=0 #finish the line off s+='L '+str(rootX+endOffsetX*thickness+dirX*length)+','+str(rootY+endOffsetY*thickness+dirY*length)+' ' if isTab and numDividers>0 and tabSymmetry==0 and not isDivider: # draw last for divider joints in side walls for dividerNumber in range(1,int(numDividers)+1): Dx=vectorX+-dirY*dividerSpacing*dividerNumber+notDirX*halfkerf+dirX*dogbone*halfkerf-dogbone*first*dirX # Dy=vectorY+dirX*dividerSpacing*dividerNumber-notDirY*halfkerf+dirY*dogbone*halfkerf-dogbone*first*dirY # Dx=vectorX+-dirY*dividerSpacing*dividerNumber-dividerEdgeOffsetX+notDirX*halfkerf Dy=vectorY+dirX*dividerSpacing*dividerNumber-dividerEdgeOffsetY+notDirY*halfkerf h='M '+str(Dx)+','+str(Dy)+' ' Dx=Dx+firstholelenX Dy=Dy+firstholelenY h+='L '+str(Dx)+','+str(Dy)+' ' Dx=Dx+notDirX*(thickness-kerf) Dy=Dy+notDirY*(thickness-kerf) h+='L '+str(Dx)+','+str(Dy)+' ' Dx=Dx-firstholelenX Dy=Dy-firstholelenY h+='L '+str(Dx)+','+str(Dy)+' ' Dx=Dx-notDirX*(thickness-kerf) Dy=Dy-notDirY*(thickness-kerf) h+='L '+str(Dx)+','+str(Dy)+' ' group.add(getLine(h)) # for dividerNumber in range(1,int(numDividers)+1): # Dx=vectorX+-dirY*dividerSpacing*dividerNumber+notDirX*halfkerf+dirX*dogbone*halfkerf # Dy=vectorY+dirX*dividerSpacing*dividerNumber-notDirY*halfkerf+dirY*dogbone*halfkerf # # Dx=vectorX+dirX*dogbone*halfkerf # # Dy=vectorY+dirX*dividerSpacing*dividerNumber-dirX*halfkerf+dirY*dogbone*halfkerf # h='M '+str(Dx)+','+str(Dy)+' ' # Dx=rootX+endOffsetX*thickness+dirX*length # Dy+=dirY*tabWidth+notDirY*firstVec+first*dirY # h+='L '+str(Dx)+','+str(Dy)+' ' # Dx+=notDirX*(secondVec-kerf) # Dy+=notDirY*(secondVec+kerf) # h+='L '+str(Dx)+','+str(Dy)+' ' # Dx-=vectorX # Dy-=(dirY*tabWidth+notDirY*firstVec+first*dirY) # h+='L '+str(Dx)+','+str(Dy)+' ' # Dx-=notDirX*(secondVec-kerf) # Dy-=notDirY*(secondVec+kerf) # h+='L '+str(Dx)+','+str(Dy)+' ' # group.add(getLine(h)) group.add(getLine(s)) return s class BoxMaker(inkex.EffectExtension): def add_arguments(self, pars): pars.add_argument('--schroff',type=int,default=0,help='Enable Schroff mode') pars.add_argument('--rail_height',type=float,default=10.0,help='Height of rail') pars.add_argument('--rail_mount_depth',type=float,default=17.4,help='Depth at which to place hole for rail mount bolt') pars.add_argument('--rail_mount_centre_offset',type=float, default=0.0,help='How far toward row centreline to offset rail mount bolt (from rail centreline)') pars.add_argument('--rows',type=int,default=0,help='Number of Schroff rows') pars.add_argument('--hp',type=int,default=0,help='Width (TE/HP units) of Schroff rows') pars.add_argument('--row_spacing',type=float,default=10.0,help='Height of rail') pars.add_argument('--unit', default='mm',help='Measure Units') pars.add_argument('--inside',type=int,default=0,help='Int/Ext Dimension') pars.add_argument('--length',type=float,default=100,help='Length of Box') pars.add_argument('--width',type=float,default=100,help='Width of Box') pars.add_argument('--depth',type=float,default=100,help='Height of Box') pars.add_argument('--tab',type=float,default=25,help='Nominal Tab Width') pars.add_argument('--tabtype',type=int,default=0,help='Tab type: regular or dogbone') pars.add_argument('--equal',type=int,default=0,help='Equal/Prop Tabs') pars.add_argument('--tabsymmetry',type=int,default=0,help='Tab style') pars.add_argument('--dimpleheight',type=float,default=0,help='Tab Dimple Height') pars.add_argument('--dimplelength',type=float,default=0,help='Tab Dimple Tip Length') pars.add_argument('--hairline',type=int,default=0,help='Line Thickness') pars.add_argument('--thickness',type=float,default=10,help='Thickness of Material') pars.add_argument('--kerf',type=float,default=0.5,help='Kerf (width) of cut') pars.add_argument('--clearance',type=float,default=0.01,help='Clearance of joints') pars.add_argument('--style',type=int,default=25,help='Layout/Style') pars.add_argument('--spacing',type=float,default=25,help='Part Spacing') pars.add_argument('--boxtype',type=int,default=25,help='Box type') pars.add_argument('--div_l',type=int,default=25,help='Dividers (Length axis)') pars.add_argument('--div_w',type=int,default=25,help='Dividers (Width axis)') pars.add_argument('--keydiv',type=int,default=3,help='Key dividers into walls/floor') def effect(self): global group,nomTab,equalTabs,tabSymmetry,dimpleHeight,dimpleLength,thickness,kerf,halfkerf,dogbone,divx,divy,hairline,linethickness,keydivwalls,keydivfloor # Get access to main SVG document element and get its dimensions. svg = self.document.getroot() # Get the attributes: widthDoc = self.svg.unittouu(svg.get('width')) heightDoc = self.svg.unittouu(svg.get('height')) # Get script's option values. hairline=self.options.hairline unit=self.options.unit inside=self.options.inside schroff=self.options.schroff kerf = self.svg.unittouu( str(self.options.kerf) + unit ) halfkerf=kerf/2 # Set the line thickness if hairline: linethickness=self.svg.unittouu('0.002in') else: linethickness=1 if schroff: rows=self.options.rows rail_height=self.svg.unittouu(str(self.options.rail_height)+unit) row_centre_spacing=self.svg.unittouu(str(122.5)+unit) row_spacing=self.svg.unittouu(str(self.options.row_spacing)+unit) rail_mount_depth=self.svg.unittouu(str(self.options.rail_mount_depth)+unit) rail_mount_centre_offset=self.svg.unittouu(str(self.options.rail_mount_centre_offset)+unit) rail_mount_radius=self.svg.unittouu(str(2.5)+unit) ## minimally different behaviour for schroffmaker.inx vs. boxmaker.inx ## essentially schroffmaker.inx is just an alternate interface with different ## default settings, some options removed, and a tiny amount of extra logic if schroff: ## schroffmaker.inx X = self.svg.unittouu(str(self.options.hp * 5.08) + unit) # 122.5mm vertical distance between mounting hole centres of 3U Schroff panels row_height = rows * (row_centre_spacing + rail_height) # rail spacing in between rows but never between rows and case panels row_spacing_total = (rows - 1) * row_spacing Y = row_height + row_spacing_total else: ## boxmaker.inx X = self.svg.unittouu( str(self.options.length + kerf) + unit ) Y = self.svg.unittouu( str(self.options.width + kerf) + unit ) Z = self.svg.unittouu( str(self.options.depth + kerf) + unit ) thickness = self.svg.unittouu( str(self.options.thickness) + unit ) nomTab = self.svg.unittouu( str(self.options.tab) + unit ) equalTabs=self.options.equal tabSymmetry=self.options.tabsymmetry dimpleHeight=self.options.dimpleheight dimpleLength=self.options.dimplelength dogbone = 1 if self.options.tabtype == 1 else 0 layout=self.options.style spacing = self.svg.unittouu( str(self.options.spacing) + unit ) boxtype = self.options.boxtype divx = self.options.div_l divy = self.options.div_w keydivwalls = 0 if self.options.keydiv == 3 or self.options.keydiv == 1 else 1 keydivfloor = 0 if self.options.keydiv == 3 or self.options.keydiv == 2 else 1 initOffsetX=0 initOffsetY=0 if inside: # if inside dimension selected correct values to outside dimension X+=thickness*2 Y+=thickness*2 Z+=thickness*2 # check input values mainly to avoid python errors # TODO restrict values to *correct* solutions # TODO restrict divisions to logical values error=0 if min(X,Y,Z)==0: inkex.errormsg(_('Error: Dimensions must be non zero')) error=1 if max(X,Y,Z)>max(widthDoc,heightDoc)*10: # crude test inkex.errormsg(_('Error: Dimensions Too Large')) error=1 if min(X,Y,Z)<3*nomTab: inkex.errormsg(_('Error: Tab size too large')) error=1 if nomTabmin(X,Y,Z)/3: # crude test inkex.errormsg(_('Error: Material too thick')) error=1 if kerf>min(X,Y,Z)/3: # crude test inkex.errormsg(_('Error: Kerf too large')) error=1 if spacing>max(X,Y,Z)*10: # crude test inkex.errormsg(_('Error: Spacing too large')) error=1 if spacing 0=holes 1=tabs # tabbed= 0=no tabs 1=tabs on this side # (sides: a=top, b=right, c=bottom, d=left) # pieceType: 1=XY, 2=XZ, 3=ZY tpFace=1 bmFace=1 ftFace=2 bkFace=2 ltFace=3 rtFace=3 def reduceOffsets(aa, start, dx, dy, dz): for ix in range(start+1,len(aa)): (s,x,y,z) = aa[ix] aa[ix] = (s-1, x-dx, y-dy, z-dz) # note first two pieces in each set are the X-divider template and Y-divider template respectively pieces=[] if layout==1: # Diagramatic Layout rr = deepcopy([row0, row1z, row2]) cc = deepcopy([col0, col1z, col2xz, col3xzz]) if not hasFt: reduceOffsets(rr, 0, 0, 0, 1) # remove row0, shift others up by Z if not hasLt: reduceOffsets(cc, 0, 0, 0, 1) if not hasRt: reduceOffsets(cc, 2, 0, 0, 1) if hasBk: pieces.append([cc[1], rr[2], X,Z, bkTabInfo, bkTabbed, bkFace]) if hasLt: pieces.append([cc[0], rr[1], Z,Y, ltTabInfo, ltTabbed, ltFace]) if hasBm: pieces.append([cc[1], rr[1], X,Y, bmTabInfo, bmTabbed, bmFace]) if hasRt: pieces.append([cc[2], rr[1], Z,Y, rtTabInfo, rtTabbed, rtFace]) if hasTp: pieces.append([cc[3], rr[1], X,Y, tpTabInfo, tpTabbed, tpFace]) if hasFt: pieces.append([cc[1], rr[0], X,Z, ftTabInfo, ftTabbed, ftFace]) elif layout==2: # 3 Piece Layout rr = deepcopy([row0, row1y]) cc = deepcopy([col0, col1z]) if hasBk: pieces.append([cc[1], rr[1], X,Z, bkTabInfo, bkTabbed, bkFace]) if hasLt: pieces.append([cc[0], rr[0], Z,Y, ltTabInfo, ltTabbed, ltFace]) if hasBm: pieces.append([cc[1], rr[0], X,Y, bmTabInfo, bmTabbed, bmFace]) elif layout==3: # Inline(compact) Layout rr = deepcopy([row0]) cc = deepcopy([col0, col1x, col2xx, col3xxz, col4, col5]) if not hasTp: reduceOffsets(cc, 0, 1, 0, 0) # remove col0, shift others left by X if not hasBm: reduceOffsets(cc, 1, 1, 0, 0) if not hasLt: reduceOffsets(cc, 2, 0, 0, 1) if not hasRt: reduceOffsets(cc, 3, 0, 0, 1) if not hasBk: reduceOffsets(cc, 4, 1, 0, 0) if hasBk: pieces.append([cc[4], rr[0], X,Z, bkTabInfo, bkTabbed, bkFace]) if hasLt: pieces.append([cc[2], rr[0], Z,Y, ltTabInfo, ltTabbed, ltFace]) if hasTp: pieces.append([cc[0], rr[0], X,Y, tpTabInfo, tpTabbed, tpFace]) if hasBm: pieces.append([cc[1], rr[0], X,Y, bmTabInfo, bmTabbed, bmFace]) if hasRt: pieces.append([cc[3], rr[0], Z,Y, rtTabInfo, rtTabbed, rtFace]) if hasFt: pieces.append([cc[5], rr[0], X,Z, ftTabInfo, ftTabbed, ftFace]) for idx, piece in enumerate(pieces): # generate and draw each piece of the box (xs,xx,xy,xz)=piece[0] (ys,yx,yy,yz)=piece[1] x=xs*spacing+xx*X+xy*Y+xz*Z+initOffsetX # root x co-ord for piece y=ys*spacing+yx*X+yy*Y+yz*Z+initOffsetY # root y co-ord for piece dx=piece[2] dy=piece[3] tabs=piece[4] a=tabs>>3&1; b=tabs>>2&1; c=tabs>>1&1; d=tabs&1 # extract tab status for each side tabbed=piece[5] atabs=tabbed>>3&1; btabs=tabbed>>2&1; ctabs=tabbed>>1&1; dtabs=tabbed&1 # extract tabbed flag for each side xspacing=(X-thickness)/(divy+1) yspacing=(Y-thickness)/(divx+1) xholes = 1 if piece[6]<3 else 0 yholes = 1 if piece[6]!=2 else 0 wall = 1 if piece[6]>1 else 0 floor = 1 if piece[6]==1 else 0 railholes = 1 if piece[6]==3 else 0 group = newGroup(self) if schroff and railholes: log("rail holes enabled on piece %d at (%d, %d)" % (idx, x+thickness,y+thickness)) log("abcd = (%d,%d,%d,%d)" % (a,b,c,d)) log("dxdy = (%d,%d)" % (dx,dy)) rhxoffset = rail_mount_depth + thickness if idx == 1: rhx=x+rhxoffset elif idx == 3: rhx=x-rhxoffset+dx else: rhx=0 log("rhxoffset = %d, rhx= %d" % (rhxoffset, rhx)) rystart=y+(rail_height/2)+thickness if rows == 1: log("just one row this time, rystart = %d" % rystart) rh1y=rystart+rail_mount_centre_offset rh2y=rh1y+(row_centre_spacing-rail_mount_centre_offset) group.add(getCircle(rail_mount_radius,(rhx,rh1y))) group.add(getCircle(rail_mount_radius,(rhx,rh2y))) else: for n in range(0,rows): log("drawing row %d, rystart = %d" % (n+1, rystart)) # if holes are offset (eg. Vector T-strut rails), they should be offset # toward each other, ie. toward the centreline of the Schroff row rh1y=rystart+rail_mount_centre_offset rh2y=rh1y+row_centre_spacing-rail_mount_centre_offset group.add(getCircle(rail_mount_radius,(rhx,rh1y))) group.add(getCircle(rail_mount_radius,(rhx,rh2y))) rystart+=row_centre_spacing+row_spacing+rail_height # generate and draw the sides of each piece side(group,(x,y),(d,a),(-b,a),atabs * (-thickness if a else thickness),dx,(1,0),a,0,(keydivfloor|wall) * (keydivwalls|floor) * divx*yholes*atabs,yspacing) # side a side(group,(x+dx,y),(-b,a),(-b,-c),btabs * (thickness if b else -thickness),dy,(0,1),b,0,(keydivfloor|wall) * (keydivwalls|floor) * divy*xholes*btabs,xspacing) # side b if atabs: side(group,(x+dx,y+dy),(-b,-c),(d,-c),ctabs * (thickness if c else -thickness),dx,(-1,0),c,0,0,0) # side c else: side(group,(x+dx,y+dy),(-b,-c),(d,-c),ctabs * (thickness if c else -thickness),dx,(-1,0),c,0,(keydivfloor|wall) * (keydivwalls|floor) * divx*yholes*ctabs,yspacing) # side c if btabs: side(group,(x,y+dy),(d,-c),(d,a),dtabs * (-thickness if d else thickness),dy,(0,-1),d,0,0,0) # side d else: side(group,(x,y+dy),(d,-c),(d,a),dtabs * (-thickness if d else thickness),dy,(0,-1),d,0,(keydivfloor|wall) * (keydivwalls|floor) * divy*xholes*dtabs,xspacing) # side d if idx==0: # remove tabs from dividers if not required if not keydivfloor: a=c=1 atabs=ctabs=0 if not keydivwalls: b=d=1 btabs=dtabs=0 y=4*spacing+1*Y+2*Z # root y co-ord for piece for n in range(0,divx): # generate X dividers group = newGroup(self) x=n*(spacing+X) # root x co-ord for piece side(group,(x,y),(d,a),(-b,a),keydivfloor*atabs*(-thickness if a else thickness),dx,(1,0),a,1,0,0) # side a side(group,(x+dx,y),(-b,a),(-b,-c),keydivwalls*btabs*(thickness if b else -thickness),dy,(0,1),b,1,divy*xholes,xspacing) # side b side(group,(x+dx,y+dy),(-b,-c),(d,-c),keydivfloor*ctabs*(thickness if c else -thickness),dx,(-1,0),c,1,0,0) # side c side(group,(x,y+dy),(d,-c),(d,a),keydivwalls*dtabs*(-thickness if d else thickness),dy,(0,-1),d,1,0,0) # side d elif idx==1: y=5*spacing+1*Y+3*Z # root y co-ord for piece for n in range(0,divy): # generate Y dividers group = newGroup(self) x=n*(spacing+Z) # root x co-ord for piece side(group,(x,y),(d,a),(-b,a),keydivwalls*atabs*(-thickness if a else thickness),dx,(1,0),a,1,divx*yholes,yspacing) # side a side(group,(x+dx,y),(-b,a),(-b,-c),keydivfloor*btabs*(thickness if b else -thickness),dy,(0,1),b,1,0,0) # side b side(group,(x+dx,y+dy),(-b,-c),(d,-c),keydivwalls*ctabs*(thickness if c else -thickness),dx,(-1,0),c,1,0,0) # side c side(group,(x,y+dy),(d,-c),(d,a),keydivfloor*dtabs*(-thickness if d else thickness),dy,(0,-1),d,1,0,0) # side d # Create effect instance and apply it. BoxMaker().run()