def test_exportCAD(datadir, fix_FCDoc): """Test step export/import.""" filePath = os.path.join(datadir, "tmp_testExport.stp") from qmt.geometry.freecad.geomUtils import makeBB testBB = (-1.0, 1.5, -2.0, 2.5, -3.0, 3.5) testShape = makeBB(testBB) exportCAD([testShape], filePath) Part.insert(filePath, fix_FCDoc.Name) CADImport = fix_FCDoc.getObject("tmp_testExport") import FreeCAD transBB = testBB[0:][::2] + testBB[ 1:][::2] # reformat to FreeCAD representation refBB = FreeCAD.Base.BoundBox(*transBB) assert repr(CADImport.Shape.BoundBox) == repr(refBB) with pytest.raises(TypeError) as err: exportCAD(testShape, "dummy.stp") assert "must be a list" in str(err.value) with pytest.raises(ValueError) as err: exportCAD([testShape], "not_a_step_file") assert "not a supported extension" in str(err.value)
def test_exportCAD(fix_testDir, fix_FCDoc): '''Test step export/import.''' filePath = os.path.join(fix_testDir, 'tmp_testExport.stp') from qmt.geometry.freecad.geomUtils import makeBB testBB = (-1., 1.5, -2., 2.5, -3., 3.5) testShape = makeBB(testBB) exportCAD([testShape], filePath) Part.insert(filePath, fix_FCDoc.Name) CADImport = fix_FCDoc.getObject("tmp_testExport") import FreeCAD transBB = testBB[0:][::2] + testBB[ 1:][::2] # reformat to FreeCAD representation refBB = FreeCAD.Base.BoundBox(*transBB) assert repr(CADImport.Shape.BoundBox) == repr(refBB) os.remove(filePath) with pytest.raises(TypeError) as err: exportCAD(testShape, 'dummy.stp') assert 'must be a list' in str(err.value) with pytest.raises(ValueError) as err: exportCAD([testShape], 'not_a_step_file') assert 'not a supported extension' in str(err.value)
def test_exportMeshed(datadir, fix_FCDoc): """Test mesh export/import.""" filePath = os.path.join(datadir, "testExport.stl") from qmt.geometry.freecad.geomUtils import makeBB testBB = (-1.0, 1.0, -2.0, 2.0, -3.0, 3.0) testShape = makeBB(testBB) exportMeshed([testShape], filePath) Mesh.insert(filePath, "testDoc") meshImport = fix_FCDoc.getObject("testExport") xMin = meshImport.Mesh.BoundBox.XMin xMax = meshImport.Mesh.BoundBox.XMax yMin = meshImport.Mesh.BoundBox.YMin yMax = meshImport.Mesh.BoundBox.YMax zMin = meshImport.Mesh.BoundBox.ZMin zMax = meshImport.Mesh.BoundBox.ZMax assert testBB == (xMin, xMax, yMin, yMax, zMin, zMax)
def test_exportMeshed(fix_testDir, fix_FCDoc): '''Test mesh export/import.''' filePath = os.path.join(fix_testDir, 'testExport.stl') from qmt.geometry.freecad.geomUtils import makeBB testBB = (-1., 1., -2., 2., -3., 3.) testShape = makeBB(testBB) exportMeshed(testShape, filePath) Mesh.insert(filePath, 'testDoc') meshImport = fix_FCDoc.getObject("testExport") xMin = meshImport.Mesh.BoundBox.XMin xMax = meshImport.Mesh.BoundBox.XMax yMin = meshImport.Mesh.BoundBox.YMin yMax = meshImport.Mesh.BoundBox.YMax zMin = meshImport.Mesh.BoundBox.ZMin zMax = meshImport.Mesh.BoundBox.ZMax assert testBB == (xMin, xMax, yMin, yMax, zMin, zMax) os.remove(filePath)
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"] = {}