2022-09-29 23:16:09 +02:00
#! /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 < http : / / www . gnu . org / licenses / > .
'''
__version__ = " 1.0 " ### please report bugs, suggestions etc at https://github.com/paulh-rnd/TabbedBoxMaker ###
2022-12-06 22:37:33 +01:00
import os
import sys
import inkex
import gettext
import math
2022-09-29 23:16:09 +02:00
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 ' }
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 ( ' --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 nomTab < thickness :
inkex . errormsg ( _ ( ' Error: Tab size too small ' ) )
error = 1
if thickness == 0 :
inkex . errormsg ( _ ( ' Error: Thickness is zero ' ) )
error = 1
if thickness > min ( 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 < kerf :
inkex . errormsg ( _ ( ' Error: Spacing too small ' ) )
error = 1
if error : exit ( )
# For code spacing consistency, we use two-character abbreviations for the six box faces,
# where each abbreviation is the first and last letter of the face name:
# tp=top, bm=bottom, ft=front, bk=back, lt=left, rt=right
# Determine which faces the box has based on the box type
hasTp = hasBm = hasFt = hasBk = hasLt = hasRt = True
if boxtype == 2 : hasTp = False
elif boxtype == 3 : hasTp = hasFt = False
elif boxtype == 4 : hasTp = hasFt = hasRt = False
elif boxtype == 5 : hasTp = hasBm = False
elif boxtype == 6 : hasTp = hasFt = hasBk = hasRt = False
# else boxtype==1, full box, has all sides
# Determine where the tabs go based on the tab style
if tabSymmetry == 2 : # Antisymmetric (deprecated)
tpTabInfo = 0b0110
bmTabInfo = 0b1100
ltTabInfo = 0b1100
rtTabInfo = 0b0110
ftTabInfo = 0b1100
bkTabInfo = 0b1001
elif tabSymmetry == 1 : # Rotationally symmetric (Waffle-blocks)
tpTabInfo = 0b1111
bmTabInfo = 0b1111
ltTabInfo = 0b1111
rtTabInfo = 0b1111
ftTabInfo = 0b1111
bkTabInfo = 0b1111
else : # XY symmetric
tpTabInfo = 0b0000
bmTabInfo = 0b0000
ltTabInfo = 0b1111
rtTabInfo = 0b1111
ftTabInfo = 0b1010
bkTabInfo = 0b1010
def fixTabBits ( tabbed , tabInfo , bit ) :
newTabbed = tabbed & ~ bit
if inside :
newTabInfo = tabInfo | bit # set bit to 1 to use tab base line
else :
newTabInfo = tabInfo & ~ bit # set bit to 0 to use tab tip line
return newTabbed , newTabInfo
# Update the tab bits based on which sides of the box don't exist
tpTabbed = bmTabbed = ltTabbed = rtTabbed = ftTabbed = bkTabbed = 0b1111
if not hasTp :
bkTabbed , bkTabInfo = fixTabBits ( bkTabbed , bkTabInfo , 0b0010 )
ftTabbed , ftTabInfo = fixTabBits ( ftTabbed , ftTabInfo , 0b1000 )
ltTabbed , ltTabInfo = fixTabBits ( ltTabbed , ltTabInfo , 0b0001 )
rtTabbed , rtTabInfo = fixTabBits ( rtTabbed , rtTabInfo , 0b0100 )
tpTabbed = 0
if not hasBm :
bkTabbed , bkTabInfo = fixTabBits ( bkTabbed , bkTabInfo , 0b1000 )
ftTabbed , ftTabInfo = fixTabBits ( ftTabbed , ftTabInfo , 0b0010 )
ltTabbed , ltTabInfo = fixTabBits ( ltTabbed , ltTabInfo , 0b0100 )
rtTabbed , rtTabInfo = fixTabBits ( rtTabbed , rtTabInfo , 0b0001 )
bmTabbed = 0
if not hasFt :
tpTabbed , tpTabInfo = fixTabBits ( tpTabbed , tpTabInfo , 0b1000 )
bmTabbed , bmTabInfo = fixTabBits ( bmTabbed , bmTabInfo , 0b1000 )
ltTabbed , ltTabInfo = fixTabBits ( ltTabbed , ltTabInfo , 0b1000 )
rtTabbed , rtTabInfo = fixTabBits ( rtTabbed , rtTabInfo , 0b1000 )
ftTabbed = 0
if not hasBk :
tpTabbed , tpTabInfo = fixTabBits ( tpTabbed , tpTabInfo , 0b0010 )
bmTabbed , bmTabInfo = fixTabBits ( bmTabbed , bmTabInfo , 0b0010 )
ltTabbed , ltTabInfo = fixTabBits ( ltTabbed , ltTabInfo , 0b0010 )
rtTabbed , rtTabInfo = fixTabBits ( rtTabbed , rtTabInfo , 0b0010 )
bkTabbed = 0
if not hasLt :
tpTabbed , tpTabInfo = fixTabBits ( tpTabbed , tpTabInfo , 0b0100 )
bmTabbed , bmTabInfo = fixTabBits ( bmTabbed , bmTabInfo , 0b0001 )
bkTabbed , bkTabInfo = fixTabBits ( bkTabbed , bkTabInfo , 0b0001 )
ftTabbed , ftTabInfo = fixTabBits ( ftTabbed , ftTabInfo , 0b0001 )
ltTabbed = 0
if not hasRt :
tpTabbed , tpTabInfo = fixTabBits ( tpTabbed , tpTabInfo , 0b0001 )
bmTabbed , bmTabInfo = fixTabBits ( bmTabbed , bmTabInfo , 0b0100 )
bkTabbed , bkTabInfo = fixTabBits ( bkTabbed , bkTabInfo , 0b0100 )
ftTabbed , ftTabInfo = fixTabBits ( ftTabbed , ftTabInfo , 0b0100 )
rtTabbed = 0
# Layout positions are specified in a grid of rows and columns
row0 = ( 1 , 0 , 0 , 0 ) # top row
row1y = ( 2 , 0 , 1 , 0 ) # second row, offset by Y
row1z = ( 2 , 0 , 0 , 1 ) # second row, offset by Z
row2 = ( 3 , 0 , 1 , 1 ) # third row, always offset by Y+Z
col0 = ( 1 , 0 , 0 , 0 ) # left column
col1x = ( 2 , 1 , 0 , 0 ) # second column, offset by X
col1z = ( 2 , 0 , 0 , 1 ) # second column, offset by Z
col2xx = ( 3 , 2 , 0 , 0 ) # third column, offset by 2*X
col2xz = ( 3 , 1 , 0 , 1 ) # third column, offset by X+Z
col3xzz = ( 4 , 1 , 0 , 2 ) # fourth column, offset by X+2*Z
col3xxz = ( 4 , 2 , 0 , 1 ) # fourth column, offset by 2*X+Z
col4 = ( 5 , 2 , 0 , 2 ) # fifth column, always offset by 2*X+2*Z
col5 = ( 6 , 3 , 0 , 2 ) # sixth column, always offset by 3*X+2*Z
# layout format:(rootx),(rooty),Xlength,Ylength,tabInfo,tabbed,pieceType
# root= (spacing,X,Y,Z) * values in tuple
# tabInfo= <abcd> 0=holes 1=tabs
# tabbed= <abcd> 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 ( )