import FreeCAD from FreeCAD import Base, Vector, Matrix import Part class BoxCfg(object): def __init__(self): self._thickness = 10 self._notches = 10 self.outerDimmensions(100, 100, 100) self.calculate() @property def thickness(self): return self._thickness @thickness.setter def thickness(self, thickness): self._thickness = thickness self.calculate() @property def notches(self): return self._notches @notches.setter def notches(self, notches): self._notches = notches self.calculate() def outerDimmensions(self, width, height, depth): self.outerWidth = width self.outerHeight = height self.outerDepth = depth self.calculate() return self @property def notchDownWidth(self): return self._notchDownWidth def calculate(self): self.innerWidth = self.outerWidth - 2 * self._thickness self.innerHeight = self.outerHeight - 2 * self._thickness self.innerDepth = self.outerDepth - 2 * self._thickness self._notchDownWidth = self.innerWidth / (self._notches * 2 + 1) def prt(self): print("Box configuration") print("Outer w/h/d: " + str(self.outerWidth) + "/" + str(self.outerHeight) + "/" + str(self.outerDepth)) print("Inner w/h/d: " + str(self.innerWidth) + "/" + str(self.innerHeight) + "/" + str(self.innerDepth)) print("Notch count: " + str(self.notches) + ", low notch width: " + str(self.notchDownWidth)) class VGen(object): def __init__(self, current, dir): if dir == 'right': self.x_x = 1 self.x_y = 0 self.y_x = 0 self.y_y = 1 elif dir == 'left': self.x_x = -1 self.x_y = 0 self.y_x = 0 self.y_y = -1 elif dir == 'down': self.x_x = 0 self.x_y = -1 self.y_x = 1 self.y_y = 0 elif dir == 'up': self.x_x = 0 self.x_y = 1 self.y_x = -1 self.y_y = 0 else: raise Exception('Bad dir: ' + dir) self.current = current self.list = [] def move(self, x, y, z): self.current = self.current.add(Vector(x, y, z)) self.list.append(self.current) def moveX(self, value): self.move(self.x_x * value, self.x_y * value, 0) def moveY(self, value): self.move(self.y_x * value, self.y_y * value, 0) def line1(cfg, start, dir, inv, include_start): x = cfg.notchDownWidth if inv: inv = -1 else: inv = 1 g = VGen(start, dir) if include_start: g.moveX(cfg.thickness) g.moveX(x) for i in range(0, cfg.notches): g.moveY(-cfg.thickness * inv) g.moveX(x) g.moveY(cfg.thickness * inv) g.moveX(x) if include_start: g.moveX(cfg.thickness) return g.list def largePolygon(cfg): v = [Vector(0, 0, 0)] top = line1(cfg, v[-1], 'right', False, True); v.extend(top) left = line1(cfg, v[-1], 'down', False, True); v.extend(left) bottom = line1(cfg, v[-1], 'left', False, True); v.extend(bottom) right = line1(cfg, v[-1], 'up', False, True); v.extend(right) return v def mediumPolygon(cfg): v = [Vector(cfg.thickness, 0, 0)] top = line1(cfg, v[-1], 'right', False, False); v.extend(top) left = line1(cfg, v[-1], 'down', True, True); v.extend(left) bottom = line1(cfg, v[-1], 'left', False, False); v.extend(bottom) right = line1(cfg, v[-1], 'up', True, True); v.extend(right) return v def smallPolygon(cfg): v = [Vector(cfg.thickness, 0, 0)] top = line1(cfg, v[-1], 'right', True, False); v.extend(top) left = line1(cfg, v[-1], 'down', True, False); v.extend(left) bottom = line1(cfg, v[-1], 'left', True, False); v.extend(bottom) right = line1(cfg, v[-1], 'up', True, False); v.extend(right) return v def makeBox(doc, cfg): # front back left # right top bottom # TODO: replace m.move() with a proper translation matrix so it looks assembled m = Matrix() m.move(cfg.outerWidth * -0.55, 0, 0) s = Part.makePolygon(largePolygon(cfg)) s.transformShape(m) doc.addObject("Part::Feature", "Front").Shape = s m = Matrix() m.move(cfg.outerWidth * 0.55, 0, 0) s = Part.makePolygon(largePolygon(cfg)) s.transformShape(m) doc.addObject("Part::Feature", "Back").Shape = s m = Matrix() m.move(cfg.outerWidth * 1.60, 0, 0) l = Part.makePolygon(mediumPolygon(cfg)) l.transformShape(m) doc.addObject("Part::Feature", "Left").Shape = l m = Matrix() m.move(cfg.outerWidth * -0.55, cfg.outerWidth * -1.1, 0) l = Part.makePolygon(smallPolygon(cfg)) l.transformShape(m) doc.addObject("Part::Feature", "Right").Shape = l m = Matrix() m.move(cfg.outerWidth * 0.55, cfg.outerWidth * -1.1, 0) l = Part.makePolygon(smallPolygon(cfg)) l.transformShape(m) doc.addObject("Part::Feature", "Top").Shape = l m = Matrix() m.move(cfg.outerWidth * 1.60, cfg.outerWidth * -1.1, 0) l = Part.makePolygon(mediumPolygon(cfg)) l.transformShape(m) doc.addObject("Part::Feature", "Bottom").Shape = l class MakeBoxCommandClass(): def GetResources(self): return {#'Pixmap' : os.path.join( iconPath , 'AddWall.svg') , # the name of a svg file available in the resources 'MenuText': "Make Box" , 'ToolTip' : "Extends a wall from a side face of metal sheet"} def Activated(self): # TODO: put all objects in a group: # group = doc.addObject("App::DocumentObjectGroup","Group") cfg = BoxCfg().outerDimmensions(300, 100, 50) cfg.notches = 2 cfg.thickness = 10 cfg.prt() doc = FreeCAD.ActiveDocument makeBox(doc, cfg) doc.recompute() Gui.SendMsgToActiveView("ViewFit") return def IsActive(self): # if len(Gui.Selection.getSelection()) < 1 or len(Gui.Selection.getSelectionEx()[0].SubElementNames) < 1: # return False # selobj = Gui.Selection.getSelection()[0] # for selFace in Gui.Selection.getSelectionEx()[0].SubObjects: # if type(selFace) != Part.Face: # return False # return True return FreeCAD.ActiveDocument is not None class RemoveBoxCommandClass(): def GetResources(self): return { 'MenuText': "Remove Box", 'ToolTip' : "Remove the box"} def Activated(self): doc = FreeCAD.ActiveDocument def rm(name): o = getattr(doc, name, None); if hasattr(doc, name): doc.removeObject(name) rm("Top") rm("Back") rm("Left") rm("Front") rm("Right") rm("Bottom") return def IsActive(self): return FreeCAD.ActiveDocument is not None from FreeCAD import Gui Gui.addCommand('BoxerMakeBox', MakeBoxCommandClass()) Gui.addCommand('BoxerRemoveBox', RemoveBoxCommandClass())