Skip to content

Instantly share code, notes, and snippets.

@eeropic
Last active September 9, 2021 12:15
Show Gist options
  • Select an option

  • Save eeropic/e00f0b500d6fc9b2f8d622d4b61b03dc to your computer and use it in GitHub Desktop.

Select an option

Save eeropic/e00f0b500d6fc9b2f8d622d4b61b03dc to your computer and use it in GitHub Desktop.
pAEper.jsx & pAEperPlayer.js
#include "lib/json2.js"
//export AE shape layer animations to Paper.js project JSON
//AE matchname shortcuts
ae={
contents:"ADBE Root Vectors Group",
transform:"ADBE Transform Group",
zerogroup:"ADBE Vectors Group",
group:"ADBE Vector Group",
groupTrans:"ADBE Vector Transform Group",
groupPos:"ADBE Vector Position",
groupAnchor:"ADBE Vector Anchor",
pathGroup:"ADBE Vector Shape - Group",
path:"ADBE Vector Shape",
rect:"ADBE Vector Shape - Rect",
rectSize:"ADBE Vector Rect Size",
rectRound:"ADBE Vector Rect Roundness",
rectPos:"ADBE Vector Rect Position",
ellipse:"ADBE Vector Shape - Ellipse",
ellipseSize:"ADBE Vector Ellipse Size",
ellipsePos:"ADBE Vector Ellipse Position",
fill:"ADBE Vector Graphic - Fill",
fillColor:"ADBE Vector Fill Color",
fillOpacity:"ADBE Vector Fill Opacity",
fillRule:"ADBE Vector Fill Rule",
composite:"ADBE Vector Composite Order",
stroke:"ADBE Vector Graphic - Stroke",
strokeColor:"ADBE Vector Stroke Color",
strokeCap:"ADBE Vector Stroke Line Cap",
strokeJoin:"ADBE Vector Stroke Line Join",
strokeMiter:"ADBE Vector Stroke Miter Limit",
strokeWidth:"ADBE Vector Stroke Width",
strokeOpacity:"ADBE Vector Stroke Opacity",
strokeDashes:"ADBE Vector Stroke Dashes",
//Dash n Gap n
strokeDash:"ADBE Vector Stroke Dash ",
strokeGap:"ADBE Vector Stroke Gap ",
strokeOffset:"ADBE Vector Stroke Offset",
strokeCaps:["","butt","round","square"],
strokeJoins:["", "miter","round","bevel"],
gradFill:"ADBE Vector Graphic - G-Fill",
gradType:"ADBE Vector Grad Type",
gradStart:"ADBE Vector Grad Start Pt",
gradEnd:"ADBE Vector Grad End Pt",
gradColors:"ADBE Vector Grad Colors",
gradStroke:"ADBE Vector Graphic - G-Stroke",
}
//comp=app.project.activeItem
//idx 1-> group 1 root obj
//comp.layers[1](ae.contents)(1)(ae.zerogroup)(ae.stroke))
level=0;
function getVectorPaths(prop){
var paths=[]
for(var i=1;i<=prop.numProperties;i++){
var child=prop.property(i)
if(child.matchName==ae.pathGroup){
paths.push(child)
}
}
return paths
}
function dumpProps(item,lvl, obj){
lvl+=1
for(var i=1;i<=item.numProperties;i++){
var itemChild=item.property(i)
if(itemChild.matchName==ae.contents){
dumpProps(itemChild,lvl,obj)
}
if(itemChild.matchName==ae.zerogroup){
dumpProps(itemChild,lvl,obj)
}
if(itemChild.matchName==ae.group){
var groupData=["Group",{
applyMatrix:false,
//position:itemChild(ae.groupTrans)(ae.groupPos).value,
//pivot:itemChild(ae.groupTrans)(ae.groupAnchor).value,
children:[]
}];
obj[1].children.push(groupData)
dumpProps(itemChild,lvl,groupData)
}
//PATH ANIMATION
if(itemChild.matchName==ae.pathGroup){
var pathValue=itemChild(ae.path).value;
var pathObject=["Path",{
applyMatrix:true,
segments:pathValue.vertices.map(
function(x, idx){
return [x, pathValue.inTangents[idx], pathValue.outTangents[idx]]
}),
closed:pathValue.closed,
data:{animData:{}}
}];
if(itemChild(ae.path).numKeys>0){
pathObject[1].data.animData.segments=[]
for(var k=0;k<itemChild(ae.path).numKeys;k++){
var keyPathValue=itemChild(ae.path).keyValue(k+1)
var keyTime=itemChild(ae.path).keyTime(k+1)
//prop.keyInTemporalEase(1)[0]
var easeInX=1-(itemChild(ae.path).keyInTemporalEase(k+1)[0].influence/100)
var easeInY=1-(itemChild(ae.path).keyInTemporalEase(k+1)[0].influence*itemChild(ae.path).keyInTemporalEase(k+1)[0].speed/100)
var easeOutX=itemChild(ae.path).keyOutTemporalEase(k+1)[0].influence/100
var easeOutY=itemChild(ae.path).keyOutTemporalEase(k+1)[0].influence*itemChild(ae.path).keyOutTemporalEase(k+1)[0].speed/100
var segments=keyPathValue.vertices.map(
function(x, idx){
return [x, keyPathValue.inTangents[idx], keyPathValue.outTangents[idx]]
})
pathObject[1].data.animData.segments.push({time:keyTime,value:segments, easeIn:[easeInX,easeInY], easeOut:[easeOutX,easeOutY]})
}
}
try{
var stroke=itemChild.propertyGroup(1)(ae.stroke);
if(typeof stroke!="undefined"){
pathObject[1].strokeWidth=stroke(ae.strokeWidth).value
pathObject[1].strokeJoin=ae.strokeJoins[stroke(ae.strokeJoin).value];
pathObject[1].strokeCap=ae.strokeCaps[stroke(ae.strokeCap).value];
pathObject[1].strokeColor=stroke(ae.strokeColor).value
if(stroke(ae.strokeWidth).numKeys>0){
pathObject[1].data.animData.strokeWidth=[]
for(var k=0;k<stroke(ae.strokeWidth).numKeys;k++){
pathObject[1].data.animData.strokeWidth.push({
time:stroke(ae.strokeWidth).keyTime(k+1),
value:stroke(ae.strokeWidth).keyValue(k+1)
})
}
}
if(stroke(ae.strokeColor).numKeys>0){
pathObject[1].data.animData.strokeColor=[]
for(var k=0;k<stroke(ae.strokeColor).numKeys;k++){
pathObject[1].data.animData.strokeColor.push({
time:stroke(ae.strokeColor).keyTime(k+1),
value:stroke(ae.strokeColor).keyValue(k+1)
})
}
}
}
}
catch(err){}
try{
var fill=itemChild.propertyGroup(1)(ae.fill)(ae.fillColor);
if(typeof fill!="undefined"){
pathObject[1].fillColor=fill.value
if(fill.numKeys>0){
pathObject[1].data.animData.fillColor=[]
for(var k=0;k<fill.numKeys;k++){
pathObject[1].data.animData.fillColor.push({
time:fill.keyTime(k+1),
value:fill.keyValue(k+1)
})
}
}
}
}
catch(err){}
obj[1].children.push(pathObject)
}
if(itemChild.numProperties>0
&& itemChild.name!="Material Options"
&& itemChild.name!="Geometry Options"
&& itemChild.name!="Layer Styles"
){
//dumpProps(itemChild,lvl)
}
}
}
var waDuration=app.project.activeItem.workAreaDuration;
var waStart=app.project.activeItem.workAreaStart;
var compDuration=app.project.activeItem.duration;
var loopAnimation=(waStart+waDuration!=compDuration);
projectData=[]
//outputFile = File.saveDialog("Save the text file.");
//if (outputFile != null) {
//outputFile.open("w","TEXT","????");
var layers=app.project.activeItem.layers;
for(var i=0;i<layers.length;i++){
var item=layers[i+1]
var layerData=["Layer",{
applyMatrix:false,
position:[item(ae.transform).position.value[0],item(ae.transform).position.value[1]],
pivot:[item(ae.transform).anchorPoint.value[0],item(ae.transform).anchorPoint.value[1]],
children:[],
data:{
animData:{
position:[]
},
markers:[],
motionPath:[],
expression:'console.log(this)',
animLoop:loopAnimation,
animLength:waDuration,
playing:false,
startTime:0
}
}];
var markers = item.property("marker");
for (var i=1; i<=markers.numKeys; i++){
var keyName=markers.keyValue(i).comment || 'anim'+i
layerData[1].data.markers.push({
name:keyName,
start:markers.keyTime(i),
end:markers.keyTime(i)+markers.keyValue(i).duration,
duration:markers.keyValue(i).duration
})
}
dumpProps(item,0,layerData)
layerData[1].children.reverse()
var itemPos=item(ae.transform).position;
for(var k=0;k<itemPos.numKeys;k++){
var pt0=[itemPos.keyValue(Math.max(1,k))[0],itemPos.keyValue(Math.max(1,k))[1]];
var pt1=[itemPos.keyValue(k+1)[0],itemPos.keyValue(k+1)[1]];
var pt2=[itemPos.keyValue(Math.min(itemPos.numKeys,k+2))[0],itemPos.keyValue(Math.min(itemPos.numKeys,k+2))[1]];
var pt01=[pt1[0]-pt0[0],pt1[1]-pt0[1]]
var pt12=[pt2[0]-pt1[0],pt2[1]-pt1[1]]
var inDelta=Math.sqrt(
Math.pow(pt01[0],2)+
Math.pow(pt01[1],2)
)
var outDelta=Math.sqrt(
Math.pow(pt12[0],2)+
Math.pow(pt12[1],2)
)
var easeInX=1-itemPos.keyInTemporalEase(k+1)[0].influence/100
var easeInY=1-(itemPos.keyInTemporalEase(k+1)[0].influence*itemPos.keyInTemporalEase(k+1)[0].speed/inDelta/100)
var easeOutX=itemPos.keyOutTemporalEase(k+1)[0].influence/100
var easeOutY=itemPos.keyOutTemporalEase(k+1)[0].influence*itemPos.keyOutTemporalEase(k+1)[0].speed/outDelta/100
layerData[1].data.animData.position.push({
time:itemPos.keyTime(k+1),
value:[itemPos.keyValue(k+1)[0],itemPos.keyValue(k+1)[1]],
easeIn: [easeInX,easeInY],
easeOut: [easeOutX,easeOutY],
index:k
})
layerData[1].data.motionPath.push([
[itemPos.keyValue(k+1)[0],itemPos.keyValue(k+1)[1]],
[itemPos.keyInSpatialTangent(k+1)[0],itemPos.keyInSpatialTangent(k+1)[1]],
[itemPos.keyOutSpatialTangent(k+1)[0],itemPos.keyOutSpatialTangent(k+1)[1]],
])
}
projectData.push(layerData)
//outputFile.writeln(JSON.stringify(item.property("Contents")))
}
var animData=JSON.stringify(projectData);
prompt('Animation data as PaperJS project:', animData)
// var str="\"helloror\""
// system.callSystem("osascript -e 'tell application \"Finder\" to set the clipboard to \""+animData+"\"'")
// system.callSystem("osascript -e 'tell application \"Finder\" to set the clipboard to "+animData+"'")
//set the clipboard to "Some text"
//system.callSystem("osascript -e \'tell application \"Finder\"\rset the clipboard to \"" + animData +"\"\rend tell\'");
//outputFile.writeln(JSON.stringify(projectData))
//outputFile.close();
//outputFile.execute();
//}
include('http://eerojohannes.com/js/bezier-easing.js')
document.addEventListener('paste', function(evt) {
if(document.activeElement.nodeName!="TEXTAREA"){
var str=evt.clipboardData.getData('text/plain');
var jsoni=project.importJSON(str)
for(layer of jsoni){
if(layer.data.motionPath!=null){
if(typeof layer.data.motionPath.className=="undefined"){
layer.data.motionPath=new Path({
segments:layer.data.motionPath,
insert:false
})
}
}
}
}
})
function lerp(obj1,obj2,prop,fac){
return (obj2[prop]-obj1[prop])*fac+obj1[prop]
}
function lerpArray(arr1,arr2,fac){
return arr1.map( (obj,i,arr) => (arr2[i]-arr1[i])*fac+arr1[i] )
}
function lerpSegments(segs1,segs2,fac){
return segs1.map( (o,i,a) =>
segs1[i].map( (p,j,b) =>
[
(segs2[i][j][0]-segs1[i][j][0])*fac+segs1[i][j][0],
(segs2[i][j][1]-segs1[i][j][1])*fac+segs1[i][j][1]
]
)
)
}
Item.prototype.scrub=function(time, time2){
let propKeys=Object.keys(this.data.animData)
for(var i=0;i<propKeys.length;i++){
var prop=propKeys[i];
var keys=this.data.animData[prop]
//first key
if(time<keys[0].time){
let key1=keys[0]
this[prop]=key1.value
}
else if(time>=keys[0].time && time<keys[keys.length-1].time){
var currentKeys=keys.filter(
(obj,idx,arr) =>
time >= arr[Math.max(0,idx-1)].time
)
if(currentKeys.length>1){ //console.log('arrrr')
let key1=currentKeys[currentKeys.length-2]
let key2=currentKeys[currentKeys.length-1]
let delta=key2.time-key1.time
let pos=time-key1.time
let fac=Math.min(1,Math.max(0,pos/delta))
if(key1.hasOwnProperty('easeIn') && key1.hasOwnProperty('easeOut') &&
key2.hasOwnProperty('easeIn') && key2.hasOwnProperty('easeOut')){
var ease=BezierEasing(key1.easeOut[0],key1.easeOut[1],key2.easeIn[0],key2.easeIn[1])
var eased=ease(fac)
}
else {
var eased=fac
}
if(prop=="position"){
if(this.className=="Layer"){
let v=lerpArray(key1.value,key2.value,eased)
/*
let curveLength=this.data.motionPath.curves[key1.index].length
let curveOffset=this.data.motionPath.getOffsetOf(this.data.motionPath.curves[key1.index].point1)
this.position=this.data.motionPath.getPointAt(ease(fac)*curveLength+curveOffset);
*/
this.position=v
}
if(this.className=="Group"){
let v=lerpArray(key1.value,key2.value,eased)
this.position=v
}
}
else if(prop=="fillColor" || prop=="strokeColor"){
let v=lerpArray(key1.value,key2.value,eased)
this[prop]=v
}
else if(prop=="segments"){
let v=lerpSegments(key1.value,key2.value,eased)
this[prop]=v
}
else {
let v=lerp(key1,key2,'value',eased)
this[prop]=v
}
}
}
//last key
else{
let key1=keys[keys.length-1]
this[prop]=key1.value
//if(this.className=="Layer" && prop=="position"){
}
}
}
Item.prototype.multiScrub=function(time,time2,midPoint){
let propKeys=Object.keys(this.data.animData)
for(var i=0;i<propKeys.length;i++){
var prop=propKeys[i];
var keys=this.data.animData[prop]
//console.log(time,time2)
if(time>=keys[0].time && time<keys[keys.length-1].time){
var currentKeys=keys.filter(
(obj,idx,arr) =>
time >= arr[Math.max(0,idx-1)].time
)
var currentKeysB=keys.filter(
(obj,idx,arr) =>
time2 >= arr[Math.max(0,idx-1)].time
)
if(currentKeys.length>1){
let key1=currentKeys[currentKeys.length-2]
let key2=currentKeys[currentKeys.length-1]
let delta=key2.time-key1.time
let pos=time-key1.time
let fac=Math.min(1,Math.max(0,pos/delta))
if(key1.hasOwnProperty('easeIn') && key1.hasOwnProperty('easeOut') &&
key2.hasOwnProperty('easeIn') && key2.hasOwnProperty('easeOut')){
var ease=BezierEasing(key1.easeOut[0],key1.easeOut[1],key2.easeIn[0],key2.easeIn[1])
var eased=ease(fac)
}
else {
var eased=fac
}
let key1B=currentKeysB[currentKeysB.length-2]
let key2B=currentKeysB[currentKeysB.length-1]
let deltaB=key2B.time-key1B.time
let posB=time2-key1B.time
let facB=Math.min(1,Math.max(0,posB/deltaB))
if(key1B.hasOwnProperty('easeIn') && key1B.hasOwnProperty('easeOut') &&
key2B.hasOwnProperty('easeIn') && key2B.hasOwnProperty('easeOut')){
var easeB=BezierEasing(key1B.easeOut[0],key1B.easeOut[1],key2B.easeIn[0],key2B.easeIn[1])
var easedB=easeB(facB)
}
else {
var easedB=facB
}
if(prop=="segments"){
let v=lerpSegments(key1.value,key2.value,eased)
let v2=lerpSegments(key1B.value,key2B.value,easedB)
let v3=lerpSegments(v,v2,midPoint)
this[prop]=v3
}
}
}
}
}
paths=0
numKeys=3
playing=true
view.startTime=0
view.time=0
view.playhead=0
view.timelineLength=3.0
function onFrame(e){
view.time=e.time
view.playhead=(e.time-view.startTime)%view.timelineLength;
var animLayers=project.layers.filter( layer => layer.data.playing==true)
for(var layer of animLayers){
var layerTime=(
(view.time-layer.data.startTime)%layer.data.animLength
)
+layer.data.animStart;
var layerTimeB=(
(view.time-layer.data.startTime)%layer.data.animLengthB
)
+layer.data.animStartB;
for(var item of layer.children){
if(item.data.animData!=null){
item.multiScrub(layerTime,layerTimeB)
}
if(item.children!=null){
for(var subitem of item.children){
if(subitem.data.animData!=null){
subitem.scrub(layerTime)
//subitem.multiScrub(layerTime,layerTimeB,mousePos.y/view.viewSize.height)
}
}
}
if(layer.data.animData!=null){
//layer.multiScrub(layerTime)
}
/*
if(layer.data.expression!=null){
var result=function(str){return eval(str)}
.call(layer,layer.data.expression);
}
*/
}
}
}
var t=new Tool()
t.on({
mousedown(){
var it=project.layers[0];
it.data.playing=true
it.data.startTime=view.time
//console.log(it.data)
},
mousemove(e){
mousePos=e.point
},
mouseup(){
project.layers[0].data.playing=false
},
keydown(e){
var it=project.layers[0];
if(!e.modifiers.command){
if(!isNaN(parseInt(e.key))){
var num=parseInt(e.key)-1
if(it.data.markers[num]!=null){
it.data.startTime=view.time
it.data.animStart=it.data.markers[num].start
it.data.animEnd=it.data.markers[num].end
it.data.animLength=it.data.markers[num].duration
if(num<2){
it.data.animStartB=it.data.markers[num+1].start
it.data.animEndB=it.data.markers[num+1].end
it.data.animLengthB=it.data.markers[num+1].duration
}
it.data.playing=true
}
}
/*
var m=it.data.markers.filter(
x => x.name=="walk"
)[0]
*/
}
}
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment