Created
September 2, 2021 09:04
-
-
Save eeropic/4c4871e3224e4304114da28552992803 to your computer and use it in GitHub Desktop.
scriptui-svg alpha
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// @target aftereffects | |
// altKeyPressed(win), optKeyPressed(mac) ctrlKeyPressed, cmdKeyPressed(mac), shiftKeyPressed, capsLockKeyPressed, numLockKeyPressed | |
// leftButtonPressed, middleButtonPressed, rightButtonPressed, mouseOver, hasFocus, | |
//encapsulate the script in a function to avoid global variables | |
(function (thisObj) { | |
default xml namespace = "http://www.w3.org/2000/svg"; | |
var xmlString = """ | |
<!-- Generator: Adobe Illustrator 23.1.1, SVG Export Plug-In --> | |
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="842px" | |
height="487.92px" viewBox="0 0 842 487.92" enable-background="new 0 0 842 487.92" xml:space="preserve"> | |
<defs> | |
</defs> | |
<line fill="none" stroke="#FFE08E" stroke-width="16" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" x1="48" y1="183.92" x2="88" y2="183.92"/> | |
<polygon fill="#FF7C97" points="8,73.92 48,93.92 48,53.92 8,33.92 "/> | |
<polygon fill="#5F9ED6" points="88,73.92 48,93.92 48,53.92 88,33.92 "/> | |
<polygon fill="#FFE08E" points="88,33.92 48,53.92 8,33.92 48.56,13.3 "/> | |
<circle fill="#FFFFFF" cx="48" cy="53.92" r="30"/> | |
<line fill="none" stroke="#FF7C97" stroke-width="16" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" x1="48" y1="123.92" x2="8" y2="183.92"/> | |
<line fill="none" stroke="#5F9ED6" stroke-width="16" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" x1="48" y1="123.92" x2="88" y2="183.92"/> | |
<line fill="none" stroke="#FFE08E" stroke-width="16" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" x1="8" y1="183.92" x2="48" y2="183.92"/> | |
<circle fill="#FF7C97" cx="118" cy="53.92" r="20"/> | |
<ellipse fill="#5F9ED6" cx="188" cy="53.92" rx="40" ry="20"/> | |
<rect x="148" y="33.92" fill="#FFE08E" width="40" height="40"/> | |
<polyline fill="#FF7C97" stroke="#5F9ED6" stroke-width="16" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" points=" | |
128,153.92 168,93.92 208,153.92 168,153.92 "/> | |
<polygon fill="none" stroke="#FF7C97" stroke-width="16" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" points=" | |
228,93.92 228,133.92 248,113.92 268,133.92 268,93.92 "/> | |
<polygon fill="none" stroke="#FF7C97" stroke-width="16" stroke-linecap="square" stroke-miterlimit="10" points="248,13.92 | |
248,53.92 268,43.92 288,53.92 288,13.92 "/> | |
<circle fill="none" stroke="#FF7C97" stroke-width="8" stroke-linecap="square" stroke-miterlimit="10" cx="138" cy="213.92" r="20"/> | |
<circle fill="#5F9ED6" stroke="#FF7C97" stroke-width="8" stroke-linecap="square" stroke-miterlimit="10" cx="198" cy="213.92" r="20"/> | |
<ellipse fill="#5F9ED6" stroke="#FF7C97" stroke-width="8" stroke-linecap="square" stroke-miterlimit="10" cx="258" cy="243.92" rx="20" ry="50"/> | |
<ellipse fill="#FF7C97" stroke="#FFFFFF" stroke-width="8" stroke-linecap="square" stroke-miterlimit="10" cx="278" cy="243.92" rx="20" ry="50"/> | |
<rect x="118" y="253.92" fill="none" stroke="#5F9ED6" stroke-width="8" stroke-linecap="square" stroke-miterlimit="10" width="40" height="40"/> | |
<rect x="178" y="253.92" fill="#FF7C97" stroke="#5F9ED6" stroke-width="8" stroke-linecap="square" stroke-miterlimit="10" width="40" height="40"/> | |
<polygon fill="#5F9ED6" points="68,213.92 28,233.92 38,263.92 58,243.92 88,283.92 88,243.92 "/> | |
<path fill="none" stroke="#5F9ED6" stroke-width="16" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" d=" | |
M358,103.92h-30l-30,40c0,0-10,40,30,50c0,0,10-40,40-40"/> | |
<path fill="none" stroke="#5F9ED6" stroke-width="16" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" d=" | |
M377.56,17.56c-1.97-6.28-9.45-8.91-16.01-9.43c-4.23-0.34-8.7-0.13-12.39,1.97c-6.39,3.63-8.66,12.35-6.37,19.33 | |
s8.22,12.26,14.78,15.57s13.81,4.93,20.9,6.85c9.78,2.64,19.4,5.88,28.79,9.68c6.37,2.58,12.77,5.52,17.77,10.23 | |
s8.46,11.55,7.56,18.37c-0.59,4.45-2.99,8.56-6.26,11.64c-5.77,5.45-14.11,7.74-22,6.98c-7.9-0.76-15.34-4.35-21.55-9.3"/> | |
<path fill="none" stroke="#5F9ED6" stroke-width="8" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" d=" | |
M408,303.92h50v-50h40c0,0,0,30,40,30s40-30,40-30h50v90c-50,0-50,40-50,40h-50c0,0-10-40-40-40s-50,40-50,40c-30,0-40-30-40-30 | |
c-30,0-40-30-40-30"/> | |
<path fill="none" stroke="#5F9ED6" stroke-width="8" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" d=" | |
M558,143.92c50,0,50,60,100,60s50-60,100-60s30,70,80,70"/> | |
<polyline fill="none" stroke="#5F9ED6" stroke-width="8" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" points=" | |
68,373.92 58,383.92 68,403.92 58,413.92 "/> | |
<polyline fill="none" stroke="#FF7C97" stroke-width="8" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" points=" | |
88,373.92 78,393.92 88,413.92 "/> | |
<path fill="none" stroke="#FF7C97" stroke-width="8" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" d=" | |
M108,433.92v-60"/> | |
<path fill="none" stroke="#FF7C97" stroke-width="8" stroke-linecap="square" stroke-miterlimit="10" d="M128,373.92 | |
c11.05,0,20,8.95,20,20c0,11.05-8.95,20-20,20l20,20"/> | |
<path fill="#5F9ED6" stroke="#FF7C97" stroke-width="8" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" d=" | |
M208,433.92l-20-20c11.05,0,20-8.95,20-20c0-11.05-8.95-20-20-20h-20v60H208z"/> | |
<path fill="none" stroke="#FF7C97" stroke-width="8" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" d=" | |
M248,483.92c60-60-10-90,50-90s0,70,60,70"/> | |
</svg> | |
""" | |
var utils = { | |
hexToArray: function(hexString){ | |
var hexColor = hexString.toString().replace('#', ''), rgbArray = [0,0,0,1]; | |
for(var i=0; i<3; i++) rgbArray[i] = parseInt(hexColor.slice(i*2, (i+1)*2), 16) / 255; | |
return (hexColor.length > 4 ? rgbArray : null); | |
}, | |
dashToCamel: function(str){ | |
return str.replace(/\W+(.)/g, function (x, chr){return chr.toUpperCase()}); | |
}, | |
getCoords: function(str){ | |
var coords = str.split(/[, ]/), vals = []; | |
for(var i = 0; i < coords.length; i = i + 2){ | |
var x = parseInt(coords[i]), y = parseInt(coords[i+1]); | |
if(!isNaN(x) && !isNaN(y)) vals.push([x,y]) | |
} | |
return vals | |
}, | |
importSVG: function(fileName) { | |
var basePath = new File($.fileName).path; | |
var svgFile = File(basePath + "/" + fileName) | |
svgFile.encoding = "UTF-8" | |
if (svgFile.open('r')) { | |
svgText = svgFile.read(); | |
svgFile.close(); | |
return svgText | |
} | |
}, | |
drawCircles: function(ctx,pts,r,fillColor){ | |
for(var i = 0; i < pts.length; i++){ | |
ctx.newPath() | |
ctx.ellipsePath(pts[i][0]-r/2,pts[i][1]-r/2,r,r); | |
ctx.fillPath(ctx.newBrush(ctx.BrushType.SOLID_COLOR, fillColor)); | |
} | |
}, | |
cmdLength: {a:7, c:6, h:1, l:2, m:2, q:4, s:4, t:2, v:1, z:0}, | |
segPattern: /([astvzqmhlc])([^astvzqmhlc]*)/ig, | |
numPattern: /-?[0-9]*\.?[0-9]+(?:e[-+]?\d+)?/ig, | |
parseVals: function(args){ | |
var nums = args.match(this.numPattern) | |
if(nums != null){ | |
for(var n in nums) nums[n] = parseFloat(nums[n]); | |
} | |
return nums || [] | |
}, | |
attributeKeys: "x,y,x1,y1,x2,y2,cx,cy,width,height,r,rx,ry,points,fill,stroke,stroke-width,stroke-linejoin,stroke-linecap,stroke-miterlimit".split(","), | |
bez2:function(w0,w1,w2,t){ | |
var t2=t*t, mt=1-t, mt2=mt*mt; | |
return w0*mt2 + w1*2*mt*t + w2*t2; | |
}, | |
bez3:function(w0,w1,w2,w3,t){ | |
var t2=t*t, t3=t2*t, mt=1-t, mt2=mt*mt, mt3=mt2*mt; | |
return w0*mt3 + w1*3*mt2*t + w2*3*mt*t2 + w3*t3; | |
}, | |
lerp: function(a,b,t){ | |
return (1-t) * a + t * b | |
}, | |
midPoint: function(p1,p2){ | |
return [ ( p1[0] + p2[0] ) / 2, ( p1[1] + p2[1] ) / 2] | |
} | |
} | |
SVG_STEPS = 24 | |
function pathDataToArray(path) { | |
var data = [] | |
path.replace(utils.segPattern, function(_, cmd, args){ | |
var type = cmd.toLowerCase() | |
args = utils.parseVals(args) | |
if (type == 'm' && args.length > 2) { | |
data.push([cmd].concat(args.splice(0, 2))) | |
type = 'l' | |
cmd = cmd == 'm' ? 'l' : 'L' | |
} | |
while (true) { | |
if (args.length == utils.cmdLength[type]) { | |
args.unshift(cmd) | |
return data.push(args) | |
} | |
if (args.length < cmdLength[type]) throw new Error('malformed path data') | |
data.push([cmd].concat(args.splice(0, cmdLength[type]))) | |
} | |
}) | |
return data | |
} | |
$.write("kek") | |
function createUI(thisObj) { | |
//xmlString = importSVG("testi.svg") | |
mainPalette = (thisObj instanceof Panel) ? thisObj : new Window('palette', "Testihomma", [100, 100, 300, 300], {resizeable: true}); | |
mainPalette.alignChildren = "fill"; | |
var content = mainPalette.add('group'); | |
content.orientation = 'row'; | |
content.alignment = 'left'; | |
content.svg = new XML(xmlString); | |
content.svgElements = content.svg.children(); | |
content.minimumSize = [parseFloat(content.svg.@width),parseFloat(content.svg.@height)] | |
content.onDraw = function(){ | |
var ctx = this.graphics | |
var items = this.svgElements; | |
for(var i = 0; i < items.length(); i++){ | |
var a = {} | |
var item = this.svgElements[i]; | |
for(var k = 0; k < utils.attributeKeys.length; k++){ | |
var keyName = utils.attributeKeys[k] | |
var val = item.attribute(keyName).toString(); | |
if(val[0] == "#" || val == "none") a[utils.dashToCamel(keyName)] = utils.hexToArray(val) | |
else a[utils.dashToCamel(keyName)] = isNaN(parseFloat(val)) ? val : parseFloat(val); | |
} | |
if (item.localName() == "polyline" || item.localName() == "polygon") a.points = utils.getCoords(item.@points); | |
var roundJoin = (a.strokeLinejoin == "round") | |
var roundCap = (a.strokeLinecap == "round") | |
var tagName = item.localName() | |
with(a){ | |
switch(tagName) { | |
case "defs": break; | |
case "path": | |
var pathString = item.attribute("d").toString() | |
var pathData=pathDataToArray(pathString) | |
if(roundCap){ | |
var cmd = pathData[0][0] | |
var vals = pathData[0].slice(1) | |
utils.drawCircles(ctx,[[vals[0],vals[1]]],strokeWidth,stroke) | |
} | |
for(var j = 0; j < pathData.length; j++){ | |
var cmd = pathData[j][0] | |
var vals = pathData[j].slice(1) | |
switch(cmd){ | |
case "M": ctx.newPath(); ctx.moveTo(vals[0],vals[1]); | |
break; | |
case "m": | |
break; | |
case "L": case "l": | |
var x = cmd == "l" ? ctx.currentPoint[0] + vals[0] : vals[0]; | |
var y = cmd == "l" ? ctx.currentPoint[1] + vals[1] : vals[1]; | |
ctx.lineTo(x,y) | |
break; | |
case "H": case "h": | |
var x = cmd == "h" ? ctx.currentPoint[0]+vals[0] : vals[0] | |
ctx.lineTo(x,ctx.currentPoint[1]) | |
break; | |
case "V": case "v": | |
var y = cmd == "v" ? ctx.currentPoint[1]+vals[0] : vals[0] | |
ctx.lineTo(ctx.currentPoint[0], y) | |
break; | |
case "C": case "c": case "S": case "s": | |
var x0 = ctx.currentPoint[0] | |
var y0 = ctx.currentPoint[1] | |
var addX = cmd == cmd.toLowerCase() ? x0 : 0; | |
var addY = cmd == cmd.toLowerCase() ? y0 : 0; | |
var steps = SVG_STEPS; | |
var shiftIndex = 0 | |
if(cmd == "S" || cmd == "s"){ | |
var x1 = 2 * x0 - ctx.currentInTangent[0] | |
var y1 = 2 * y0 - ctx.currentInTangent[1] | |
shiftIndex = 2 | |
} | |
else { | |
var x1 = vals[0] + addX | |
var y1 = vals[1] + addY | |
} | |
var x2 = vals[2-shiftIndex] + addX | |
var y2 = vals[3-shiftIndex] + addY | |
var x3 = vals[4-shiftIndex] + addX | |
var y3 = vals[5-shiftIndex] + addY | |
ctx.currentInTangent = [x2,y2] | |
var p0 = [x0,y0], p1 = [x1,y1], p2 = [x2,y2], p3 = [x3,y3]; | |
var p4 = utils.midPoint(p0, p1); | |
var p5 = utils.midPoint(p1, p2); | |
var p6 = utils.midPoint(p2, p3); | |
var p7 = utils.midPoint(p4, p5); | |
var p8 = utils.midPoint(p5, p6); | |
var p9 = utils.midPoint(p7, p8); | |
// TODO: adaptive subdivision, let's try simple 50% split now | |
// as extreme handles require too many samples to appear correctly/smooth | |
var splitCurves = [ [p0, p4, p7, p9], [p9, p8, p6, p3] ] | |
for(var c=0;c < splitCurves.length;c++){ | |
var curve = splitCurves[c] | |
for(var s=0;s<SVG_STEPS;s++){ | |
var pos = s / SVG_STEPS; | |
var x = utils.bez3(curve[0][0],curve[1][0],curve[2][0],curve[3][0],pos) | |
var y = utils.bez3(curve[0][1],curve[1][1],curve[2][1],curve[3][1],pos) | |
ctx.lineTo(x,y) | |
} | |
} | |
break; | |
case "Z": case "z": | |
ctx.closePath(); | |
break; | |
default: | |
break; | |
/* | |
TODO: | |
quadratic "Q" "q" smooth quadratic "T" "t" elliptical arc "A" "a" | |
*/ | |
} | |
if(j == pathData.length-1){ | |
var tempPath = ctx.currentPath | |
//utils.drawCircles(ctx,[ctx.currentPoint],strokeWidth,stroke) | |
ctx.currentPath = tempPath | |
} | |
} | |
break; | |
case "rect": ctx.newPath(); ctx.rectPath(x||0 , y||0, width, height); break; | |
case "circle": | |
case "ellipse": ctx.newPath(); ctx.ellipsePath(cx-(r||rx), cy-(r||ry), (r||rx)*2, (r||ry)*2); break; | |
case "line": | |
if(roundCap) utils.drawCircles(ctx, [[x1, y1],[x2,y2]], strokeWidth, stroke); | |
ctx.newPath();ctx.moveTo(x1,y1);ctx.lineTo(x2,y2); | |
break; | |
case "polyline": | |
case "polygon": | |
if(roundCap) utils.drawCircles(ctx, [points[0],points[points.length-1]], strokeWidth, stroke); | |
if(roundJoin){ | |
utils.drawCircles(ctx,points.slice(1,-1),strokeWidth,stroke); | |
for(var j = 0; j < points.length; j++){ | |
if(tagName == "polyline" && j == points.length - 1) break; | |
ctx.newPath(); | |
ctx.moveTo(points[j][0],points[j][1]); | |
ctx.lineTo(points[ (j+1) % points.length ][0], points[ (j+1) % points.length ][1]); | |
ctx.strokePath(ctx.newPen(ctx.PenType.SOLID_COLOR, stroke, strokeWidth)); | |
} | |
} | |
else { | |
ctx.newPath(); | |
ctx.moveTo(points[0][0],points[0][1]); | |
for(var j = 1; j < points.length; j++) ctx.lineTo(points[j][0],points[j][1]); | |
if(tagName=="polygon")ctx.closePath(); | |
} | |
default: | |
} | |
if(fill) ctx.fillPath(ctx.newBrush(ctx.BrushType.SOLID_COLOR, fill)); | |
if(stroke) ctx.strokePath(ctx.newPen(ctx.PenType.SOLID_COLOR, stroke, strokeWidth)); | |
} | |
} | |
} | |
mainPalette.layout.layout(true); | |
return mainPalette; | |
} | |
var testPanel = createUI(thisObj); | |
if (testPanel != null && testPanel instanceof Window) { | |
testPanel.show(); | |
} | |
})(this); |
Author
eeropic
commented
Sep 2, 2021
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment