blob: eff4da96126363a27c61fddef24b9fc573bee7d3 [file] [log] [blame]
(function addPropertyDecorator() {
function loopOut(type, duration, durationFlag) {
if (!this.k || !this.keyframes) {
return this.pv;
}
type = type ? type.toLowerCase() : '';
var currentFrame = this.comp.renderedFrame;
var keyframes = this.keyframes;
var lastKeyFrame = keyframes[keyframes.length - 1].t;
if (currentFrame <= lastKeyFrame) {
return this.pv;
} else {
var cycleDuration, firstKeyFrame;
if (!durationFlag) {
if (!duration || duration > keyframes.length - 1) {
duration = keyframes.length - 1;
}
firstKeyFrame = keyframes[keyframes.length - 1 - duration].t;
cycleDuration = lastKeyFrame - firstKeyFrame;
} else {
if (!duration) {
cycleDuration = Math.max(0, lastKeyFrame - this.elem.data.ip);
} else {
cycleDuration = Math.abs(lastKeyFrame - elem.comp.globalData.frameRate * duration);
}
firstKeyFrame = lastKeyFrame - cycleDuration;
}
var i, len, ret;
if (type === 'pingpong') {
var iterations = Math.floor((currentFrame - firstKeyFrame) / cycleDuration);
if (iterations % 2 !== 0) {
return this.getValueAtTime(((cycleDuration - (currentFrame - firstKeyFrame) % cycleDuration + firstKeyFrame)) / this.comp.globalData.frameRate, 0);
}
} else if (type === 'offset') {
var initV = this.getValueAtTime(firstKeyFrame / this.comp.globalData.frameRate, 0);
var endV = this.getValueAtTime(lastKeyFrame / this.comp.globalData.frameRate, 0);
var current = this.getValueAtTime(((currentFrame - firstKeyFrame) % cycleDuration + firstKeyFrame) / this.comp.globalData.frameRate, 0);
var repeats = Math.floor((currentFrame - firstKeyFrame) / cycleDuration);
if (this.pv.length) {
ret = new Array(initV.length);
len = ret.length;
for (i = 0; i < len; i += 1) {
ret[i] = (endV[i] - initV[i]) * repeats + current[i];
}
return ret;
}
return (endV - initV) * repeats + current;
} else if (type === 'continue') {
var lastValue = this.getValueAtTime(lastKeyFrame / this.comp.globalData.frameRate, 0);
var nextLastValue = this.getValueAtTime((lastKeyFrame - 0.001) / this.comp.globalData.frameRate, 0);
if (this.pv.length) {
ret = new Array(lastValue.length);
len = ret.length;
for (i = 0; i < len; i += 1) {
ret[i] = lastValue[i] + (lastValue[i] - nextLastValue[i]) * ((currentFrame - lastKeyFrame) / this.comp.globalData.frameRate) / 0.0005;
}
return ret;
}
return lastValue + (lastValue - nextLastValue) * (((currentFrame - lastKeyFrame)) / 0.001);
}
return this.getValueAtTime((((currentFrame - firstKeyFrame) % cycleDuration + firstKeyFrame)) / this.comp.globalData.frameRate, 0);
}
}
function loopIn(type, duration, durationFlag) {
if (!this.k) {
return this.pv;
}
type = type ? type.toLowerCase() : '';
var currentFrame = this.comp.renderedFrame;
var keyframes = this.keyframes;
var firstKeyFrame = keyframes[0].t;
if (currentFrame >= firstKeyFrame) {
return this.pv;
} else {
var cycleDuration, lastKeyFrame;
if (!durationFlag) {
if (!duration || duration > keyframes.length - 1) {
duration = keyframes.length - 1;
}
lastKeyFrame = keyframes[duration].t;
cycleDuration = lastKeyFrame - firstKeyFrame;
} else {
if (!duration) {
cycleDuration = Math.max(0, this.elem.data.op - firstKeyFrame);
} else {
cycleDuration = Math.abs(elem.comp.globalData.frameRate * duration);
}
lastKeyFrame = firstKeyFrame + cycleDuration;
}
var i, len, ret;
if (type === 'pingpong') {
var iterations = Math.floor((firstKeyFrame - currentFrame) / cycleDuration);
if (iterations % 2 === 0) {
return this.getValueAtTime((((firstKeyFrame - currentFrame) % cycleDuration + firstKeyFrame)) / this.comp.globalData.frameRate, 0);
}
} else if (type === 'offset') {
var initV = this.getValueAtTime(firstKeyFrame / this.comp.globalData.frameRate, 0);
var endV = this.getValueAtTime(lastKeyFrame / this.comp.globalData.frameRate, 0);
var current = this.getValueAtTime((cycleDuration - (firstKeyFrame - currentFrame) % cycleDuration + firstKeyFrame) / this.comp.globalData.frameRate, 0);
var repeats = Math.floor((firstKeyFrame - currentFrame) / cycleDuration) + 1;
if (this.pv.length) {
ret = new Array(initV.length);
len = ret.length;
for (i = 0; i < len; i += 1) {
ret[i] = current[i] - (endV[i] - initV[i]) * repeats;
}
return ret;
}
return current - (endV - initV) * repeats;
} else if (type === 'continue') {
var firstValue = this.getValueAtTime(firstKeyFrame / this.comp.globalData.frameRate, 0);
var nextFirstValue = this.getValueAtTime((firstKeyFrame + 0.001) / this.comp.globalData.frameRate, 0);
if (this.pv.length) {
ret = new Array(firstValue.length);
len = ret.length;
for (i = 0; i < len; i += 1) {
ret[i] = firstValue[i] + (firstValue[i] - nextFirstValue[i]) * (firstKeyFrame - currentFrame) / 0.001;
}
return ret;
}
return firstValue + (firstValue - nextFirstValue) * (firstKeyFrame - currentFrame) / 0.001;
}
return this.getValueAtTime(((cycleDuration - (firstKeyFrame - currentFrame) % cycleDuration + firstKeyFrame)) / this.comp.globalData.frameRate, 0);
}
}
function smooth(width, samples) {
if (!this.k) {
return this.pv;
}
width = (width || 0.4) * 0.5;
samples = Math.floor(samples || 5);
if (samples <= 1) {
return this.pv;
}
var currentTime = this.comp.renderedFrame / this.comp.globalData.frameRate;
var initFrame = currentTime - width;
var endFrame = currentTime + width;
var sampleFrequency = samples > 1 ? (endFrame - initFrame) / (samples - 1) : 1;
var i = 0, j = 0;
var value;
if (this.pv.length) {
value = createTypedArray('float32', this.pv.length);
} else {
value = 0;
}
var sampleValue;
while (i < samples) {
sampleValue = this.getValueAtTime(initFrame + i * sampleFrequency);
if (this.pv.length) {
for (j = 0; j < this.pv.length; j += 1) {
value[j] += sampleValue[j];
}
} else {
value += sampleValue;
}
i += 1;
}
if (this.pv.length) {
for (j = 0; j < this.pv.length; j += 1) {
value[j] /= samples;
}
} else {
value /= samples;
}
return value;
}
function getValueAtTime(frameNum) {
frameNum *= this.elem.globalData.frameRate;
frameNum -= this.offsetTime;
if (frameNum !== this._cachingAtTime.lastFrame) {
this._cachingAtTime.lastIndex = this._cachingAtTime.lastFrame < frameNum ? this._cachingAtTime.lastIndex : 0;
this._cachingAtTime.value = this.interpolateValue(frameNum, this._cachingAtTime);
this._cachingAtTime.lastFrame = frameNum;
}
return this._cachingAtTime.value;
}
function getTransformValueAtTime(time) {
if (!this._transformCachingAtTime) {
this._transformCachingAtTime = {
v: new Matrix(),
};
}
/// /
var matrix = this._transformCachingAtTime.v;
matrix.cloneFromProps(this.pre.props);
if (this.appliedTransformations < 1) {
var anchor = this.a.getValueAtTime(time);
matrix.translate(
-anchor[0] * this.a.mult,
-anchor[1] * this.a.mult,
anchor[2] * this.a.mult,
);
}
if (this.appliedTransformations < 2) {
var scale = this.s.getValueAtTime(time);
matrix.scale(
scale[0] * this.s.mult,
scale[1] * this.s.mult,
scale[2] * this.s.mult,
);
}
if (this.sk && this.appliedTransformations < 3) {
var skew = this.sk.getValueAtTime(time);
var skewAxis = this.sa.getValueAtTime(time);
matrix.skewFromAxis(-skew * this.sk.mult, skewAxis * this.sa.mult);
}
if (this.r && this.appliedTransformations < 4) {
var rotation = this.r.getValueAtTime(time);
matrix.rotate(-rotation * this.r.mult);
} else if (!this.r && this.appliedTransformations < 4) {
var rotationZ = this.rz.getValueAtTime(time);
var rotationY = this.ry.getValueAtTime(time);
var rotationX = this.rx.getValueAtTime(time);
var orientation = this.or.getValueAtTime(time);
matrix.rotateZ(-rotationZ * this.rz.mult)
.rotateY(rotationY * this.ry.mult)
.rotateX(rotationX * this.rx.mult)
.rotateZ(-orientation[2] * this.or.mult)
.rotateY(orientation[1] * this.or.mult)
.rotateX(orientation[0] * this.or.mult);
}
if (this.data.p && this.data.p.s) {
var positionX = this.px.getValueAtTime(time);
var positionY = this.py.getValueAtTime(time);
if (this.data.p.z) {
var positionZ = this.pz.getValueAtTime(time);
matrix.translate(
positionX * this.px.mult,
positionY * this.py.mult,
-positionZ * this.pz.mult,
);
} else {
matrix.translate(positionX * this.px.mult, positionY * this.py.mult, 0);
}
} else {
var position = this.p.getValueAtTime(time);
matrix.translate(
position[0] * this.p.mult,
position[1] * this.p.mult,
-position[2] * this.p.mult,
);
}
return matrix;
/// /
}
function getTransformStaticValueAtTime(time) {
return this.v.clone(new Matrix());
}
var getTransformProperty = TransformPropertyFactory.getTransformProperty;
TransformPropertyFactory.getTransformProperty = function (elem, data, container) {
var prop = getTransformProperty(elem, data, container);
if (prop.dynamicProperties.length) {
prop.getValueAtTime = getTransformValueAtTime.bind(prop);
} else {
prop.getValueAtTime = getTransformStaticValueAtTime.bind(prop);
}
prop.setGroupProperty = expressionHelpers.setGroupProperty;
return prop;
};
var propertyGetProp = PropertyFactory.getProp;
PropertyFactory.getProp = function (elem, data, type, mult, container) {
var prop = propertyGetProp(elem, data, type, mult, container);
// prop.getVelocityAtTime = getVelocityAtTime;
// prop.loopOut = loopOut;
// prop.loopIn = loopIn;
if (prop.kf) {
prop.getValueAtTime = expressionHelpers.getValueAtTime.bind(prop);
} else {
prop.getValueAtTime = expressionHelpers.getStaticValueAtTime.bind(prop);
}
prop.setGroupProperty = expressionHelpers.setGroupProperty;
prop.loopOut = loopOut;
prop.loopIn = loopIn;
prop.smooth = smooth;
prop.getVelocityAtTime = expressionHelpers.getVelocityAtTime.bind(prop);
prop.getSpeedAtTime = expressionHelpers.getSpeedAtTime.bind(prop);
prop.numKeys = data.a === 1 ? data.k.length : 0;
prop.propertyIndex = data.ix;
var value = 0;
if (type !== 0) {
value = createTypedArray('float32', data.a === 1 ? data.k[0].s.length : data.k.length);
}
prop._cachingAtTime = {
lastFrame: initialDefaultFrame,
lastIndex: 0,
value: value,
};
expressionHelpers.searchExpressions(elem, data, prop);
if (prop.k) {
container.addDynamicProperty(prop);
}
return prop;
};
function getShapeValueAtTime(frameNum) {
// For now this caching object is created only when needed instead of creating it when the shape is initialized.
if (!this._cachingAtTime) {
this._cachingAtTime = {
shapeValue: shape_pool.clone(this.pv),
lastIndex: 0,
lastTime: initialDefaultFrame,
};
}
frameNum *= this.elem.globalData.frameRate;
frameNum -= this.offsetTime;
if (frameNum !== this._cachingAtTime.lastTime) {
this._cachingAtTime.lastIndex = this._cachingAtTime.lastTime < frameNum ? this._caching.lastIndex : 0;
this._cachingAtTime.lastTime = frameNum;
this.interpolateShape(frameNum, this._cachingAtTime.shapeValue, this._cachingAtTime);
}
return this._cachingAtTime.shapeValue;
}
var ShapePropertyConstructorFunction = ShapePropertyFactory.getConstructorFunction();
var KeyframedShapePropertyConstructorFunction = ShapePropertyFactory.getKeyframedConstructorFunction();
function ShapeExpressions() {}
ShapeExpressions.prototype = {
vertices: function (prop, time) {
if (this.k) {
this.getValue();
}
var shapePath = this.v;
if (time !== undefined) {
shapePath = this.getValueAtTime(time, 0);
}
var i, len = shapePath._length;
var vertices = shapePath[prop];
var points = shapePath.v;
var arr = createSizedArray(len);
for (i = 0; i < len; i += 1) {
if (prop === 'i' || prop === 'o') {
arr[i] = [vertices[i][0] - points[i][0], vertices[i][1] - points[i][1]];
} else {
arr[i] = [vertices[i][0], vertices[i][1]];
}
}
return arr;
},
points: function (time) {
return this.vertices('v', time);
},
inTangents: function (time) {
return this.vertices('i', time);
},
outTangents: function (time) {
return this.vertices('o', time);
},
isClosed: function () {
return this.v.c;
},
pointOnPath: function (perc, time) {
var shapePath = this.v;
if (time !== undefined) {
shapePath = this.getValueAtTime(time, 0);
}
if (!this._segmentsLength) {
this._segmentsLength = bez.getSegmentsLength(shapePath);
}
var segmentsLength = this._segmentsLength;
var lengths = segmentsLength.lengths;
var lengthPos = segmentsLength.totalLength * perc;
var i = 0, len = lengths.length;
var j = 0, jLen;
var accumulatedLength = 0, pt;
while (i < len) {
if (accumulatedLength + lengths[i].addedLength > lengthPos) {
var initIndex = i;
var endIndex = (shapePath.c && i === len - 1) ? 0 : i + 1;
var segmentPerc = (lengthPos - accumulatedLength) / lengths[i].addedLength;
pt = bez.getPointInSegment(shapePath.v[initIndex], shapePath.v[endIndex], shapePath.o[initIndex], shapePath.i[endIndex], segmentPerc, lengths[i]);
break;
} else {
accumulatedLength += lengths[i].addedLength;
}
i += 1;
}
if (!pt) {
pt = shapePath.c ? [shapePath.v[0][0], shapePath.v[0][1]] : [shapePath.v[shapePath._length - 1][0], shapePath.v[shapePath._length - 1][1]];
}
return pt;
},
vectorOnPath: function (perc, time, vectorType) {
// perc doesn't use triple equality because it can be a Number object as well as a primitive.
perc = perc == 1 ? this.v.c ? 0 : 0.999 : perc;
var pt1 = this.pointOnPath(perc, time);
var pt2 = this.pointOnPath(perc + 0.001, time);
var xLength = pt2[0] - pt1[0];
var yLength = pt2[1] - pt1[1];
var magnitude = Math.sqrt(Math.pow(xLength, 2) + Math.pow(yLength, 2));
if (magnitude === 0) {
return [0, 0];
}
var unitVector = vectorType === 'tangent' ? [xLength / magnitude, yLength / magnitude] : [-yLength / magnitude, xLength / magnitude];
return unitVector;
},
tangentOnPath: function (perc, time) {
return this.vectorOnPath(perc, time, 'tangent');
},
normalOnPath: function (perc, time) {
return this.vectorOnPath(perc, time, 'normal');
},
setGroupProperty: expressionHelpers.setGroupProperty,
getValueAtTime: expressionHelpers.getStaticValueAtTime,
};
extendPrototype([ShapeExpressions], ShapePropertyConstructorFunction);
extendPrototype([ShapeExpressions], KeyframedShapePropertyConstructorFunction);
KeyframedShapePropertyConstructorFunction.prototype.getValueAtTime = getShapeValueAtTime;
KeyframedShapePropertyConstructorFunction.prototype.initiateExpression = ExpressionManager.initiateExpression;
var propertyGetShapeProp = ShapePropertyFactory.getShapeProp;
ShapePropertyFactory.getShapeProp = function (elem, data, type, arr, trims) {
var prop = propertyGetShapeProp(elem, data, type, arr, trims);
prop.propertyIndex = data.ix;
prop.lock = false;
if (type === 3) {
expressionHelpers.searchExpressions(elem, data.pt, prop);
} else if (type === 4) {
expressionHelpers.searchExpressions(elem, data.ks, prop);
}
if (prop.k) {
elem.addDynamicProperty(prop);
}
return prop;
};
}());