def clone(self, parent): k = self.__class__(self.time(), self.value(), parent) k.__inTangent = Vec2(self.inTangent) k.__outTangent = Vec2(self.outTangent) k.__tangentBroken = self.tangentBroken k.__tangentMode = self.tangentMode return k
def __frameOnKeys(self, keys): boundsMin = None boundsMax = None for key in keys: point = key.point() if boundsMin is None: boundsMin = Vec2(point) boundsMax = Vec2(boundsMin) continue boundsMin.x = min(boundsMin.x, point.x) boundsMin.y = min(boundsMin.y, point.y) boundsMax.x = max(boundsMax.x, point.x) boundsMax.y = max(boundsMax.y, point.y) if boundsMin is None: return # Determine padding on both sides (32 pixels please) paddingX = (32.0 / max(1.0, self.width())) * ( (boundsMax.x - boundsMin.x) if (boundsMax.x != boundsMin.x) else 1.0) paddingY = (32.0 / max(1.0, self.height())) * ( (boundsMax.y - boundsMin.y) if (boundsMax.y != boundsMin.y) else 1.0) region = boundsMin.x - paddingX, boundsMin.y - paddingY, boundsMax.x - boundsMin.x + 2 * paddingX, boundsMax.y - boundsMin.y + 2 * paddingY self.__cameraUndoStack.push(CameraFrameAction(self.__camera, region)) self.repaint()
def __init__(self, time, value, parent): self.__point = Vec2(time, value) self.__parent = parent # note that tangent X values have been deprecated and is not exported; they were for cubic bezier curves that never got made self.inTangent = Vec2(0.0, 0.0) self.outTangent = Vec2(0.0, 0.0) self.__inTangentType = Key.TYPE_LINEAR self.__outTangentType = Key.TYPE_LINEAR self.__tangentBroken = False self.__tangentMode = Key.TANGENT_AUTO
def addKeyWithTangents(self, inTangentX, inTangentY, time, value, outTangentX, outTangentY, tangentBroken, tangentMode): k = Key(time, value, self) self.__keys.append(k) self.sortKeys() k.inTangent = Vec2(inTangentX, inTangentY) k.outTangent = Vec2(outTangentX, outTangentY) k.tangentBroken = tangentBroken k.tangentMode = tangentMode return k
def updateTangents(self): if self.__tangentMode == Key.TANGENT_USER: return if self.__tangentMode == Key.TANGENT_STEPPED: # this leave the input tangent as is, so you can go set e.g. "linear" to get the input, then back to "stepped" # TODO: have "output is stepped" as separate state ("in tangent" with "stepped output" control is tedious) self.outTangent = Vec2(0.0, float('inf')) return if self.__tangentMode == Key.TANGENT_FLAT: self.inTangent = Vec2(0.0, 0.0) self.outTangent = Vec2(0.0, 0.0) else: self.__parent.updateTangents(self, self.__tangentMode)
def mapTangentToScreen(self, key, isInTangent): # Calculate the tangent position on screen (as a PointF) point = key.point() cameraRect = self.__camera.region() screenSize = Vec2(float(self.width()), float(self.height())) viewPort = Vec2(cameraRect[2], cameraRect[3]) delta = Vec2(-key.inTangent if isInTangent else key.outTangent) if delta.sqrLen() == 0: delta = Vec2(-1.0 if isInTangent else 1.0, 0.0) px = (delta * screenSize) / viewPort px.normalize() px *= 50.0 delta = (px / screenSize) * viewPort return Vec2(point.x + delta.x, point.y + delta.y)
def keyDirection(a, b): keyDifference = b.point() - a.point() try: keyDifference.normalize() except ZeroDivisionError: return Vec2(0.0, 0.0) keyDifference.x = abs(keyDifference.x) return keyDifference
def _apply(self): """ Set key state. """ i = 0 for key in self.__selection: x = self.__restoreData[i][0] + self.__delta[0] y = self.__restoreData[i][1] + self.__delta[1] if self.__snap[0]: x = round(x * self.__snap[0]) / float(self.__snap[0]) if self.__snap[1]: y = round(y * self.__snap[1]) / float(self.__snap[1]) key.setPoint(Vec2(x, y)) i += 1
def setPoint(self, point): self.__point = Vec2(point) self.__parent.sortKeys() self.__parent.keyChanged(self)
def point(self): return Vec2(self.__point)
def updateTangents(self, key, mode): idx = self.__keys.index(key) first = idx == 0 last = idx == len(self.__keys) - 1 if first and last: return def keyDirection(a, b): keyDifference = b.point() - a.point() try: keyDifference.normalize() except ZeroDivisionError: return Vec2(0.0, 0.0) keyDifference.x = abs(keyDifference.x) return keyDifference def finalize(): if not first and key.inTangent.length() != 0: pd = self.__keys[idx].time() - self.__keys[idx - 1].time() try: key.inTangent *= pd / key.inTangent.x except ZeroDivisionError: pass if not last and key.outTangent.length() != 0: nd = self.__keys[idx + 1].time() - self.__keys[idx].time() try: key.outTangent *= nd / key.outTangent.x except ZeroDivisionError: pass if mode == Key.TANGENT_LINEAR: if first: key.inTangent = Vec2(0.0, 0.0) else: key.inTangent = keyDirection(self.__keys[idx], self.__keys[idx - 1]) key.inTangent.x = -key.inTangent.x if last: key.outTangent = Vec2(0.0, 0.0) else: key.outTangent = keyDirection(self.__keys[idx], self.__keys[idx + 1]) finalize() return elif mode == Key.TANGENT_SPLINE: if first: key.outTangent = keyDirection(self.__keys[idx], self.__keys[idx + 1]) key.inTangent = key.outTangent elif last: key.inTangent = keyDirection(self.__keys[idx], self.__keys[idx - 1]) key.inTangent.x = -key.inTangent.x key.outTangent = -key.inTangent else: key.outTangent = keyDirection(self.__keys[idx - 1], self.__keys[idx + 1]) key.inTangent = -key.outTangent finalize() return elif mode == Key.TANGENT_AUTO: def sgn(x): return -1 if x < 1 else 1 if x > 1 else 0 if first or last or sgn(self.__keys[idx - 1].value() - key.value() ) == sgn(self.__keys[idx + 1].value() - key.value()): key.inTangent = Vec2(0.0, 0.0) key.outTangent = Vec2(0.0, 0.0) else: key.outTangent = keyDirection(self.__keys[idx - 1], self.__keys[idx + 1]) key.inTangent = -key.outTangent finalize() return elif mode in (Key.TANGENT_USER, Key.TANGENT_STEPPED): return assert False, 'Invalid tangent mode for key.'