jsuarez/extra/embyr_deprecated/embyr/transform.py (117 lines of code) (raw):

from pdb import set_trace as T import numpy as np import time from kivy.uix.widget import Widget from kivy.config import Config rad = 80 sz = 500 pi = 3.14159265 class Transform(Widget): def __init__(self, pos=[0, 0, 0.01], lookvec=[0, 0, 0], **kwargs): super().__init__(**kwargs) self.zoom = Zoom() self.pan = Pan('left', self.zoom) self.rotate = Rotate('right') self.t = 0 self.pos0 = np.array(pos) self.vec0 = np.array(lookvec) self.posn = np.array(pos) self.vecn = np.array(lookvec) self.add_widget(self.pan) self.add_widget(self.zoom) self.add_widget(self.rotate) self.button = None def update(self, dt): self.t += dt pos, vec = self.pos0, self.vec0 pos = self.zoom(pos, vec) pos, vec = self.rotate(pos, vec) pos, vec = self.pan(pos, vec) self.posn = pos self.vecn = vec return pos, vec #pos = self.zoom(pos, vec) #pos, vec = self.pan(pos, vec) #pos = self.rotate(pos, vec) return pos, vec class TouchWidget(Widget): def __init__(self, button, **kwargs): super().__init__(**kwargs) self.x, self.y, self.xVol, self.yVol = 0, 0, 0, 0 self.button = button self.reset = True #Currently broken due to race condition? #def on_touch_down(self, touch): # if touch.button == self.button: # self.xStart, self.yStart = touch.pos def on_touch_up(self, touch): if touch.button == self.button: self.x += self.xVol self.y += self.yVol self.xVol, self.yVol= 0, 0 self.reset = True def on_touch_move(self, touch): if self.reset: self.xStart, self.yStart = touch.pos self.reset = False if touch.button == self.button: xEnd, yEnd = touch.pos self.xVol = xEnd - self.xStart self.yVol = yEnd - self.yStart class Zoom(Widget): def __init__(self, **kwargs): super().__init__(**kwargs) self.zoom, self.delta = 1, 0.2 def clip(self, zoom, exprange=4): return np.clip(zoom, 0.5**exprange, 2**exprange) def on_touch_down(self, touch): if touch.button == 'scrollup': self.zoom = self.clip(self.zoom + self.delta) elif touch.button == 'scrolldown': self.zoom = self.clip(self.zoom - self.delta) def __call__(self, pos, vec): #return offvec * self.zoom pos = vec + self.zoom*(pos - vec) return pos class Pan(TouchWidget): def __init__(self, button, zoom, **kwargs): super().__init__(button, **kwargs) self.x, self.y, self.xVol, self.yVol = 0, 0, 0, 0 self.zoom = zoom self.panx, self.pany = 0, 0 def on_touch_move(self, touch): super().on_touch_move(touch) self.xVol = int(self.xVol * self.zoom.zoom) self.yVol = int(self.yVol * self.zoom.zoom) def __call__(self, pos, vec): x = 10 * (self.x + self.xVol) / sz z = 10 * (self.y + self.yVol) / sz pos0 = pos vec0 = vec #pos = pos0 + np.array([x, 0, 0]) #vec = vec0 + np.array([x, 0, 0]) #return pos, vec #Depth vector xx, _, zz = pos - vec norm = np.sqrt(xx**2 + zz**2) xx, zz = xx/norm, zz/norm vec = np.array([xx, 0, zz]) #Horizontal component unit_y = np.array([0, 1, 0]) xh, _, zh = np.cross(vec, unit_y) xh, zh = x*xh, x*zh horiz = np.array([xh, 0, zh]) #Depth component xd, zd = z*xx, z*zz depth = np.array([xd, 0, zd]) delta = horiz + depth #delta = np.array([xh, 0, zh]) #delta = np.array([xd, 0, zd]) return pos0 + delta, vec0 + delta class Rotate(TouchWidget): def __call__(self, pos, vec): x = (self.x + self.xVol) / sz y = (self.y + self.yVol) / sz #yclip = np.clip(y, -pi/2, 0) yclip = -y xz_x = np.cos(x) xz_z = np.sin(x) yz_y = np.cos(yclip) yz_z = np.sin(yclip) xz = np.array([ [xz_x, 0, -xz_z], [0 , 1, 0 ], [xz_z, 0, xz_x]]) yz = np.array([ [1, 0, 0 ], [0, yz_y, -yz_z], [0, yz_z, yz_y]]) #Find cylindrical xz plane rotation xx, _, zz = np.dot(xz, pos - vec) #Find spherical yz plane rotation _, yy, _ = np.dot(yz, pos - vec) #For x, z: shrink to position of spherical rotation #For y: use height from spherical rotation #pos = np.array([xx*xz_norm, yy, zz*xz_norm]) pos = np.array([xx, yy, zz]) pos = vec + pos return pos, vec