def _build_extrude(self, partName): """Build an extrude part.""" partDict = self.model.modelDict['3DParts'][partName] assert partDict['directive'] == 'extrude' z0 = self._fetch_geo_param(partDict['z0']) deltaz = self._fetch_geo_param(partDict['thickness']) sketch = self.doc.getObject(partDict['fcName']) splitSketches = splitSketch(sketch) extParts = [] for mySplitSketch in splitSketches: extPart = extrudeBetween(mySplitSketch, z0, z0 + deltaz) extPart.Label = partName extParts.append(extPart) delete(mySplitSketch) return extParts
def makeSAG(sketch, zBot, zMid, zTop, tIn, tOut, offset=0.): doc = FreeCAD.ActiveDocument # First, compute the geometric quantities we will need: a = zTop - zMid # height of the top part b = tOut + tIn # width of one of the trianglular pieces of the top alpha = np.abs(np.arctan(a / np.float(b))) # lower angle of the top part c = a + 2 * offset # height of the top part including the offset # horizontal width of the trianglular part of the top after offset d = c / np.tan(alpha) # horizontal shift in the triangular part of the top after an offset f = offset / np.sin(alpha) sketchList = splitSketch(sketch) returnParts = [] for tempSketch in sketchList: # TODO: right now, if we try to taper the top of the SAG wire to a point, this # breaks, since the offset of topSketch is empty. We should detect and handle this. # For now, just make sure that the wire has a small flat top. botSketch = draftOffset(tempSketch, offset) # the base of the wire midSketch = draftOffset(tempSketch, f + d - tIn) # the base of the cap topSketch = draftOffset(tempSketch, -tIn + f) # the top of the cap delete(tempSketch) # remove the copied sketch part # Make the bottom wire: rectPartTemp = extrude(botSketch, zMid - zBot) rectPart = copy(rectPartTemp, moveVec=(0., 0., zBot - offset)) delete(rectPartTemp) # make the cap of the wire: topSketchTemp = copy(topSketch, moveVec=(0., 0., zTop - zMid + 2 * offset)) capPartTemp = doc.addObject('Part::Loft', sketch.Name + '_cap') capPartTemp.Sections = [midSketch, topSketchTemp] capPartTemp.Solid = True doc.recompute() capPart = copy(capPartTemp, moveVec=(0., 0., zMid - offset)) delete(capPartTemp) delete(topSketchTemp) delete(topSketch) delete(midSketch) delete(botSketch) returnParts += [capPart, rectPart] returnPart = genUnion(returnParts, consumeInputs=True) return returnPart
def _initialize_lithography(self, fillShells=True): self.fillShells = fillShells # The lithography step requires some infrastructure to track things # throughout. self.lithoDict = { } # dictionary containing objects for the lithography step self.lithoDict['layers'] = {} # Dictionary for containing the substrate. () indicates un-offset objects, # and subsequent tuples are offset by t_i for each index in the tuple. self.lithoDict['substrate'] = {(): []} # To start, we need to collect up all the lithography directives, and # organize them by layerNum and objectIDs within layers. baseSubstratePartNames = [] for partName in self.model.modelDict['3DParts'].keys(): partDict = self.model.modelDict['3DParts'][partName] # If this part is a litho step if 'lithography' == partDict['directive']: layerNum = partDict['layerNum'] # layerNum of this part # Add the layerNum to the layer dictionary: if layerNum not in self.lithoDict['layers']: self.lithoDict['layers'][layerNum] = {'objIDs': {}} layerDict = self.lithoDict['layers'][layerNum] # Gennerate the base and thickness of the layer: layerBase = self._fetch_geo_param(partDict['z0']) layerThickness = self._fetch_geo_param(partDict['thickness']) # All parts within a given layer number are required to have # identical thickness and base, so check that: if 'base' in layerDict: assert layerBase == layerDict['base'] else: layerDict['base'] = layerBase if 'thickness' in layerDict: assert layerThickness == layerDict['thickness'] else: layerDict['thickness'] = layerThickness # A given part references a base sketch. However, we need to split # the sketch here into possibly disjoint sub-sketches to work # with them: sketch = self.doc.getObject(partDict['fcName']) splitSketches = splitSketch(sketch) for mySplitSketch in splitSketches: objID = len(layerDict['objIDs']) objDict = {} objDict['partName'] = partName objDict['sketch'] = mySplitSketch self.trash.append(mySplitSketch) self.lithoDict['layers'][layerNum]['objIDs'][ objID] = objDict # Add the base substrate to the appropriate dictionary baseSubstratePartNames += partDict['lithoBase'] # Get rid of any duplicates: baseSubstratePartNames = list(set(baseSubstratePartNames)) # Now convert the part names for the substrate into 3D freeCAD objects, which # should have already been rendered. for baseSubstratePartName in baseSubstratePartNames: for baseSubstrateObjName in self.model.modelDict['3DParts'][ baseSubstratePartName]['fileNames'].keys(): self.lithoDict['substrate'][()] += [ self.doc.getObject(baseSubstrateObjName) ] # Now that we have ordered the primitives, we need to compute a few # aux quantities that we will need. First, we compute the total bounding # box of the lithography procedure: thicknesses = [] bases = [] for layerNum in self.lithoDict['layers'].keys(): thicknesses.append(self.lithoDict['layers'][layerNum]['thickness']) bases.append(self.lithoDict['layers'][layerNum]['base']) bottom = min(bases) totalThickness = sum(thicknesses) assert len(self.lithoDict['substrate'][ ()]) > 0 # Otherwise, we don't have a reference for the lateral BB substrateUnion = genUnion(self.lithoDict['substrate'][()], consumeInputs=False) # total substrate BB = list(getBB(substrateUnion)) # bounding box BB[4] = min([bottom, BB[4]]) BB[5] = max([BB[5] + totalThickness, bottom + totalThickness]) BB = tuple(BB) constructionZone = makeBB(BB) # box that encompases the whole domain. self.lithoDict['boundingBox'] = [BB, constructionZone] delete(substrateUnion) # not needed for next steps delete(constructionZone) # not needed for next steps # Next, we add two prisms for each sketch. The first, which we denote "B", # is bounded by the base from the bottom and the layer thickness on the top. # These serve as "stencils" that would be the deposited shape if no other. # objects got in the way. The second set of prisms, denoted "C", covers the # base of the layer to the top of the entire domain box. This is used for # forming the volumes occupied when substrate objects are offset and # checking for overlaps. for layerNum in self.lithoDict['layers'].keys(): base = self.lithoDict['layers'][layerNum]['base'] thickness = self.lithoDict['layers'][layerNum]['thickness'] for objID in self.lithoDict['layers'][layerNum]['objIDs']: sketch = self.lithoDict['layers'][layerNum]['objIDs'][objID][ 'sketch'] B = extrudeBetween(sketch, base, base + thickness) C = extrudeBetween(sketch, base, BB[5]) self.lithoDict['layers'][layerNum]['objIDs'][objID]['B'] = B self.lithoDict['layers'][layerNum]['objIDs'][objID]['C'] = C self.trash.append(B) self.trash.append(C) # In addition, add a hook for the HDict, which will contain the "H" # constructions for this object, but offset to thicknesses of various # layers, according to the keys. self.lithoDict['layers'][layerNum]['objIDs'][objID][ 'HDict'] = {}