chiark / gitweb /
curveopt: add a commented-out call to synch everything
[moebius3.git] / moebius.py
index 38e3cb8d9e7d3c75f04c4f140004915f54e42dc9..224678ad3358c1e19a79e2bf98c8a3174a4339a7 100644 (file)
@@ -1,14 +1,17 @@
 
+from __future__ import print_function
+
 import numpy as np
+from numpy import cos, sin
 
-tau = np.pi * 2
+from bezier import BezierSegment
+from moenp import *
+from moebez import *
+from curveopt import *
 
-origin =  (0,0,0)
-unit_x = (1,0,0)
-unit_y = (0,1,0)
-unit_z = (0,0,1)
+import sys
 
-def ParametricCircle:
+class ParametricCircle:
   def __init__(pc, c, r0, r1):
     ''' circle centred on c
         with theta=0 point at c+r0
@@ -21,36 +24,144 @@ def ParametricCircle:
   def point(pc, theta):
     return pc._c + pc.radius(theta)
 
-def Twirler(ParametricCircle):
+class Twirler(ParametricCircle):
   def __init__(tw, c, r0, r1, cycles, begin_zeta):
     ''' circle centred on c, etc.
         but with an orientation at each point, orthogonal to
           the circle
         the orientation circles round cycles times during the
-          whole cycle
+          whole cycle (if cycles<1, to get a whole circling of
+          the dirn around the circle, must pass some theta>tau)
         begin_zeta is the angle from outwards at theta==0
-          positive meaning in the direction of r0 x r1
+          positive meaning in the direction of r0 x r1 from r0
     '''
     ParametricCircle.__init__(tw, c, r0, r1)
     tw._cycles = cycles
     tw._begin_zeta = begin_zeta
     tw._axis = np.cross(r0, r1)
-  def dirn(tw, theta):
-    zeta = tw._begin_zeta + theta * tw._cycles
-    r = radius(tw, theta)
-    return cos(zeta) * r + sin(zeta) * pc._axis
-
-class Moebius:
-  def __init__(m, n_u):
-    m._edge    = Twirler(origin,  unit_z, unit_x, 2, 0)
-    m._midline = Twirler(-unit_z, unit_z, unit_y, 1, 0)
-    m._beziers = [ self._bezier(u) for u in np.linspace(0, 1, n_u) ]
-  def _bezier(u):
-    theta = u * tau
+  def dirn(tw, theta, extra_zeta=0):
+    ''' unit vector for dirn at theta,
+         but rotated extra_theta more around the circle
+    '''
+    zeta = tw._begin_zeta + theta * tw._cycles + extra_zeta
+    r = tw.radius(theta)
+    return cos(zeta) * r + sin(zeta) * tw._axis
+
+class MoebiusHalf:
+  def __init__(m, nu, nt):
+    '''
+     MoebiusHalf().edge is a Twirler for the edge,
+      expecting theta = u * tau (see MoebiusHalf().point)
+      with dirn pointing into the surface
+    '''
+    m.edge    = Twirler(origin,  unit_z, unit_x, -2, tau/2)
+    m.midline = Twirler(-unit_z, unit_z, unit_y, -0.5, 0)
+    m.nu      = nu
+    m.nt      = nt
+    m._thetas = [ u * tau for u in np.linspace(0, 1, nu+1) ]
+    m._cp2b = BezierSegment([ (c,) for c in [0.33,0.33, 1.50]])
+    m._dbeziers = [ m._dbezier(theta) for theta in m._thetas ]
+    #check = int(nu/3)-1
+    #checks = (
+    #  m._dbezier(m._thetas[check], OptimisedCurve),
+    #  m._dbezier(m._thetas[check+1], OptimisedCurve),
+    #  m._dbezier(m._thetas[check+2], OptimisedCurve),
+    #)
+    #for c in checks: c.point_at_it(0)
+  def _dbezier(m, theta, dconstructor=OptimisedCurve):
     cp = [None] * 4
-    cp[0] =               m._edge   .point(theta)
-    cp[1] = cp[0] + 0.5 * m._edge   .dirn (theta)
-    cp[3] =               m._midline.point(theta*2)
-    cp[2] = cp[3] + 0.5 * m._midline.dirn (theta*2)
-    return BezierSegmentcp)
-    
+    cp[0] =               m.edge   .point(theta)
+    cp[3] =               m.midline.point(theta*2)
+    ncp3 = np.linalg.norm(cp[3])
+    cpt = ncp3 * ncp3 / 4
+    cp2scale = m._cp2b.point_at_t(ncp3/2)
+    cp1scale = 1.0 * cpt + 0.33 * (1-cpt)
+    #print('u=%d ncp3=%f cp2scale=%f' % (u, ncp3, cp2scale),
+    #      file=sys.stderr)
+    cp[1] = cp[0] + cp1scale * m.edge   .dirn (theta)
+    cp[2] = cp[3] + cp2scale * m.midline.dirn (theta*2)
+    return dconstructor(cp, m.nt)
+  def point(m, iu, it):
+    '''
+    0 <= iu <= nu     meaning 0 <= u <= 1
+                      along the extent (well, along the edge)
+                       0 and 1 are both the top half of the flat traverse
+                       0.5        is the bottom half of the flat traverse
+    0 <= it <= nt     across the half-traverse
+                       0 is the edge, 1 is the midline
+    '''
+    return np.array(m._dbeziers[iu].point_at_it(it))
+
+  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.h = MoebiusHalf(nu=nv*2, nt=m.nt)
+
+  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 { 'it': 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