Example #1
0
 def _collect_garbarge(self):
     """Delete all the objects in self.trash."""
     for obj in self.trash:
         try:
             delete(obj)
         except BaseException:
             pass
Example #2
0
 def _gen_offset(self, obj, offsetVal):
     """Generates an offset non-destructively."""
     # First, we need to check if the object needs special treatment:
     treatment = 'standard'
     for partName in self.model.modelDict['3DParts'].keys():
         partDict = self.model.modelDict['3DParts'][partName]
         if obj.Name in partDict['fileNames']:
             treatment = partDict['directive']
             break
     if treatment == 'extrude' or treatment == 'lithography':
         treatment = 'standard'
     if treatment == 'standard':
         # Apparently the offset function is buggy for very small offsets...
         if offsetVal < 1e-5:
             offsetDupe = copy(obj)
         else:
             offset = self.doc.addObject("Part::Offset")
             offset.Source = obj
             offset.Value = offsetVal
             offset.Mode = 0
             offset.Join = 2
             self.doc.recompute()
             offsetDupe = copy(offset)
             self.doc.recompute()
             delete(offset)
     elif treatment == 'wire':
         offsetDupe = self._build_wire(partName, offset=offsetVal)[0]
     elif treatment == 'wireShell':
         offsetDupe = self._build_wire_shell(partName, offset=offsetVal)[0]
     elif treatment == 'SAG':
         offsetDupe = self._build_SAG(partName, offset=offsetVal)[0]
     self.doc.recompute()
     return offsetDupe
Example #3
0
 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
Example #4
0
 def _collect_garbarge(self):
     ''' Delete all the objects in self.trash.
     '''
     for obj in self.trash:
         try:
             delete(obj)
         except:
             pass
Example #5
0
 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
Example #6
0
def deepRemove(obj=None, name=None, label=None):
    ''' Remove a targeted object and recursively delete all sub-objects it contains.
    '''
    doc = FreeCAD.ActiveDocument
    if obj is not None:
        pass
    elif name is not None:
        obj = doc.getObject(name)
    elif label is not None:
        obj = doc.getObjectsByLabel(label)[0]
    else:
        raise RuntimeError('No object selected!')
    parentName = obj.Name  # Initialize the parent to be obj
    parent = obj
    children = obj.OutList  # it's children are obj.OutList
    stillWorking = True  # Trip this once we are done
    breadCrumbs = []
    while stillWorking:  # We are still deleting stuff
        for child in children:  # Loop through the children of parent
            grandchildren = child.OutList  # for each child, determine if it has children
            if len(grandchildren) > 0:  # If it does, go down the tree
                breadCrumbs.append(parentName)  # Store where we came from
                parentName = child.Name  # set new parent to be the child
                parent = doc.getObject(parentName)
                children = parent.OutList  # and update the children list
                break  # break out of the children loop
            else:  # there are no children
                delete(child)  # delete the child
                break  # break since we modified the childrenList
        children = parent.OutList  # Check the children list
        if len(children) == 0:  # If there are no more at this level
            if obj.Name == parentName:  # Check to see if we are at the top
                delete(parent)  # delete the parent, which is obj
                stillWorking = False  # if yes, then stop looping
            else:  # otherwise we are no longer at the top
                parentName = breadCrumbs.pop(
                )  # reset the current parent to one level up
                parent = doc.getObject(parentName)
                children = parent.OutList  # update the children
Example #7
0
def draftOffset(inputSketch, t, tol=1e-8):
    ''' Attempt to offset the draft figure by a thickness t. Positive t is an
    inflation, while negative t is a deflation. tol sets how strict we should be when
    checking if the offset worked.
    '''
    from qmt.freecad import extrude, copy, delete

    if t == 0.:
        return copy(inputSketch)
    deltaT = np.abs(t)
    offsetVec1 = FreeCAD.Vector(-deltaT, -deltaT, 0.)
    offsetVec2 = FreeCAD.Vector(deltaT, deltaT, 0.)

    offset0 = copy(inputSketch)
    offset1 = Draft.offset(inputSketch, offsetVec1, copy=True)
    offset2 = Draft.offset(inputSketch, offsetVec2, copy=True)

    solid0 = extrude(offset0, 10.0)
    solid1 = extrude(offset1, 10.0)
    solid2 = extrude(offset2, 10.0)

    # Compute the volumes of these solids:
    V0 = solid0.Shape.Volume
    try:
        V1 = solid1.Shape.Volume
    except BaseException:
        V1 = None
    try:
        V2 = solid2.Shape.Volume
    except BaseException:
        V2 = None

    # If everything worked properly, these should either be ordered as
    # V1<V0<V2 or V2<V0<V1:
    if V2 > V0 and V0 > V1:
        bigSketch = offset2
        littleSketch = offset1
    elif V1 > V0 and V0 > V2:
        bigSketch = offset1
        littleSketch = offset2
    elif V2 > V1 and V1 > V0:
        bigSketch = offset2
        littleSketch = None
    # If we aren't in correct case, we still might be able to salvage things
    # for certain values of t:
    elif V1 > V2 and V2 > V0:
        bigSketch = offset1
        littleSketch = None
    elif V2 < V1 and V1 < V0:
        bigSketch = None
        littleSketch = offset2
    elif V1 < V2 and V2 < V0:
        bigSketch = None
        littleSketch = offset1
    else:
        bigSketch = None
        littleSketch = None
    delete(solid0)
    delete(solid1)
    delete(solid2)
    if t < 0 and littleSketch is not None:
        returnSketch = copy(littleSketch)
    elif t > 0 and bigSketch is not None:
        returnSketch = copy(bigSketch)
    elif abs(t) < tol:
        returnSketch = copy(inputSketch)
    else:
        raise ValueError('Failed to offset the sketch ' +
                         str(inputSketch.Name) + ' by amount ' + str(t))

    # # now that we have the three solids, we need to figure out which is bigger
    # # and which is smaller.
    # diff10 = subtract(solid1,solid0)
    # diff20 = subtract(solid2,solid0)
    # numVerts10 = len(diff10.Shape.Vertexes)
    # numVerts20 = len(diff20.Shape.Vertexes)
    # if numVerts10 > 0 and numVerts20 == 0:
    #     positiveOffsetIndex = 1
    # elif numVerts10 == 0 and numVerts20 > 0 :
    #     positiveOffsetIndex = 2
    # else:
    #     raise ValueError('draftOffset has failed to give a non-empty shape!')
    # delete(solid0)
    # delete(solid1)
    # delete(solid2)
    # delete(diff10)
    # delete(diff20)
    # if t > 0:
    #     if positiveOffsetIndex == 1:
    #         returnSketch = copy(offset1)
    #     else:
    #         returnSketch = copy(offset2)
    # elif t<0:
    #     if positiveOffsetIndex == 1:
    #         returnSketch = copy(offset2)
    #     else:
    #         returnSketch = copy(offset1)
    delete(offset0)
    delete(offset1)
    delete(offset2)
    return returnSketch
Example #8
0
def buildAlShell(sketch,
                 zBottom,
                 width,
                 verts,
                 thickness,
                 depoZone=None,
                 etchZone=None,
                 offset=0.0):
    """Builds a shell on a nanowire parameterized by sketch, zBottom, and width.

    Here, verts describes the vertices that are covered, and thickness describes
    the thickness of the shell. depoZone, if given, is extruded and intersected
    with the shell (for an etch). Note that offset here *is not* a real offset -
    for simplicity we keep this a thin shell that lies cleanly on top of the
    bigger wire offset. There's no need to include the bottom portion since that's
    already taken up by the wire.
    """
    lineSegments = findSegments(sketch)[0]
    x0, y0, z0 = lineSegments[0]
    x1, y1, z1 = lineSegments[1]
    dx = x1 - x0
    dy = y1 - y0
    rAxis = np.array([-dy, dx, 0])
    # axis perpendicular to the wire in the xy plane
    rAxis /= np.sqrt(np.sum(rAxis**2))
    zAxis = np.array([0, 0, 1.])
    doc = FreeCAD.ActiveDocument
    shellList = []
    for vert in verts:
        # Make the original wire (including an offset if applicable)
        originalWire = buildWire(sketch, zBottom, width, offset=offset)
        # Now make the shifted wire:
        angle = vert * np.pi / 3.
        dirVec = rAxis * np.cos(angle) + zAxis * np.sin(angle)
        shiftVec = (thickness) * dirVec
        transVec = FreeCAD.Vector(tuple(shiftVec))
        face = makeHexFace(sketch, zBottom - offset,
                           width + 2 * offset)  # make the bigger face
        shiftedFace = Draft.move(face, transVec, copy=False)
        extendedSketch = extendSketch(sketch, offset)
        # The shell offset is handled manually since we are using faceOverride to
        # input a shifted starting face:
        shiftedWire = buildWire(extendedSketch,
                                zBottom,
                                width,
                                faceOverride=shiftedFace)
        delete(extendedSketch)
        shellCut = doc.addObject("Part::Cut",
                                 sketch.Name + "_cut_" + str(vert))
        shellCut.Base = shiftedWire
        shellCut.Tool = originalWire
        doc.recompute()
        shell = Draft.move(shellCut, FreeCAD.Vector(0., 0., 0.), copy=True)
        doc.recompute()
        delete(shellCut)
        delete(originalWire)
        delete(shiftedWire)
        shellList.append(shell)
    if len(shellList) > 1:
        coatingUnion = doc.addObject("Part::MultiFuse",
                                     sketch.Name + "_coating")
        coatingUnion.Shapes = shellList
        doc.recompute()
        coatingUnionClone = copy(coatingUnion)
        doc.removeObject(coatingUnion.Name)
        for shell in shellList:
            doc.removeObject(shell.Name)
    elif len(shellList) == 1:
        coatingUnionClone = shellList[0]
    else:
        raise NameError(
            'Trying to build an empty Al shell. If no shell is desired, omit the AlVerts key from the json.'
        )
    if (depoZone is None) and (etchZone is None):
        return coatingUnionClone

    elif depoZone is not None:
        coatingBB = getBB(coatingUnionClone)
        zMin = coatingBB[4]
        zMax = coatingBB[5]
        depoVol = extrudeBetween(depoZone, zMin, zMax)
        etchedCoatingUnionClone = intersect([depoVol, coatingUnionClone],
                                            consumeInputs=True)
        return etchedCoatingUnionClone
    else:  # etchZone instead
        coatingBB = getBB(coatingUnionClone)
        zMin = coatingBB[4]
        zMax = coatingBB[5]
        etchVol = extrudeBetween(etchZone, zMin, zMax)
        etchedCoatingUnionClone = subtract(coatingUnionClone,
                                           etchVol,
                                           consumeInputs=True)
        return etchedCoatingUnionClone
Example #9
0
 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'] = {}
Example #10
0
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