# -*- coding: utf-8 -*- """DIFERENCIAL - por que las dos ruedas pueden girar a distinta velocidad. Todas las piezas se generan por codigo y se mueven con la cinematica exacta del mecanismo: w_izq + w_der = 2 * w_jaula, y el satelite gira sobre su eje a (w_der - w_izq)/2 respecto de la jaula. El pinon y la corona respetan su relacion: 14 y 43 dientes, con semiangulos de cono que suman 90 grados. """ import math, os, sys sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) import bpy from mathutils import Vector from base import * NOMBRE = "diferencial" EJE_X = 1.32 # donde van las ruedas R_RUEDA = 0.52 OMEGA = 1.45 # rad/s de la jaula RADIO_GIRO = 3.9 TROCHA = 1.70 INCL = -0.72 # bien diagonal: en 9:16 es lo que aprovecha el alto N_COR, N_PIN = 43, 14 # relacion 3,07:1, como un diferencial de calle G_COR, G_PIN = 72.0, 18.0 # los semiangulos suman 90: es la condicion de engrane N_LAT = 16 X_COR = -0.50 # apice comun de corona y pinon: lejos de los satelites D_I, D_O = 0.70, 1.00 # la cara dentada, igual en los dos P_FASE = math.pi / 2 # para que el diente caiga en el hueco P_ESP = -0.62 # mano contraria a la corona P_SIG = 1.0 # el conjunto mide 3,68 de punta a punta; inclinado 41 grados eso son 2,77 de # ancho y 2,41 de alto, que es lo que fija estos numeros ENCUADRE = [(0, 6.2), (2, 5.0), (3, 4.2), (4, 5.6), (5, 4.6), (6, 5.0), (7, 5.6), (8, 5.6), (9, 5.8), (10, 5.2)] DIR_CAM = Vector((0.30, -0.93, 0.21)).normalized() def alto_en(T, f): tabla, altos, v = dict(ENCUADRE), [], ENCUADRE[0][1] for i in range(len(T.beats)): v = tabla.get(i, v) altos.append(v) i = 0 for k in range(len(T.beats)): if f >= T.rango(k)[0]: i = k ini, fin = T.rango(i) p = min(1.0, (f - ini) / max(1.0, (fin - ini) * 0.55)) return mezcla(altos[i - 1] if i else altos[0], altos[i], p) def construir(T): sc = escena() mundo_estudio(0.65) # lo que refleja el acero cam = camara((0, -6, 0), (0, 0, 0), lente=50) cam.data.sensor_fit = 'VERTICAL' cam.data.sensor_height = 36.0 luz("key", 'AREA', (-3.4, -4.6, 4.4), 1400, "blanco", tam=6.0, mira=(0, 0, 0)) luz("fill", 'AREA', (4.2, -3.2, -1.6), 420, "#BFD4FF", tam=5.0, mira=(0, 0, 0)) luz("rim", 'AREA', (-0.8, 4.6, 2.8), 900, "blanco", tam=4.0, mira=(0, 0, 0)) M = { "acero": metal("acero", "#B7BEC9", rug=0.22), "acero2": metal("acero2", "#9BA3B0", rug=0.30), "fundido": metal("fundido", "#5E6774", rug=0.62, met=0.85), "eje_i": metal("eje_i", "#C2C8D2", rug=0.24), "eje_d": metal("eje_d", "#C2C8D2", rug=0.24), "goma": material("goma", "#1E222C", rug=0.52), "alu": metal("alu", "#CBD3DE", rug=0.15), "freno": metal("freno", "#79818E", rug=0.45, met=0.9), } # --- vista de arriba: el auto doblando -------------------------------- piso = bpy.data.objects.new("suelo_g", None) bpy.context.collection.objects.link(piso) arcos, ruedas_a = [], [] for k, (rr, c) in enumerate(((RADIO_GIRO - TROCHA / 2, "rosa"), (RADIO_GIRO + TROCHA / 2, "verde"))): pts = [] for i in range(120): th = -0.62 + 1.24 * i / 119.0 pts.append((-RADIO_GIRO + rr * math.cos(th), rr * math.sin(th), 0.0)) cu = curva_poly(f"arco{k}", [pts], grosor=0.045, radios=[[0.0] * 120], mat=material(f"m_arco{k}", c, emis=2.4, rug=0.5)) cu.parent = piso arcos.append((cu, pts)) r = rueda_completa(f"ra{k}", M, R=R_RUEDA, r=0.185, ancho=0.28) r.parent = piso ruedas_a.append(r) eje_a = cilindro("eje_a", 0.075, TROCHA, M["acero2"]) eje_a.parent = piso patina = curva_poly("patina", [[(0, 0, 0)] * 60], grosor=0.11, radios=[[0.0] * 60], mat=material("m_pat", "blanco", emis=3.4)) patina.parent = piso # --- el diferencial ---------------------------------------------------- dif = bpy.data.objects.new("dif", None) bpy.context.collection.objects.link(dif) dif.scale = (0, 0, 0) jaula = bpy.data.objects.new("jaula", None) bpy.context.collection.objects.link(jaula) jaula.parent = dif # caja de la jaula: dos bridas anulares y dos paredes curvas sobre el eje del # perno. Los lados +-Y quedan abiertos: por ahi se ven los engranajes, que es # como se ve una caja de diferencial de verdad # todo lo de la jaula queda por dentro de r=0.52: el pinon nunca baja de # r=0.59, y asi la jaula puede girar sin pasarle por encima for sx in (0.58, -0.58): br = anillo_plano(f"brida{sx}", 0.13, 0.52, 0.08, M["fundido"]) br.rotation_euler = (0, math.pi / 2, 0) br.location = (sx, 0, 0) br.parent = jaula for k, centro in enumerate((math.pi / 2, -math.pi / 2)): pared = sector_anillo(f"pared{k}", 0.43, 0.52, 1.16, centro - 0.62, centro + 0.62, M["fundido"]) pared.rotation_euler = (0, math.pi / 2, 0) pared.parent = jaula perno = cilindro("perno", 0.062, 1.00, M["acero2"]) perno.parent = jaula corona = engranaje_conico("corona", (1, 0, 0), N=N_COR, gamma=G_COR, d_i=D_I, d_o=D_O, alto=0.042, mat=M["acero"], espiral=0.62, espesor=0.14, hueco=0.58, por_diente=24) corona.location = (X_COR, 0, 0) corona.parent = jaula tornillos("bulones", 10, 0.78, -0.11, largo=0.10, r_t=0.035, mat=M["acero2"], padre=jaula) lat = {} for nom, eje, sx in (("izq", (-1, 0, 0), -1), ("der", (1, 0, 0), 1)): g = bpy.data.objects.new(f"lat_{nom}", None) bpy.context.collection.objects.link(g) g.parent = dif eng = engranaje_conico(f"eng_{nom}", eje, N=N_LAT, gamma=45.0, d_i=0.26, d_o=0.50, alto=0.042, mat=M["acero"], espiral=0.30, espesor=0.12, hueco=0.09) eng.parent = g # semieje escalonado + brida for (largo, radio, x0) in ((0.48, 0.110, 0.50), (EJE_X - 0.82, 0.080, 0.95)): se = cilindro(f"se_{nom}_{radio}", radio, largo, M[f"eje_{nom[0]}"]) se.rotation_euler = (0, math.pi / 2, 0) se.location = (sx * x0, 0, 0) se.parent = g brida = anillo_plano(f"bw_{nom}", 0.09, 0.22, 0.07, M["acero2"]) brida.rotation_euler = (0, math.pi / 2, 0) brida.location = (sx * (EJE_X - 0.20), 0, 0) brida.parent = g rue = rueda_completa(f"rue_{nom}", M, R=R_RUEDA, r=0.185, ancho=0.28) rue.location = (sx * EJE_X, 0, 0) rue.parent = g lat[nom] = g sat = [] for k, eje in enumerate(((0, 0, 1), (0, 0, -1))): g = bpy.data.objects.new(f"sat{k}", None) bpy.context.collection.objects.link(g) g.parent = jaula eng = engranaje_conico(f"eng_s{k}", eje, N=N_LAT, gamma=45.0, d_i=0.26, d_o=0.50, alto=0.042, mat=M["acero2"], espiral=-0.30, espesor=0.12, hueco=0.09, fase=math.pi) eng.parent = g if k == 0: p = esfera(f"testigo{k}", 0.050, material("m_test", "ambar", emis=3.0)) p.location = (0.30, 0.0, 0.42) p.parent = g sat.append(g) entrada = bpy.data.objects.new("entrada", None) bpy.context.collection.objects.link(entrada) entrada.parent = dif # misma cara que la corona, espiral de mano contraria y fase para que el # diente caiga en el hueco pinon = engranaje_conico("pinon", (0, -1, 0), N=N_PIN, gamma=G_PIN, d_i=D_I, d_o=D_O, alto=0.042, mat=M["acero"], espiral=P_ESP, espesor=0.12, hueco=0.07, fase=P_FASE, por_diente=24) pinon.location = (X_COR, 0, 0) pinon.parent = entrada cardan = cilindro("cardan", 0.095, 1.30, M["acero2"]) cardan.rotation_euler = (math.pi / 2, 0, 0) cardan.location = (X_COR, -1.50, 0) cardan.parent = entrada horquilla = anillo_plano("horquilla", 0.10, 0.24, 0.10, M["acero2"]) horquilla.location = (X_COR, -1.20, 0) horquilla.rotation_euler = (math.pi / 2, 0, 0) horquilla.parent = entrada barras = {} for nom, c, z in (("izq", "rosa", -0.92), ("der", "verde", -0.60)): riel = cilindro(f"riel_{nom}", 0.038, 1.50, material(f"mr_{nom}", "riel", rug=0.6)) riel.rotation_euler = (0, math.pi / 2, 0) riel.location = (0.90, -0.6, z) b = cilindro(f"bar_{nom}", 0.060, 1.0, material(f"mb_{nom}", c, emis=2.6)) b.rotation_euler = (0, math.pi / 2, 0) barras[nom] = (riel, b, z) riel.scale = b.scale = (0, 0, 0) fecha = texto("1827", tam=0.44, color="ambar") fecha.scale = (0, 0, 0) def emision(mat, v, col=None): n = mat.node_tree.nodes["Principled BSDF"] n.inputs["Emission Strength"].default_value = v if col is not None: n.inputs["Emission Color"].default_value = (*srgb(col), 1) return dict(cam=cam, M=M, emision=emision, piso=piso, arcos=arcos, ruedas_a=ruedas_a, eje_a=eje_a, patina=patina, dif=dif, lat=lat, jaula=jaula, sat=sat, entrada=entrada, pinon=pinon, barras=barras, fecha=fecha) def main(): T = Tiempo(NOMBRE) ob = construir(T) M, barras = ob["M"], ob["barras"] def actualizar(f): t = T.t(f) baja = suave(T.p(f, 2) / 0.62) dist = 50.0 / 36.0 * alto_en(T, f) apuntar(ob["cam"], Vector((0.0, -1.1, 8.6)).lerp(DIR_CAM * dist, baja), Vector((0.0, 0.0, 0.0)).lerp(Vector((0.0, 0.0, -0.50)), baja)) # ---- vista de arriba vis_g = 1.0 - suave(T.p(f, 2) / 0.45) ob["piso"].scale = (vis_g, vis_g, vis_g) if vis_g > 0.001: av = suave(T.p(f, 0) * 0.62) if f < T.rango(1)[0] else \ mezcla(0.62, 1.0, T.p(f, 1)) th = -0.60 + 1.20 * av centros = [] for k, (cu, pts) in enumerate(ob["arcos"]): rr = RADIO_GIRO + (TROCHA / 2) * (1 if k else -1) c = Vector((-RADIO_GIRO + rr * math.cos(th), rr * math.sin(th), R_RUEDA)) centros.append(c) n = max(2, int(120 * av)) rehacer_curva(cu, [pts], [[1.0 if i <= n else 0.30 for i in range(120)]]) giro = (rr * (th + 0.60)) / R_RUEDA if f >= T.rango(1)[0]: rig = (RADIO_GIRO + TROCHA / 2) * (th + 0.60) / R_RUEDA giro = mezcla(giro, rig, suave(T.p(f, 1) / 0.35)) r = ob["ruedas_a"][k] r.location = c r.rotation_euler = (giro, 0, th) # rueda sobre su eje y apunta al centro medio = (centros[0] + centros[1]) / 2 ob["piso"].location = (-medio.x, -medio.y - 0.30, 0.0) ob["eje_a"].location = medio ob["eje_a"].rotation_euler = (0, math.pi / 2, th) pv = suave(T.p(f, 1) / 0.4) rr = RADIO_GIRO - TROCHA / 2 pts, rad = [], [] for i in range(60): a2 = -0.60 + (th + 0.60) * i / 59.0 pts.append((-RADIO_GIRO + rr * math.cos(a2), rr * math.sin(a2), 0.04)) rad.append(pv * min(1.0, (i / 59.0) * 2.4)) rehacer_curva(ob["patina"], [pts], [rad]) # ---- el diferencial ap = suave((T.p(f, 2) - 0.35) / 0.5) ob["dif"].scale = (ap, ap, ap) ob["dif"].rotation_euler = (0, INCL, 0) arranca = T.rango(6)[0] tt = max(0.0, t - T.t(arranca)) k_giro = mezcla(0.0, 0.42, T.p(f, 8) / 0.7) ang_j = OMEGA * tt if f >= arranca else 0.0 ang_i = ang_j * (1.0 - k_giro) ang_d = ang_j * (1.0 + k_giro) ob["jaula"].rotation_euler = (ang_j, 0, 0) ob["lat"]["izq"].rotation_euler = (ang_i, 0, 0) ob["lat"]["der"].rotation_euler = (ang_d, 0, 0) for k, g in enumerate(ob["sat"]): g.rotation_euler = (0, 0, (ang_i - ang_d) / 2 * (1 if k == 0 else -1)) ob["pinon"].rotation_euler = (0, P_SIG * ang_j * N_COR / N_PIN, 0) # ---- resaltados en los colores del canal, sobre el acero def pico(i): p = T.p(f, i) return 0.85 * math.sin(math.pi * p) ** 2 if 0.0 < p < 1.0 else 0.0 ob["emision"](M["eje_i"], pico(4), "rosa") ob["emision"](M["eje_d"], pico(4), "verde") ob["emision"](M["acero2"], pico(5), "celeste") ob["emision"](M["fundido"], pico(5), "ambar") ob["emision"](M["acero"], pico(6), "ambar") # ---- barras de velocidad bv = suave(T.p(f, 9) / 0.4) * (1.0 - suave(T.p(f, 10) / 0.3)) for nom, (riel, bar, z) in barras.items(): riel.scale = (bv, bv, bv) rel = (1.0 - k_giro) if nom == "izq" else (1.0 + k_giro) L = 1.50 * rel / 1.5 bar.scale = (bv, bv, bv * L) bar.location = (0.15 + L / 2, -0.6, z) fv = suave((T.p(f, 10) - 0.25) / 0.4) ob["fecha"].location = (0.90, 0.0, -0.80) ob["fecha"].scale = (fv, fv, fv) render_secuencia(NOMBRE, T, actualizar) main()