"""Исследовательский образец трёхсторонней молнии: гибкие мостики, усиленные зубья, сферические узлы и конический ползунок. Геометрия разработана для каталога по принципу Y-Zipper; работу зубьев и жёсткость необходимо испытать.""" import FreeCAD # noqa: F401 (initialize the CAD runtime first) # isort: split import math import sys from functools import lru_cache from pathlib import Path sys.path.insert(0, str(Path(__file__).resolve().parents[1])) from common import ( METAL, ORANGE, WHITE, App, Part, Piece, Pose, V, box, join, main, params, require, ) MODEL_ID = "y-zipper" DEFAULTS = { "length": 56, "width": 20, "bridge_thickness": 0.8, "tooth_thickness": 2.4, "pitch": 8, "node_radius": 1.2, "opening": 8, "clearance": 0.25, } NOTE = "Исследовательский образец трёхсторонней молнии: гибкие мостики, усиленные зубья, сферические узлы и конический ползунок. Геометрия разработана для каталога по принципу Y-Zipper; работу зубьев и жёсткость необходимо испытать." MODE = "experimental" def make_parts(supplied=None): p = params(DEFAULTS, supplied) length, width, wall, tooth, pitch, nr, opening, c = [ p[k] for k in ( "length", "width", "bridge_thickness", "tooth_thickness", "pitch", "node_radius", "opening", "clearance", ) ] require( length >= 4 * pitch and tooth > wall and pitch > 4 * nr and width > 8 * nr, "Teeth and flexible bridges need working space", ) ap = width / (2 * math.sqrt(3)) count = int(length / pitch) slider_h = 8 def wire(thick, z, shift=0): pts = [ V(-width / 2 + c, -ap - shift, z), V(width / 2 - c, -ap - shift, z), V(width / 2 + thick * math.sqrt(3) - c, -ap - thick - shift, z), V(-width / 2 - thick * math.sqrt(3) + c, -ap - thick - shift, z), ] return Part.makePolygon(pts + pts[:1]) body = Part.Face(wire(wall, 0)).extrude(V(0, 0, length)) for k in range(count): z = k * pitch end = min(length, z + pitch * 0.5) rib = Part.Face(wire(tooth, z)).extrude(V(0, 0, end - z)) body = join(body, rib) ball_z = z + pitch * 0.25 body = join(body, Part.makeSphere(nr, V(width / 2 + 0.43, -ap - 0.25, ball_z))) body = body.cut( Part.makeSphere(nr + c, V(-width / 2 - 0.43, -ap - 0.25, ball_z)) ) @lru_cache(maxsize=120) def strip(which, t): front = min(length, slider_h + pitch * int((length - slider_h) * t / pitch)) if front >= length: result = body.copy() else: lower = body.common( box(-width * 2, -width * 2, -1, width * 4, width * 4, front + 1) ) upper = body.common( box( -width * 2, -width * 2, front, width * 4, width * 4, length - front + 1, ) ) matrix = App.Matrix() matrix.A23 = -opening / length matrix.A24 = opening * front / length result = join(lower, upper.transformGeometry(matrix)) return result.rotated(V(), V(0, 0, 1), which * 120) parts = [ Piece( f"strip{i}", f"Гибкая зубчатая лента {i + 1}", strip(i, 1), deform=lambda t, i=i: strip(i, t), color=WHITE if i == 0 else ORANGE, ) for i in range(3) ] outer = width / math.sqrt(3) + 2 * tooth inner = outer + 2 * c def tri(radius, z): return Part.makePolygon( [ V( radius * math.cos(math.radians(a)), radius * math.sin(math.radians(a)), z, ) for a in (-30, 90, 210, -30) ] ) frame = Part.Face(tri(inner + 5, 0)).extrude(V(0, 0, slider_h)) funnel = Part.makeLoft( [tri(inner, 0), tri(inner + 2 * opening * slider_h / length, slider_h)], True, True, ) slider = frame.cut(funnel) parts.append( Piece( "slider", "Конический ползунок трёх лент", slider, lambda t: Pose(offset=(0, 0, (length - slider_h) * t)), color=METAL, ) ) return parts if __name__ == "__main__": main(make_parts, DEFAULTS, MODEL_ID, __file__, NOTE, MODE)