+ return np.array(m._beziers[iu].point_at_t(t))
+
+ def details(m, iu, t):
+ '''
+ returns tuple of 4 vectors:
+ - point location
+ - normal (+ve is in the +ve y direction at iu=t=0) unit vector
+ - along extent (towrds +ve iu) unit vector
+ - along traverse (towards +ve t) unit vector
+ '''
+ if iu == m.nu:
+ return m.details(0, t)
+ p = m.point(iu, t)
+ vec_t = unit_v( m.point(iu, t + 0.01) - p )
+ if t == 0:
+ normal = m.edge.dirn(m._thetas[iu], extra_zeta=-tau/4)
+ vec_u = np.cross(vec_t, normal)
+ else:
+ vec_u = unit_v( m.point(iu+1, t) - p )
+ normal = np.cross(vec_u, vec_t)
+ return p, normal, vec_u, vec_t
+
+ def point_offset(m, iu, t, offset):
+ '''
+ offset by offset perpendicular to the surface
+ at the top (iu=t=0), +ve offset is in the +ve y direction
+ '''
+ p, normal, dummy, dummy = m.details(iu, t)
+ return p + offset * normal
+
+class Moebius():
+ def __init__(m, nv, nw):
+ '''
+ 0 <= v <= nw along the extent, v=0 is the flat traverse
+ 0 <= w <= nv across the traverse nw must be even
+ the top is both v=0, w=0 v=nv, w=nw
+ '''
+ assert(nw % 1 == 0)
+ m.nv = nv
+ m.nw = nw
+ m.nt = nw/2
+ m._t_vals = np.linspace(0, 1, m.nt+1)
+ m.h = MoebiusHalf(nu=nv*2)
+
+ def _vw2tiu_kw(m, v, w):
+ if w <= m.nt:
+ it = w
+ iu = v
+ else:
+ it = m.nw - w
+ iu = m.nv + v
+ #print('v,w=%d,%d => it,iu=%d,%d' % (v,w,it,iu),
+ # file=sys.stderr)
+ return { 't': m._t_vals[it], 'iu': iu }
+
+ def point(m, v, w):
+ return m.h.point(**m._vw2tiu_kw(v,w))
+
+ def point_offset(m, v, w, offset):
+ return m.h.point_offset(offset=
+ offset if w <= m.nt else -offset,
+ **m._vw2tiu_kw(v,w))
+
+ def details(m, v, w):
+ '''
+ returns tuple of 4 vectors:
+ - point location
+ - normal (+ve is in the +ve y direction at iu=t=0) unit vector
+ - along extent (towrds +ve v) unit vector
+ - along traverse (towards +ve w) unit vector
+ '''
+ p, normal, vec_v, vec_w = m.h.details(**m._vw2tiu_kw(v,w))
+ if w > m.nt:
+ normal = -normal
+ vec_w = -vec_w
+ return p, normal, vec_v, vec_w