def _gen_U(self, layerNum, objID): """For a given layerNum and objID, compute the quantity: ```latex U_{n,i}(t) = (\cup_{m<n;j} G_{m,j}) \cup (\cup_{k} A_k), ``` where the inner union terms are not included if their intersection with B_i is empty. """ layers = self.lithoDict['layers'] B = layers[layerNum]['objIDs'][objID][ 'B'] # B prism for this layer & objID GList = [] for m in layers.keys(): if m < layerNum: # then this is a lower layer for j in layers[m].keys(): if 'G' not in layers[layerNum]['objIDs'][objID]: self._gen_G(m, j) G = layers[layerNum]['objIDs'][objID]['G'] if checkOverlap([B, G]): GList.append(G) AList = [] for A in self.lithoDict['substrate'][()]: if checkOverlap([B, A]): AList.append(A) unionList = GList + AList unionObj = genUnion(unionList, consumeInputs=False) return unionObj
def exportBuiltParts(self, stepFileDir=None, stlFileDir=None): # Now that we are ready to export, we first want to merge all of the # 3D renders corresponding to a single shape into one entity: totalObjsDict = {} for partName in self._buildPartsDict.keys(): objsList = self._buildPartsDict[partName] mergedObj = genUnion(objsList, consumeInputs=True) mergedObj.Label = partName totalObjsDict[partName] = mergedObj # Now that we have merged the objects, we want to center them in the x-y # plane so the distances aren't ridiculous: centerObjects(totalObjsDict.values()) # Finally, we go through the dictionary and export: for partName in totalObjsDict.keys(): obj = totalObjsDict[partName] objFCName = obj.Name if stepFileDir is not None: filePath = stepFileDir + '/' + partName + '.step' exportCAD(obj, filePath) self.model.registerCadPart(partName, objFCName, filePath, reset=True) if stlFileDir is not None: filePath = stlFileDir + '/' + partName + '.stl' exportMeshed(obj, filePath)
def _gen_G(self, layerNum, objID): ''' Generate the gate deposition for a given layerNum and objID. ''' if 'G' not in self.lithoDict['layers'][layerNum]['objIDs'][objID]: if () not in self.lithoDict['layers'][layerNum]['objIDs'][objID][ 'HDict']: self.lithoDict['layers'][layerNum]['objIDs'][objID]['HDict'][( )] = self._H_offset(layerNum, objID) H = genUnion( self.lithoDict['layers'][layerNum]['objIDs'][objID]['HDict'][( )], consumeInputs=False) self.trash += [H] if self.fillShells: G = copy(H) else: U = self._gen_U(layerNum, objID) G = subtract(H, U) delete(U) self.lithoDict['layers'][layerNum]['objIDs'][objID]['G'] = G G = self.lithoDict['layers'][layerNum]['objIDs'][objID]['G'] partName = self.lithoDict['layers'][layerNum]['objIDs'][objID][ 'partName'] G.Label = partName return G
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'] = {}