diff --git a/extensions/fablabchemnitz/box_maker_t_slot/box_maker_t_slot.inx b/extensions/fablabchemnitz/box_maker_t_slot/box_maker_t_slot.inx new file mode 100644 index 00000000..2ec441f8 --- /dev/null +++ b/extensions/fablabchemnitz/box_maker_t_slot/box_maker_t_slot.inx @@ -0,0 +1,43 @@ + + + Box Maker - T-Slot + fablabchemnitz.de.box_maker_t_slot + + + + + + + + + + 50.0 + 50.0 + 50.0 + 10.0 + + + + + 6.0 + 0.0 + 0.05 + + + + + + + 5.0 + 12 + 3 + + all + + + + + + \ No newline at end of file diff --git a/extensions/fablabchemnitz/box_maker_t_slot/box_maker_t_slot.py b/extensions/fablabchemnitz/box_maker_t_slot/box_maker_t_slot.py new file mode 100644 index 00000000..91582bc8 --- /dev/null +++ b/extensions/fablabchemnitz/box_maker_t_slot/box_maker_t_slot.py @@ -0,0 +1,321 @@ +#! /usr/bin/env python3 +''' +Generates Inkscape SVG file containing box components needed to +laser cut a tabbed construction box taking kerf and clearance into account + +Copyright (C) 2011 elliot white elliot@twot.eu +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__ = "0.8" ### please report bugs, suggestions etc to bugs@twot.eu ### + +from ink_helper import * +from lxml import etree + +def drill(center, diameter, n_pt): + from math import sin, cos, pi + center = Vec2(center) + radius = diameter / 2. + out = Vec2([1, 0]) + up = Vec2([0, 1]) + path = Path([center + out * radius]) + dtheta = (2 * pi) / n_pt + for i in range(n_pt + 1): + path.append(center + out * radius * cos(i * dtheta) + up * radius * sin(i * dtheta)) + return path + +def t_slot(center, orient, screw_diameter, nut_diameter): + ''' + make one t-slot starting + __ + | | + -----------+ +-----+ ------ + | ^ + x center | screw_diameter x----------------------> orient + | v + -----------+ +-----+ ------ + | | + -- + ''' + orient = Vec2(orient) + out = orient / orient.norm() + up = Vec2([out[1], -out[0]]) + center = Vec2(center) + screw_r = screw_diameter / 2. + nut_r = nut_diameter / 2. + nut_w = screw_diameter + path = Path([center + up * screw_r]) + path.append_from_last(orient) + path.append_from_last(up * (nut_r - screw_r)) + path.append_from_last(out * (nut_w)) + path.append_from_last(-up * (nut_r - screw_r)) + path.append_from_last(out * (screw_length - thickness - orient.norm() - nut_w)) + path.append_from_last(-up * screw_r) + path.extend(path.reflect(center, up).reverse()) + return path + +def t_slots(rx, ry, sox, soy, eox, eoy, tabVec, length, dirx, diry, isTab, do_holes): + # root startOffset endOffset tabVec length direction isTab + + divs=int(length/nomTab) # divisions + if not divs%2: divs-=1 # make divs odd + divs=float(divs) + tabs=(divs-1)/2 # tabs for side + + if equalTabs: + gapWidth=tabWidth=length/divs + else: + tabWidth=nomTab + gapWidth=(length-tabs*nomTab)/(divs-tabs) + + if isTab: # kerf correction + gapWidth-=correction + tabWidth+=correction + first=correction/2 + else: + gapWidth+=correction + tabWidth-=correction + first=-correction/2 + + s=[] + firstVec=0; secondVec=tabVec + dirxN=0 if dirx else 1 # used to select operation on x or y + diryN=0 if diry else 1 + (Vx,Vy)=(rx+sox*thickness,ry+soy*thickness) + nut_diameter = 2 * screw_diameter + + step = Vec2([dirx * (tabWidth + gapWidth + firstVec * 2), diry * (tabWidth + gapWidth + firstVec * 2)]) + orient = Vec2([-diry * (screw_length - thickness - screw_diameter), dirx * (screw_length - thickness - screw_diameter)]) + center = Vec2(Vx + dirx * (gapWidth + tabWidth/2.), Vy + diry * (gapWidth + tabWidth/2.)) + (orient / orient.norm()) * thickness + slot = t_slot(center, orient, screw_diameter, nut_diameter) + hole = drill(center - (orient / orient.norm()) * (thickness * 1.5 + spacing), screw_diameter, 360) + + slots = [] + holes = [] + for i in range(0, int(divs / 2), 1): + slots.append(slot.translate(step * i)) + if do_holes: + holes.append(hole.translate(step * i)) + holes.append(hole.translate(step * i - orient / orient.norm() * (Z - thickness) )) + + out = [s.drawXY() for s in slots] + out.extend([h.drawXY() for h in holes]) + return out + +def side(rx, ry, sox, soy, eox, eoy, tabVec, length, dirx, diry, isTab): + # root startOffset endOffset tabVec length direction isTab + + divs=int(length/nomTab) # divisions + if not divs%2: divs-=1 # make divs odd + divs=float(divs) + tabs=(divs-1)/2 # tabs for side + + if equalTabs: + gapWidth=tabWidth=length/divs + else: + tabWidth=nomTab + gapWidth=(length-tabs*nomTab)/(divs-tabs) + + if isTab: # kerf correction + gapWidth-=correction + tabWidth+=correction + first=correction/2 + else: + gapWidth+=correction + tabWidth-=correction + first=-correction/2 + + firstVec=0; secondVec=tabVec + dirxN=0 if dirx else 1 # used to select operation on x or y + diryN=0 if diry else 1 + (Vx,Vy)=(rx+sox*thickness,ry+soy*thickness) + s='M '+str(Vx)+','+str(Vy)+' ' + + if dirxN: Vy=ry # set correct line start + if diryN: Vx=rx + + # 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 n in range(1,int(divs)): + if n%2: + Vx=Vx+dirx*gapWidth+dirxN*firstVec+first*dirx + Vy=Vy+diry*gapWidth+diryN*firstVec+first*diry + s+='L '+str(Vx)+','+str(Vy)+' ' + Vx=Vx+dirxN*secondVec + Vy=Vy+diryN*secondVec + s+='L '+str(Vx)+','+str(Vy)+' ' + else: + Vx=Vx+dirx*tabWidth+dirxN*firstVec + Vy=Vy+diry*tabWidth+diryN*firstVec + s+='L '+str(Vx)+','+str(Vy)+' ' + Vx=Vx+dirxN*secondVec + Vy=Vy+diryN*secondVec + s+='L '+str(Vx)+','+str(Vy)+' ' + + (secondVec,firstVec)=(-secondVec,-firstVec) # swap tab direction + first=0 + s+='L '+str(rx+eox*thickness+dirx*length)+','+str(ry+eoy*thickness+diry*length)+' ' + return s + + +class TSlotBoxMaker(inkex.Effect): + + def add_arguments(self, pars): + 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('--equal',type=int, default=0, help='Equal/Prop Tabs') + 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('--screw_length',type=float, default=25, help='Screw Length') + pars.add_argument('--screw_diameter',type=float, default=25, help='Screw Diameter') + + def effect(self): + global parent,nomTab,equalTabs,thickness,correction, screw_length, screw_diameter, spacing, Z + + # Get access to main SVG document element and get its dimensions. + svg = self.document.getroot() + + # Get the attibutes: + widthDoc = self.svg.unittouu(svg.get('width')) + heightDoc = self.svg.unittouu(svg.get('height')) + + # Create a new layer. + layer = etree.SubElement(svg, 'g') + layer.set(inkex.addNS('label', 'inkscape'), 'T-Slot Box') + layer.set(inkex.addNS('groupmode', 'inkscape'), 'layer') + + parent=self.svg.get_current_layer() + + # Get script's option values. + unit=self.options.unit + inside=self.options.inside + X = self.svg.unittouu( str(self.options.length) + unit ) + Y = self.svg.unittouu( str(self.options.width) + unit ) + Z = self.svg.unittouu( str(self.options.depth) + unit ) + thickness = self.svg.unittouu( str(self.options.thickness) + unit ) + nomTab = self.svg.unittouu( str(self.options.tab) + unit ) + equalTabs=self.options.equal + kerf = self.svg.unittouu( str(self.options.kerf) + unit ) + clearance = self.svg.unittouu( str(self.options.clearance) + unit ) + layout=self.options.style + spacing = self.svg.unittouu( str(self.options.spacing) + unit ) + screw_length = self.svg.unittouu( str(self.options.screw_length) + unit ) + screw_diameter = self.svg.unittouu( str(self.options.screw_diameter) + unit ) + + if inside: # if inside dimension selected correct values to outside dimension + X+=thickness*2 + Y+=thickness*2 + Z+=thickness*2 + + correction=kerf-clearance + + # check input values mainly to avoid python errors + # TODO restrict values to *correct* solutions + 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 correction>min(X,Y,Z)/3: # crude test + inkex.errormsg(_('Error: Kerf/Clearence 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 + if layout==1: # Diagramatic Layout + pieces=[[(2,0,0,1),(3,0,1,1),X,Z,0b1010, False, False], + [(1,0,0,0),(2,0,0,1),Z,Y,0b1111, False, False], + [(2,0,0,1),(2,0,0,1),X,Y,0b0000, True, True], + [(3,1,0,1),(2,0,0,1),Z,Y,0b1111, False, False], + [(4,1,0,2),(2,0,0,1),X,Y,0b0000, True, False], + [(2,0,0,1),(1,0,0,0),X,Z,0b1010, False, False]] + elif layout==2: # 3 Piece Layout + pieces=[[(2,0,0,1),(2,0,1,0),X,Z,0b1010, False, False], + [(1,0,0,0),(1,0,0,0),Z,Y,0b1111, False, False], + [(2,0,0,1),(1,0,0,0),X,Y,0b0000, True, False]] + elif layout==3: # Inline(compact) Layout + pieces=[[(1,0,0,0),(1,0,0,0),X,Y,0b0000, False, False], + [(2,1,0,0),(1,0,0,0),X,Y,0b0000, False, False], + [(3,2,0,0),(1,0,0,0),Z,Y,0b0101, True, True], + [(4,2,0,1),(1,0,0,0),Z,Y,0b0101, False, False], + [(5,2,0,2),(1,0,0,0),X,Z,0b1111, True, False], + [(6,3,0,2),(1,0,0,0),X,Z,0b1111, False, False]] + elif layout==4: # Diagramatic Layout with Alternate Tab Arrangement + pieces=[[(2,0,0,1),(3,0,1,1),X,Z,0b1001, False, False], + [(1,0,0,0),(2,0,0,1),Z,Y,0b1100, False, False], + [(2,0,0,1),(2,0,0,1),X,Y,0b1100, True, False], + [(3,1,0,1),(2,0,0,1),Z,Y,0b0110, False, True], + [(4,1,0,2),(2,0,0,1),X,Y,0b0110, True, False], + [(2,0,0,1),(1,0,0,0),X,Z,0b1100, False, False]] + + for piece in 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 # root x co-ord for piece + y=ys*spacing+yx*X+yy*Y+yz*Z # root y co-ord for piece + dx=piece[2] + dy=piece[3] + tabs=piece[4] + slots = piece[5] + holes = piece[6] + a=tabs>>3&1; b=tabs>>2&1; c=tabs>>1&1; d=tabs&1 # extract tab status for each side + # generate and draw the sides of each piece + drawS(side(x , y , d, a, -b, a, -thickness if a else thickness, dx, 1, 0, a), layer) # side a + drawS(side(x+dx, y , -b, a, -b, -c, thickness if b else -thickness, dy, 0, 1, b), layer) # side b + drawS(side(x+dx, y+dy, -b, -c, d, -c, thickness if c else -thickness, dx, -1, 0, c), layer) # side c + drawS(side(x , y+dy, d, -c, d, a, -thickness if d else thickness, dy, 0, -1, d), layer) # side d + + # side((rx,ry),(sox,soy),(eox,eoy),tabVec,length,(dirx,diry),isTab): + # root startOffset endOffset tabVec length direction isTab + + if slots: + [drawS(slot, layer) for slot in t_slots(x , y , d, a, -b, a, -thickness if a else thickness, dx, 1, 0, a, holes)] # slot a + [drawS(slot, layer) for slot in t_slots(x+dx, y , -b, a, -b, -c, thickness if b else -thickness, dy, 0, 1, b, holes)] # slot b + [drawS(slot, layer) for slot in t_slots(x+dx, y+dy , -b, -c, d, -c, thickness if c else -thickness, dx, -1, 0, c, holes)] # slot c + [drawS(slot, layer) for slot in t_slots(x , y+dy , d, -c, d, a, -thickness if d else thickness, dy, 0, -1, d, holes)] # slot d + +# Create effect instance and apply it. +TSlotBoxMaker().run() diff --git a/extensions/fablabchemnitz/box_maker_t_slot/ink_helper.py b/extensions/fablabchemnitz/box_maker_t_slot/ink_helper.py new file mode 100644 index 00000000..9be0db04 --- /dev/null +++ b/extensions/fablabchemnitz/box_maker_t_slot/ink_helper.py @@ -0,0 +1,161 @@ +import math +import inkex +from lxml import etree + +def drawS(XYstring, parent): # Draw lines from a list + name='part' + style = { 'stroke': '#000000', 'stroke-width':'0.26458333', 'fill': 'none' } + drw = {'style':str(inkex.Style(style)),inkex.addNS('label','inkscape'):name,'d':XYstring} + etree.SubElement(parent, inkex.addNS('path','svg'), drw ) + return + +class Vec2: + + def __init__(self, x, y=None): + if y is None: + y = x[1] + x = x[0] + self.x = x + self.y = y + + def norm(self): + return math.sqrt(self.x ** 2 + self.y ** 2) + + def __getitem__(self, idx): + return [self.x, self.y][idx] + + def __neg__(self): + return Vec2(-self.x, -self.y) + + def __add__(self, other): + return Vec2(self.x + other[0], self.y + other[1]) + + def __sub__(self, other): + return self + [-other[0], -other[1]] + + def __mul__(self, scalar): + return Vec2(self.x * scalar, self.y * scalar) + + def __truediv__(self, scalar): + return Vec2(self.x / scalar, self.y / scalar) + + def dot(self, other): + return self.x * other[0] + self.y * other[1] + + def inner(self, other): + return self.dot(other) + + def outer(self, other): + return [[self[0] * other[0], self[0] * other[1]], + [self[1] * other[0], self[1] * other[1]]] + + def __repr__(self): + return 'Vec2(%s, %s)' % (self.x, self.y) + + def toXY(self): + return '%s,%s ' % (self.x, self.y) + +def mat_x_vec(mat, vec): + return Vec2(vec.dot(mat[0]), vec.dot(mat[1])) + +def sign(x): + return 1 if x > 0 else -1 + +class Path: + ''' + a list of Vec2 points + ''' + def __init__(self, path=()): + self.path = [Vec2(p) for p in path] + + def append(self, point): + self.path.append(Vec2(point)) + + def rotate(self, center, angle): + ''' + angle in degrees + ''' + from math import cos, sin + angle *= math.pi / 180. + R = [[cos(angle), -sin(angle)], + [sin(angle), cos(angle)]] + out = [mat_x_vec(R, p - center) + center for p in self.path] + return Path(out) + + def translate(self, vec): + return Path([p + vec for p in self.path]) + + def append_from_last(self, v): + self.path.append(self.path[-1] + v) + + def extend(self, points): + self.path.extend(points) + + def __getitem__(self, idx): + return self.path[idx] + + def reflect(self, center, orient): + out = self.translate(-center) + R = Vec2(orient).outer(orient) + R = [[1 - 2 * R[0][0], 2 * R[0][1]], + [2 * R[1][0], 1 - 2 * R[1][1]]] + out = Path([mat_x_vec(R, p) for p in out]) + out = out.translate(center) + return out + + def reverse(self): + return Path(self.path[::-1]) + + def drawXY(self): + XYstring = 'M ' + 'L '.join([p.toXY() for p in self.path]) + return XYstring + + def plot(self, lt='-'): + from pylab import plot + xs = [l.x for l in self.path] + ys = [l.y for l in self.path] + plot(xs, ys, lt) + + +def Vec2__test__(): + v1 = Vec2(1, 1) + assert abs(v1.norm() - math.sqrt(2)) < 1e-8 + assert abs(-v1[0] + 1) < 1e-8 + assert abs((v1 + v1)[0] - 2) < 1e-8 + assert abs((v1 - v1)[0] - 0) < 1e-8 + assert abs((v1 + [1, 2]).x - 2) < 1e-8 + assert abs((v1 - [1, 2]).x - 0) < 1e-8 + assert (v1.dot(v1) - v1.norm() ** 2) < 1e-8 +Vec2__test__() + +if __name__ == '__main__': + from pylab import plot, figure, clf, show, axis + from numpy import array + mm = 1. + center = [0, 30 * mm] + orient = [12. * mm, 0] + screw_diameter = 3 * mm + nut_diameter = 5.5 * mm + nut_w = 2.5 * mm + screw_length = 16 * mm + thickness = 6 * mm + + orient = Vec2(orient) + out = orient / math.sqrt(orient[0] ** 2 + orient[1] ** 2) + up = Vec2([-out[1], out[0]]) + center = Vec2(center) + screw_r = screw_diameter / 2. + nut_r = nut_diameter / 2. + path = Path([center + up * screw_r]) + path.append_from_last(orient) + path.append_from_last(up * (nut_r - screw_r)) + path.append_from_last(out * (nut_w)) + path.append_from_last(-up * (nut_r - screw_r)) + path.append_from_last(out * (screw_length - thickness)) + path.append_from_last(-up * screw_r) + rest = path.reflect(center, up).reverse() + path.extend(rest) + path.plot() + rest.plot('o-') + axis('equal') + show() \ No newline at end of file diff --git a/extensions/fablabchemnitz/box_maker_t_slot/meta.json b/extensions/fablabchemnitz/box_maker_t_slot/meta.json new file mode 100644 index 00000000..a2448355 --- /dev/null +++ b/extensions/fablabchemnitz/box_maker_t_slot/meta.json @@ -0,0 +1,20 @@ +[ + { + "name": "Box Maker - T-Slot", + "id": "fablabchemnitz.de.box_maker_t_slot", + "path": "box_maker_t_slot", + "original_name": "T-Slot Box Maker", + "original_id": "eu.twot.render.my_boxmaker", + "license": "GNU GPL v3", + "license_url": "https://github.com/kchimbo/inkscape_tslot_boxmaker/blob/master/LICENSE.md", + "comment": "", + "source_url": "https://gitea.fablabchemnitz.de/FabLab_Chemnitz/mightyscape-1.X/src/branch/master/extensions/fablabchemnitz/box_maker_t_slot", + "fork_url": "https://github.com/kchimbo/inkscape_tslot_boxmaker", + "documentation_url": "https://stadtfabrikanten.org/display/IFM/Box+Maker+-+T-Slot", + "inkscape_gallery_url": null, + "main_authors": [ + "github.com/kchimbo", + "github.com/vmario89" + ] + } +] \ No newline at end of file diff --git a/extensions/fablabchemnitz/frame_animation_sequence/meta.json b/extensions/fablabchemnitz/frame_animation_sequence/meta.json index ef385237..5ae34bbd 100644 --- a/extensions/fablabchemnitz/frame_animation_sequence/meta.json +++ b/extensions/fablabchemnitz/frame_animation_sequence/meta.json @@ -13,7 +13,7 @@ "documentation_url": "https://stadtfabrikanten.org/display/IFM/Frame+Animation+Sequence", "inkscape_gallery_url": null, "main_authors": [ - "https://github.com/yttiktak", + "github.com/yttiktak", "github.com/vmario89" ] }