Esempio n. 1
0
def build_extrude(part):
    """Build an extrude part.

    Parameters
    ----------
    part :


    Returns
    -------


    """
    assert isinstance(part, part_3d.ExtrudePart)
    z0 = part.z0
    deltaz = part.thickness
    doc = FreeCAD.ActiveDocument
    sketch = get_freecad_object(doc, part.fc_name)
    splitSketches = splitSketch(sketch)
    extParts = []
    for sketch in splitSketches:
        extParts.append(
            extrudeBetween(sketch, z0, z0 + deltaz, name=part.label))
        delete(sketch)
    doc.recompute()
    return genUnion(extParts, consumeInputs=True if not DBG_OUT else False)
Esempio n. 2
0
def build_lithography(part, opts, info_holder):
    """Build a lithography part.

    Parameters
    ----------
    part :

    opts : dict
        Options dict in the QMT Geometry3D.__init__ input format.

    info_holder :


    Returns
    -------


    """
    assert isinstance(part, part_3d.LithographyPart)
    if not info_holder.litho_setup_done:
        initialize_lithography(info_holder, opts, fillShells=True)
        info_holder.litho_setup_done = True

    if DBG_OUT:
        FreeCAD.ActiveDocument.saveAs("tmp_after_init.fcstd")
    layer_num = part.layer_num
    returnObjs = []
    for objID in info_holder.lithoDict["layers"][layer_num]["objIDs"]:
        if (part.fc_name == info_holder.lithoDict["layers"][layer_num]
            ["objIDs"][objID]["partName"]):
            returnObjs.append(gen_G(info_holder, opts, layer_num, objID))
    logging.debug([o.Name for o in returnObjs])
    return genUnion(returnObjs, consumeInputs=True if not DBG_OUT else False)
Esempio n. 3
0
def gen_G(info, opts, layerNum, objID):
    """Generate the gate deposition for a given layerNum and objID."""

    layerobj = info.lithoDict['layers'][layerNum]['objIDs'][objID]
    logging.debug('>>> layer %d obj %d (part:%s B:%s C:%s sketch:%s)', layerNum, objID,
                  layerobj['partName'], layerobj['B'].Name, layerobj['C'].Name,
                  layerobj['sketch'].Name)

    if 'G' not in layerobj:
        if () not in layerobj['HDict']:
            layerobj['HDict'][()] = H_offset(info, opts, layerNum, objID)

        if DBG_OUT:
            FreeCAD.ActiveDocument.saveAs('tmp_after_H_offset.fcstd')
        # TODO: reuse new function
        # This block fixes multifuses for wireshells with too big offsets,
        # by forcing all participating object shells into a new solid.
        # It still needs to be coerced into handling disjoint "solids".
        # ~ solid_hlist = []
        # ~ import Part
        # ~ for obj in layerobj['HDict'][()]:
            # ~ obj.Shape.Solids
            # ~ try:

                # ~ __s__ = obj.Shape.Faces
                # ~ __s__ = Part.Solid(Part.Shell(__s__))
                # ~ __o__ = FreeCAD.ActiveDocument.addObject("Part::Feature", obj.Name + "_solid")
                # ~ __o__.Label = obj.Label + "_solid"
                # ~ __o__.Shape = __s__

            # ~ except Part.OCCError:
                #Draft.downgrade(obj,delete=True)  # doesn't work without GUI
                # ~ for solid in obj.Shape.Solids:
                    # ~ for shell in solid.Shells:
                        # ~ pass

            # ~ solid_hlist.append(__o__)
            # ~ info.trash.append(obj)
            # ~ info.trash.append(__o__)
            # ~ info.trash.append(__s__)

        # ~ layerobj['HDict'][()] = solid_hlist
        # ~ logging.debug('new HDict: %s', [o.Name + ' (' + o.Label + ')' for o in layerobj['HDict'][()]])

        H = genUnion(layerobj['HDict'][()],
                     consumeInputs=False)
        info.trash.append(H)
        if info.fillShells:
            G = copy_move(H)
        else:
            U = gen_U(info, layerNum, objID)
            G = subtract(H, U)
            delete(U)
        layerobj['G'] = G

    G = layerobj['G']
    partName = layerobj['partName']
    G.Label = partName
    logging.debug('<<< G from H: %s (%s)', G.Name, G.Label)
    return G
Esempio n. 4
0
def gen_U(info, 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 = info.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]:
                    gen_G(info, m, j)
                G = layers[layerNum]["objIDs"][objID]["G"]
                if checkOverlap([B, G]):
                    GList.append(G)
    AList = []
    for A in info.lithoDict["substrate"][()]:
        if checkOverlap([B, A]):
            AList.append(A)
    unionList = GList + AList
    unionObj = genUnion(unionList, consumeInputs=False)
    return unionObj
Esempio n. 5
0
def build_lithography(part, opts, info_holder):
    """Build a lithography part."""
    assert part.directive == 'lithography'
    if not info_holder.litho_setup_done:
        initialize_lithography(info_holder, opts, fillShells=part.fill_litho)
        info_holder.litho_setup_done = True

    layerNum = part.layer_num
    returnObjs = []
    for objID in info_holder.lithoDict['layers'][layerNum]['objIDs']:
        if part.fc_name == info_holder.lithoDict['layers'][layerNum]['objIDs'][objID]['partName']:
            returnObjs.append(gen_G(info_holder, opts, layerNum, objID))
    return genUnion(returnObjs, consumeInputs=True)
Esempio n. 6
0
def build_extrude(part):
    """Build an extrude part."""
    assert part.directive == "extrude"
    z0 = part.z0
    deltaz = part.thickness
    doc = FreeCAD.ActiveDocument
    sketch = doc.getObject(part.fc_name)
    splitSketches = splitSketch(sketch)
    extParts = []
    for sketch in splitSketches:
        extParts.append(extrudeBetween(sketch, z0, z0 + deltaz, name=part.label))
        delete(sketch)
    doc.recompute()
    return genUnion(extParts, consumeInputs=True if not DBG_OUT else False)
Esempio n. 7
0
def build_lithography(part, opts, info_holder):
    """Build a lithography part."""
    assert part.directive == 'lithography'
    if not info_holder.litho_setup_done:
        initialize_lithography(info_holder, opts, fillShells=True)
        info_holder.litho_setup_done = True

    if DBG_OUT:
        FreeCAD.ActiveDocument.saveAs('tmp_after_init.fcstd')
    layerNum = part.layer_num
    returnObjs = []
    for objID in info_holder.lithoDict['layers'][layerNum]['objIDs']:
        if part.fc_name == info_holder.lithoDict['layers'][layerNum]['objIDs'][objID]['partName']:
            returnObjs.append(gen_G(info_holder, opts, layerNum, objID))

    logging.debug([o.Name for o in returnObjs])
    return genUnion(returnObjs, consumeInputs=True
                                if not DBG_OUT else False)
Esempio n. 8
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_move(rectPartTemp, moveVec=(0., 0., zBot - offset))
        delete(rectPartTemp)
        # make the cap of the wire:
        topSketchTemp = copy_move(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_move(capPartTemp, moveVec=(0., 0., zMid - offset))
        delete(capPartTemp)
        delete(topSketchTemp)
        delete(topSketch)
        delete(midSketch)
        delete(botSketch)
        returnParts += [capPart, rectPart]
        returnPart = genUnion(returnParts, consumeInputs=True
                                           if not DBG_OUT else False)
    return returnPart
Esempio n. 9
0
def gen_G(info, opts, layerNum, objID):
    """Generate the gate deposition for a given layerNum and objID."""
    layer = info.lithoDict['layers'][layerNum]
    if 'G' not in layer['objIDs'][objID]:
        if () not in layer['objIDs'][objID]['HDict']:
            layer['objIDs'][objID]['HDict'][()] = H_offset(
                info, opts, layerNum, objID)

        # ~ import sys
        # ~ sys.stderr.write('>>> ' + str(layer['objIDs'][objID]['HDict']) + '\n')
        # ~ # TODO: reuse new function
        # ~ # This block fixes multifuses for wireshells with too big offsets,
        # ~ # by forcing all participating object shells into a new solid.
        # ~ solid_hlist = []
        # ~ import Part
        # ~ for obj in layer['objIDs'][objID]['HDict'][()]:
        # ~ __s__ = obj.Shape.Faces
        # ~ __s__ = Part.Solid(Part.Shell(__s__))
        # ~ __o__ = FreeCAD.ActiveDocument.addObject("Part::Feature", obj.Name + "_solid")
        # ~ __o__.Label = obj.Label + "_solid"
        # ~ __o__.Shape = __s__
        # ~ solid_hlist.append(__o__)
        # ~ info.trash.append(obj)
        # ~ info.trash.append(__o__)
        # ~ info.trash.append(__s__)

        # ~ layer['objIDs'][objID]['HDict'][()] = solid_hlist
        # ~ sys.stderr.write('>>> ' + str(layer['objIDs'][objID]['HDict']) + '\n')

        H = genUnion(layer['objIDs'][objID]['HDict'][()], consumeInputs=False)
        info.trash.append(H)
        if info.fillShells:
            G = copy_move(H)
        else:
            U = gen_U(info, layerNum, objID)
            G = subtract(H, U)
            delete(U)
        layer['objIDs'][objID]['G'] = G
    G = layer['objIDs'][objID]['G']
    partName = layer['objIDs'][objID]['partName']
    G.Label = partName
    return G
Esempio n. 10
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"] = {}
Esempio n. 11
0
def makeSAG(sketch, zBot, zMid, zTop, tIn, tOut, offset=0.0):
    doc = FreeCAD.ActiveDocument
    assert zBot <= zMid
    assert zMid <= zTop

    # First, compute the geometric quantities we will need:
    a = zTop - zMid  # height of the top part
    b = tOut + tIn  # width of one of the triangular pieces of the top
    # if there is no slope to the roof, it's a different geometry which we don't handle:
    assert not np.isclose(
        b, 0
    ), "Either overshoot or inner displacement values need to be non-zero for SAG \
                                 (otherwise use extrude)"

    # This also means there would be no slope to the roof:
    assert not np.isclose(
        a, 0
    ), "Top and middle z values need to be different for SAG (otherwise use extrude)."
    alpha = np.arctan(a / 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 * (1 - np.cos(alpha)) / np.sin(alpha)

    sketchList = splitSketch(sketch)
    returnParts = []
    for tempSketch in sketchList:
        botSketch = draftOffset(tempSketch, offset)  # the base of the wire
        midSketch = draftOffset(tempSketch, f + d - tIn)  # the base of the cap
        top_offset = f - tIn
        topSketch = draftOffset(tempSketch, top_offset)  # the top of the cap
        # If topSketch has been shrunk exactly to a line or a point, relax the offset to 5E-5. Any closer and FreeCAD seems to suffer from numerical errors
        if topSketch.Shape.Area == 0:
            top_offset -= 5e-5
            delete(topSketch)
            topSketch = draftOffset(tempSketch, top_offset)

        delete(tempSketch)  # remove the copied sketch part
        # Make the bottom wire:
        if zMid - zBot != 0:
            rectPartTemp = extrude(botSketch, zMid - zBot)
            rectPart = copy_move(rectPartTemp,
                                 moveVec=(0.0, 0.0, zBot - offset))
            delete(rectPartTemp)
        else:
            rectPart = None

        # make the cap of the wire:
        topSketchTemp = copy_move(topSketch,
                                  moveVec=(0.0, 0.0, zTop - zMid + 2 * offset))
        capPartTemp = doc.addObject("Part::Loft", f"{sketch.Name}_cap")
        capPartTemp.Sections = [midSketch, topSketchTemp]
        capPartTemp.Solid = True
        doc.recompute()
        capPart = copy_move(capPartTemp, moveVec=(0.0, 0.0, zMid - offset))
        delete(capPartTemp)
        delete(topSketchTemp)

        delete(topSketch)
        delete(midSketch)
        delete(botSketch)
        returnPart = (genUnion([capPart, rectPart],
                               consumeInputs=True if not DBG_OUT else False)
                      if rectPart is not None else capPart)
        returnParts.append(returnPart)
    return returnParts