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