Пример #1
0
def initialize_lithography(info, opts, fillShells=True):
    doc = FreeCAD.ActiveDocument
    info.fillShells = fillShells
    # The lithography step requires some infrastructure to track things
    # throughout.
    info.lithoDict = {
    }  # dictionary containing objects for the lithography step
    layers = info.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.
    info.lithoDict["substrate"] = {(): []}

    # To start, we need to collect up all the lithography directives, and
    # organize them by layer_num and objectIDs within layers.
    base_substrate_parts = []
    for part in opts["input_parts"]:
        # If this part is a litho step
        if isinstance(part, part_3d.LithographyPart):
            layer_num = part.layer_num  # layer_num of this part
            # Add the layer_num to the layer dictionary:
            if layer_num not in layers:
                layers[layer_num] = {"objIDs": {}}
            layer = layers[layer_num]
            # Generate the base and thickness of the layer:
            layer_base = float(part.z0)
            layer_thickness = float(part.thickness)
            # All parts within a given layer number are required to have
            # identical thickness and base, so check that:
            if "base" in layer:
                assert layer_base == layer["base"]
            else:
                layer["base"] = layer_base
            if "thickness" in layer:
                assert layer_thickness == layer["thickness"]
            else:
                layer["thickness"] = layer_thickness
            # 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 = get_freecad_object(doc, part.fc_name)
            splitSketches = splitSketch(sketch)
            for mySplitSketch in splitSketches:
                objID = len(layer["objIDs"])
                objDict = {}
                objDict["partName"] = part.fc_name
                objDict["sketch"] = mySplitSketch
                info.trash.append(mySplitSketch)
                layers[layer_num]["objIDs"][objID] = objDict
            # Add the base substrate to the appropriate dictionary
            base_substrate_parts += part.litho_base

    # Get rid of any duplicates:
    base_substrate_parts = list(set(base_substrate_parts))

    # Now convert the part names for the substrate into 3D freeCAD objects, which
    # should have already been rendered.
    for base_substrate in base_substrate_parts:
        try:
            built_part_name = opts["built_part_names"][base_substrate.label]
        except:
            raise KeyError(f"No substrate built for '{base_substrate.label}'")
        info.lithoDict["substrate"][()] += [
            get_freecad_object(doc, built_part_name)
        ]
    # ~ import sys
    # ~ sys.stderr.write(">>> litdic " + str(info.lithoDict) + "\n")

    # 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:

    bottom = min(layer["base"] for layer in layers.values())
    totalThickness = sum(layer["thickness"] for layer in layers.values())

    assert (len(info.lithoDict["substrate"][()]) > 0
            )  # Otherwise, we don't have a reference for the lateral BB
    substrateUnion = genUnion(info.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.
    info.lithoDict["boundingBox"] = [BB, constructionZone]
    delete(substrateUnion)  # not needed for next steps
    delete(constructionZone)  # not needed for next steps  ... WHY?

    # 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 layer_num in layers.keys():
        base = layers[layer_num]["base"]
        thickness = layers[layer_num]["thickness"]
        for objID in layers[layer_num]["objIDs"]:
            sketch = layers[layer_num]["objIDs"][objID]["sketch"]
            B = extrudeBetween(sketch, base, base + thickness)
            C = extrudeBetween(sketch, base, BB[5])
            layers[layer_num]["objIDs"][objID]["B"] = B
            layers[layer_num]["objIDs"][objID]["C"] = C
            info.trash.append(B)
            info.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.
            layers[layer_num]["objIDs"][objID]["HDict"] = {}
Пример #2
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.

    Parameters
    ----------
    sketch :

    zBottom :

    width :

    verts :

    thickness :

    depoZone :
        (Default value = None)
    etchZone :
        (Default value = None)
    offset :
        (Default value = 0.0)

    Returns
    -------


    """
    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.0])
    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.0
        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", f"{sketch.Name}_cut_{vert}")
        shellCut.Base = shiftedWire
        shellCut.Tool = originalWire
        doc.recompute()
        shell = Draft.move(shellCut, FreeCAD.Vector(0.0, 0.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",
                                     f"{sketch.Name}_coating")
        coatingUnion.Shapes = shellList
        doc.recompute()
        coatingUnionClone = copy_move(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 if not DBG_OUT else False)
        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 if not DBG_OUT else False)
        return etchedCoatingUnionClone