#! /usr/bin/python3 # Last edited on 2021-06-15 02:19:39 by jstolfi # Computes a cross-section of a vintage style gear with curved # and bifurcated spokes. import sploine # import sploine_smooth import rn import rmxn import sys from math import sqrt, hypot, sin, cos, atan2, log, exp, floor, ceil, inf, nan, pi def spoke_A(): # Main dimensions: R0 = 15 # Radius of axle hole. R1 = 25 # Radius of hub area. R2 = 40 # Outer radius of inner plate. R3 = 100 # Inner radius of outer plate. R4 = 105 # Inner radius of teeth crown. R5 = 115 # Inner radius of teeth. R6 = 130 # Outer radius of teeth (to be computed). # List {knot} has knots in polar coords. # See {knot_polar_to_cart} for format of each elem. # {None} is the separator between curves. knot = [] knot.append(( 85, R2, 0, 0, 2 )) knot.append(( 60, 53, 60, 0, 2 )) knot.append(( 55, 70, 45, 0, 2 )) knot.append(( 60, 88, 110, 0, 0 )) knot.append(( 70, R3, 180, 0, 2 )) knot.append(( 85, R3, 180, 0, 2 )) knot.append(None) knot.append(( 55, R3, 180, 0, 2 )) knot.append(( 55, 95, 120, 0, 1 )) knot.append(( 50, 78, 100, 0, 1 )) knot.append(( 40, 78, 230, 0, 1 )) knot.append(( 35, 95, 315, 0, 1 )) knot.append(( 40, R3, 180, 0, 2 )) knot.append(( 55, R3, 180, 0, 2 )) knot.append(None) knot.append(( 13, R2, 180, 0, 2 )) knot.append(( 15, R2, 180, 0, 2 )) knot.append(( 40, 65, 75, 0, 2 )) knot.append(( 35, 75, 65, 0, 2 )) knot.append(( 30, 95, 90, 0, 1 )) knot.append(( 25, R3, 0, 0, 2 )) knot.append(( 13, R3, 0, 0, 2 )) nk = len(knot) # Number of knots including {None}s. # Convert knots to knot states in Cartesian coordinates: ckn = [ knot_polar_to_cart(knot[k]) for k in range(nk) ] # ??? Finish it ??? return # ---------------------------------------------------------------------- def spoke_B(): # Main dimensions: R0 = 10 # Radius of axle hole. R1 = 20 # Radius of hub area. R2 = 30 # Outer radius of inner plate. R3 = 100 # Inner radius of outer plate. R4 = 105 # Inner radius of teeth crown. R5 = 115 # Inner radius of teeth. R6 = 130 # Outer radius of teeth (to be computed). R7 = 125 # Mid radius of rim stock. # Polar knots that define the spine of the spoke. # See {knot_polar_to_cart} for format of each elem. # A {None} marks the end of a segment of the spine.. knot = [] # Inner plate: knot.append(None) knot.append(( 9, R2, 180, 10.0, 2 )) knot.append(( 27, R2, 180, 10.0, 2 )) knot.append(( 45, R2, 180, 10.0, 2 )) knot.append(( 63, R2, 180, 10.0, 2 )) knot.append(( 81, R2, 180, 10.0, 2 )) # Trunk: knot.append(None) knot.append(( 50, R2, 105, 10.0, 2 )) knot.append(( 45, 50, 70, 9.0, 2 )) # Rear branch: knot.append(None) knot.append(( 45, 50, 70, 9.0, 2 )) knot.append(( 60, 97, 150, 6.0, 1 )) knot.append(( 70, R3, 180, 5.5, 2 )) # Forward branch: knot.append(None) knot.append(( 45, 50, 70, 9.0, 2 )) knot.append(( 22, 70, 15, 7.0, 1 )) knot.append(( 17, 97, 150, 6.0, 1 )) knot.append(( 27, R3, 180, 5.5, 2 )) # Outer plate: knot.append(None) knot.append(( 9, R3, 180, 6.0, 2 )) knot.append(( 27, R3, 180, 6.0, 2 )) knot.append(( 45, R3, 180, 6.0, 2 )) knot.append(( 63, R3, 180, 6.0, 2 )) knot.append(( 81, R3, 180, 6.0, 2 )) # Rim stock: knot.append(None) knot.append(( 9, R7, 180, 25.0, 2 )) knot.append(( 27, R7, 180, 25.0, 2 )) knot.append(( 45, R7, 180, 25.0, 2 )) knot.append(( 63, R7, 180, 25.0, 2 )) knot.append(( 81, R7, 180, 25.0, 2 )) replicate_turning(knot, 5) # Hub stock: knot.append(None) knot.append(( 0, 0, 0, R2, 2 )) nk = len(knot) # Convert knots to states in Cartesian coordinates: ckn = [ knot_polar_to_cart(knot[k]) for k in range(nk) ] # Interpolate between the knots: ds = 1.5 # Spacing of samples along the spline. STS = [] for k in range(nk): # Get two consecutive knots: ckn_b = ckn[k] ckn_c = None if k+1 >= nk else ckn[k+1] if ckn_b != None: if ckn_c != None: # Interpolate between {ckn_b} (inclusive) and {ckn_c} (exclusive): STS += sploine.interpolate_many(ckn_b, ckn_c, ds) else: # Single point at {phv_b}: STS.append(ckn_b) else: STS.append(None) # To mark a break in the curve # Smooth out the curve sections and fill in the missing tangent diections: np = 3 # Number of smoothing passes. for ip in range(np): sys.stderr.write("smoothing pass %d ...\n" % ip) relax_curves(STS) # Write out file for plotting: s = 0 p_prev = None for st in STS: if st == None: sys.stdout.write("\n") s = 0 p_prev = None else: p_this = st[0] t_this = st[1] sys.stdout.write("%12.4f %12.4f %12.4f %12.4f %12.4f\n" % (s,p_this[0],p_this[1],0,p_this[2])) if p_prev != None: ds = rn.dist(p_prev, p_this) s += ds p_prev = p_this sys.stdout.flush() return # ---------------------------------------------------------------------- def relax_curves(STS): sys.stderr.write("!! {relax_curves} not written yet\n") return # ---------------------------------------------------------------------- def knot_polar_to_cart(knot): # Converts a knot state {knot} from polar to Cartesian coordinates. # Except that returns {None} if {knot} is {None}. # # If not {None},the {knot} must be quintuple {(azimuth, radius, tilt, # halfwidh, fixed)}. The {azimuth} is the angular coordinate, from {X} # axis. The {radius} is the radial coordinate, from the origin. The # {tilt} is is the angle between the radial direction and the normal # to the curve. The {halfwidth} is a float. The {fixed} is an integer # used in relaxation: 0 means the state is not fixed, 1 means that the # position is fixed, 2 means position and tangent direction are fixed. # All angles are in degrees CCW. # # Returns a Cartesian state equivalent form of the knot, namely a # quadruple {(point, dir, fixed)} where {point} is a # 3-vector (tuple of 3 floats) which is the two Cartesian position # coordinates {X,Y} and the {halfwidth}, and {dir} is a unit 3-vector that specifies the tangent # direction. if knot == None: ckn = None else: ang = knot[0]*pi/180 # Azimuth of point (radians). rad = knot[1] # Radial distance of point. ant = ang + (knot[2]-90)*pi/180 # Azimuth of tangent direction (radians). p = ( rad*cos(ang), rad*sin(ang), knot[3] ) t = ( cos(ant), sin(ant), 0 ) f = knot[4] ckn = (p, t, f,) return ckn # ---------------------------------------------------------------------- def replicate_turning(knot, m): # Appends to the list {knot} {m-1} copies of its current contents, # each copy rotated {360/m} degrees. nk1 = len(knot) da = 360/m for kr in range(m-1): ang = (kr + 1)*da for i in range(nk1): knk = knot[i] if knk == None: knot.append(knk) else: knk_rot = (knk[0] + ang,) + knk[1:] knot.append(knk_rot) return # ---------------------------------------------------------------------- spoke_B()