# Mako: the hull's shape and the 2D outline helpers. Pure functions and constants, no scene changes: exec'd by build_mako.py # and by mk_collision.py on its own (after lib2.py, tender_lib.py and mk_measure.py). The Spearfish's sections (sf_shape.py) # with the nose at image left and a table for the widest line. import math from mathutils import Vector def _f(tab, conv): return pchip(sorted((Yi(x), conv(v)) for x, v in tab)) TOPf, BOTf, Wf = _f(TOPY, Zi), _f(BOTY, Zi), _f(WPX, Lp) _ZMF = pchip(sorted((Yi(x), v) for x, v in ZMFRAC)) Y_NOSE, Y_TAIL = Yi(X_TIP), Yi(X_STERN) def ZMf(y): return TOPf(y) - _ZMF(y) * (TOPf(y) - BOTf(y)) def HTf(y): return max(1e-3, TOPf(y) - ZMf(y)) def HBf(y): return max(1e-3, ZMf(y) - BOTf(y)) def ximg(y): return X0 + y / S def NT(y): """top half: a round dome over the canopy, fuller over the collar and pods, a slight ridge toward the tail""" x = ximg(y) return 2.0 + 0.35 * smoothstep(250, 330, x) - 0.35 * smoothstep(480, 700, x) - 0.1 * smoothstep(780, 884, x) def NB(y): """bottom half: round under the glass nose, a V under the jaw, full under the lower body, boxy under the rear bay""" x = ximg(y) return 2.0 - 0.30 * smoothstep(110, 170, x) + 0.45 * smoothstep(280, 360, x) + 0.55 * smoothstep(440, 560, x) def sec(y, th, inset=0.0): c, s = math.cos(th), math.sin(th) n = NT(y) if s >= 0 else NB(y) W = max(0.004, Wf(y) - inset) H = max(0.004, (HTf(y) if s >= 0 else HBf(y)) - inset) x = W * math.copysign(abs(c) ** (2.0 / n), c) z = ZMf(y) + H * math.copysign(abs(s) ** (2.0 / n), s) return Vector((x, y, z)) def hull_z_at(x, y, top=True, inset=0.0): W = max(0.004, Wf(y) - inset); n = NT(y) if top else NB(y); H = max(0.004, (HTf(y) if top else HBf(y)) - inset) q = max(0.0, 1.0 - min(1.0, abs(x) / W) ** n) return ZMf(y) + (1 if top else -1) * H * q ** (1.0 / n) def hull_x_at(z, y, inset=0.0): W = max(0.004, Wf(y) - inset); zm = ZMf(y) top = z >= zm; n = NT(y) if top else NB(y); H = max(0.004, (HTf(y) if top else HBf(y)) - inset) q = max(0.0, 1.0 - min(1.0, abs(z - zm) / H) ** n) return W * q ** (1.0 / n) def _F(p): y = min(max(p.y, Y_NOSE + 1e-3), Y_TAIL) zm = ZMf(y); top = p.z >= zm n = NT(y) if top else NB(y); W = Wf(y); H = HTf(y) if top else HBf(y) return (abs(p.x) / W) ** n + (abs(p.z - zm) / H) ** n def normal_at(p, h=1e-4): g = Vector(((_F(p + Vector((h, 0, 0))) - _F(p - Vector((h, 0, 0)))), (_F(p + Vector((0, h, 0))) - _F(p - Vector((0, h, 0)))), (_F(p + Vector((0, 0, h))) - _F(p - Vector((0, 0, h)))))) return g.normalized() if g.length > 1e-12 else Vector((0, 0, 1)) def inside_depth(p): y = p.y if y < Y_NOSE or y > Y_TAIL + 0.005: return -1.0 W = Wf(y); zm = ZMf(y); top = p.z >= zm n = NT(y) if top else NB(y); H = HTf(y) if top else HBf(y) f = (abs(p.x) / W) ** n + (abs(p.z - zm) / H) ** n return (1.0 - f ** (1.0 / n)) * min(W, H) def on_top(lat, y, lift=0.0): p = Vector((lat, y, hull_z_at(lat, y, True))) return p + normal_at(p) * lift if lift else p def on_bot(lat, y, lift=0.0): p = Vector((lat, y, hull_z_at(lat, y, False))) return p + normal_at(p) * lift if lift else p def on_side(sg, y, z, lift=0.0): p = Vector((sg * hull_x_at(z, y), y, z)) return p + normal_at(p) * lift if lift else p def side_poly(img): return [(Yi(x), Zi(v)) for x, v in img] def pip(pt, poly): x, y = pt; inside = False; n = len(poly) for i in range(n): x1, y1 = poly[i]; x2, y2 = poly[(i + 1) % n] if (y1 > y) != (y2 > y) and x < (x2 - x1) * (y - y1) / (y2 - y1) + x1: inside = not inside return inside def resample(poly, step, closed=True): pts = list(poly) + ([poly[0]] if closed else []) out = [] for i in range(len(pts) - 1): (ax, ay), (bx, by) = pts[i], pts[i + 1] n = max(1, int(math.ceil(math.hypot(bx - ax, by - ay) / step))) for j in range(n): out.append((ax + (bx - ax) * j / n, ay + (by - ay) * j / n)) if not closed: out.append(pts[-1]) return out def table_f(tab, conv_x=Yi, conv_v=lambda v: v): """a measured polyline [(image x, value)] as a function of model y (linear, clamped)""" pts = sorted((conv_x(x), conv_v(v)) for x, v in tab) def f(y): if y <= pts[0][0]: return pts[0][1] if y >= pts[-1][0]: return pts[-1][1] for (a, va), (b, vb) in zip(pts, pts[1:]): if a <= y <= b: return va + (vb - va) * (y - a) / (b - a) return f CHINz = table_f(CHIN, Yi, Zi) SHELLz = table_f(SHELL, Yi, Zi) HOODth = table_f(HOOD_EDGE, Yi, math.radians) P_SIDEWIN = side_poly(SIDEWIN)