Snippets

renderTom AE Script: Shape to Circle

Updated by renderTom -

File Shape to Circle.js Modified

  • Ignore whitespace
  • Hide word diff
     	Morpth any shape into a circle.
 
 	Change log:
+		v1.1.1 - 2017 05 20
+			- Disabled Custom Radius drop down menu upon loading script as dockable panel.
+
 		v1.1 - 2017 05 14
 			- Center of the circle is calculated from shapes bounding box.
 			- Adds more radius selection options: "Bounding Box Height" and "Bounding Box Width"
 
 		var ddRadius = win.add("dropdownlist", undefined, ["Average from Center", "Bounding Box Height", "Bounding Box Width", "Maximum from Center", "Minimum from Center"]);
 		ddRadius.selection = 0;
+		ddRadius.enabled = false;
 
 		var button = win.add("button", undefined, "Do It!");
 		button.onClick = function() {
 	};
 
 	function getMagicNumber() {
-		// http://stackoverflow.com/questions/1734745/how-to-create-circle-with-bézier-curves
+		// http://stackoverflow.com/questions/1734745/how-to-create-circle-with-bézier-curves
 		// https://people-mozilla.org/~jmuizelaar/Riskus354.pdf
 		return (4 / 3) * Math.tan(Math.PI / (2 * 4));
 	}
Updated by renderTom -

File Shape to Circle.js Modified

  • Ignore whitespace
  • Hide word diff
 /**********************************************************************************************
-    Shape to Circle.jsx
+    Shape to Circle v.1.1
     Copyright (c) 2017 Tomas Šinkūnas. All rights reserved.
     www.rendertom.com
 
     Description:
     	Morpth any shape into a circle.
 
-        This script is provided "as is," without warranty of any kind, expressed
-        or implied. In no event shall the author be held liable for any damages
-        arising in any way from the use of this script.
+	Change log:
+		v1.1 - 2017 05 14
+			- Center of the circle is calculated from shapes bounding box.
+			- Adds more radius selection options: "Bounding Box Height" and "Bounding Box Width"
+
+		v1.0 - 2017 05 10
+			- Initial release
+
+	Released as open-source under the MIT license:
+
+		The MIT License (MIT)
+		Copyright (c) 2017 Tomas Šinkūnas www.renderTom.com
+		Permission is hereby granted, free of charge, to any person obtaining a copy of this
+		software and associated documentation files (the "Software"), to deal in the Software
+		without restriction, including without limitation the rights to use, copy, modify, merge,
+		publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
+		to whom the Software is furnished to do so, subject to the following conditions:
+
+		The above copyright notice and this permission notice shall be included in all copies or
+		substantial portions of the Software.
+
+		THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+		INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+		PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+		FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+		OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+		DEALINGS IN THE SOFTWARE.
+
 **********************************************************************************************/
 
 (function(thisObj) {
 		var etCustomRadius = grpCustomRadius.add("edittext", undefined, "200");
 		etCustomRadius.alignment = ["fill", "top"];
 
-		var ddRadius = win.add("dropdownlist", undefined, ["Average from center", "Maximum from center", "Minimum from center"])
+		var ddRadius = win.add("dropdownlist", undefined, ["Average from Center", "Bounding Box Height", "Bounding Box Width", "Maximum from Center", "Minimum from Center"]);
 		ddRadius.selection = 0;
 
 		var button = win.add("button", undefined, "Do It!");
 			} else {
 				main(ddRadius.selection.text);
 			}
-		}
+		};
+
 		win.onShow = function() {
 			checkCustomRadius.onClick();
-		}
+		};
+
 		win.onResizing = win.onResize = function() {
 			this.layout.resize();
 		};
 			var selectedLayers = getSelectedLayers(composition);
 			var allShapeProperties = getAllShapeProperties(selectedLayers);
 			if (allShapeProperties.length === 0)
-				throw new Error ("Please select Path property")
+				throw new Error("Please select Path property.");
+
+			var pathValue, circleShape,
+				boundingBox = [],
+				centerCoordinates = [],
+				circlePoints = [];
 
 			app.beginUndoGroup("Shape to Circle");
 
-			for (var j = 0, jl = allShapeProperties.length; j < jl; j ++) {
-				var pathValue = allShapeProperties[j].value;
-				var vertices = pathValue.vertices;
-				var centerCoordinates = getAverageInArray(vertices);
+			for (var j = 0, jl = allShapeProperties.length; j < jl; j++) {
+				pathValue = allShapeProperties[j].value;
+				boundingBox = getBoundingBox(pathValue);
+				centerCoordinates = averageInArray(boundingBox);
 
 				if (isNaN(radiusFromUI)) {
-					distanceArray = calculateDistancesFromCenter(centerCoordinates, vertices);
-					if (radiusFromUI.match("Average")) 	  	radius = getAverageInArray(distanceArray);
+					distanceArray = getDistanceFromCenter(centerCoordinates, pathValue.vertices);
+					if (radiusFromUI.match("Average")) radius = averageInArray(distanceArray);
+					else if (radiusFromUI.match("Width")) radius = (boundingBox[1][0] - boundingBox[0][0]) / 2;
+					else if (radiusFromUI.match("Height")) radius = (boundingBox[1][1] - boundingBox[0][1]) / 2;
 					else if (radiusFromUI.match("Maximum")) radius = Math.max.apply(null, distanceArray);
 					else if (radiusFromUI.match("Minimum")) radius = Math.min.apply(null, distanceArray);
 				}
 
-				var circlePoints = pointsToCircle(centerCoordinates, radius, vertices);
+				circlePoints = pointsToCircle(centerCoordinates, radius, pathValue);
 
-				var circleShape = new Shape();
-					circleShape.vertices = circlePoints.vertices;
-					circleShape.inTangents = circlePoints.inTangents;
-					circleShape.outTangents = circlePoints.outTangents;
-					circleShape.closed = pathValue.closed;
+				circleShape = new Shape();
+				circleShape.vertices = circlePoints.vertices;
+				circleShape.inTangents = circlePoints.inTangents;
+				circleShape.outTangents = circlePoints.outTangents;
+				circleShape.closed = pathValue.closed;
 
 				allShapeProperties[j].numKeys === 0
 					? allShapeProperties[j].setValue(circleShape)
 					: allShapeProperties[j].setValueAtTime(composition.time, circleShape);
 
 			}
-
 			app.endUndoGroup();
-
 		} catch (e) {
 			alert(e.message);
 		}
-
 	}
 
+// -----------------------------------------------
 	function getActiveComposition() {
 		var composition = app.project.activeItem;
 		if (!composition || !(composition instanceof CompItem))
 			throw new Error("Please select composition first.");
 		return composition;
-	}
+	};
 
-	function getSelectedLayers(composition) {
-		var selectedLayers = composition.selectedLayers;
-		if (selectedLayers.length === 0)
-			throw new Error("Please select layer first.");
-		return selectedLayers;
-	}
+	function getAllShapeProperties(selectedLayers) {
+		var allShapeProperties = [];
+		var layerShapeProperties = [];
+		for (var i = 0, il = selectedLayers.length; i < il; i++) {
+			layerShapeProperties = getLayerShapeProperties(selectedLayers[i]);
+			if (layerShapeProperties.length > 0) {
+				allShapeProperties = allShapeProperties.concat(layerShapeProperties);
+			}
+		}
+		return allShapeProperties;
+	};
 
 	function getLayerShapeProperties(layer) {
 		var shapeProperties = [];
 		var prop;
 		var selectedProperties = layer.selectedProperties;
-		for (var i = 0, il = selectedProperties.length; i < il; i ++) {
+		for (var i = 0, il = selectedProperties.length; i < il; i++) {
 			prop = selectedProperties[i];
-			
+
 			if (prop.matchName === "ADBE Vector Shape - Group") {
 				if (prop.property("ADBE Vector Shape").selected === true) {
 					continue
 			}
 		}
 		return shapeProperties;
-	}
+	};
 
-	function getAllShapeProperties(selectedLayers) {
-		var allShapeProperties = [];
-		var layerShapeProperties = [];
-		for (var i = 0, il = selectedLayers.length; i < il; i ++) {
-			layerShapeProperties = getLayerShapeProperties(selectedLayers[i]);
-			if (layerShapeProperties.length > 0) {
-				allShapeProperties = allShapeProperties.concat(layerShapeProperties);
+	function getSelectedLayers(composition) {
+		var selectedLayers = composition.selectedLayers;
+		if (selectedLayers.length === 0)
+			throw new Error("Please select layer first.");
+		return selectedLayers;
+	};
+
+// -----------------------------------------------
+	function averageInArray(verticesArray) {
+		return sumArray(verticesArray) / verticesArray.length;
+	};
+
+	function clockwiseDirection(vertices) {
+		// http://stackoverflow.com/questions/14505565/detect-if-a-set-of-points-in-an-array-that-are-the-vertices-of-a-complex-polygon
+		var polygonArea = getPoligonArea(vertices);
+		return polygonArea > 0;
+	};
+
+	function distanceBetweenPoints(point1, point2) {
+		var deltaX = point1[0] - point2[0];
+		var deltaY = point1[1] - point2[1];
+		var distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
+		return distance;
+	};
+
+	function getAngle(point1, point2) {
+		var distanceX = point2[0] - point1[0];
+		var distanceY = point2[1] - point1[1];
+		var theta = Math.atan2(distanceY, distanceX);
+		return theta;
+	};
+
+	function getBoundingBox(pathValue) {
+		var bezierPoints = toBezierPoints(pathValue),
+			xValues = [],
+			yValues = [],
+			segmentBoundingBox = {};
+
+		for (var i = 0, il = bezierPoints.length; i < il; i++) {
+			segmentBoundingBox = getBoundsOfCurve(bezierPoints[i][0], bezierPoints[i][1], bezierPoints[i][2], bezierPoints[i][3]);
+			xValues.push(segmentBoundingBox.left);
+			xValues.push(segmentBoundingBox.right);
+			yValues.push(segmentBoundingBox.top);
+			yValues.push(segmentBoundingBox.bottom);
+		}
+
+		return [
+			[Math.min.apply(null, xValues), Math.min.apply(null, yValues)],
+			[Math.max.apply(null, xValues), Math.max.apply(null, yValues)]
+		];
+	};
+
+	function getBoundsOfCurve(p1, p2, p3, p4) {
+		// http://stackoverflow.com/questions/2587751/an-algorithm-to-find-bounding-box-of-closed-bezier-curves
+		var tvalues = [],
+			xvalues = [],
+			yvalues = [],
+			a, b, c, t, roots;
+
+		for (var i = 0; i < 2; ++i) {
+			if (i === 0) {
+				b = 6 * p1[0] - 12 * p2[0] + 6 * p3[0];
+				a = -3 * p1[0] + 9 * p2[0] - 9 * p3[0] + 3 * p4[0];
+				c = 3 * p2[0] - 3 * p1[0];
+			} else {
+				b = 6 * p1[1] - 12 * p2[1] + 6 * p3[1];
+				a = -3 * p1[1] + 9 * p2[1] - 9 * p3[1] + 3 * p4[1];
+				c = 3 * p2[1] - 3 * p1[1];
 			}
+
+			if (Math.abs(a) < 1e-12) { // Numerical robustness
+				if (Math.abs(b) < 1e-12) continue; // Numerical robustness
+
+				t = -c / b;
+				if (0 < t && t < 1) tvalues.push(t);
+				continue;
+			}
+
+			// Solve Quadratic Equation
+			roots = quadraticEquation(a, b, c);
+			if (!roots) continue;
+			if (0 < roots.root1 && roots.root1 < 1) tvalues.push(roots.root1);
+			if (0 < roots.root2 && roots.root2 < 1) tvalues.push(roots.root2);
+
 		}
-		return allShapeProperties;
+
+		var u, j = tvalues.length;
+		while (j--) {
+			t = tvalues[j];
+			u = 1 - t;
+			xvalues[j] = (u * u * u * p1[0]) + (3 * u * u * t * p2[0]) + (3 * u * t * t * p3[0]) + (t * t * t * p4[0]);
+			yvalues[j] = (u * u * u * p1[1]) + (3 * u * u * t * p2[1]) + (3 * u * t * t * p3[1]) + (t * t * t * p4[1]);
+		}
+
+		xvalues.push(p1[0], p4[0]);
+		yvalues.push(p1[1], p4[1]);
+
+		return {
+			left: Math.min.apply(null, xvalues),
+			top: Math.min.apply(null, yvalues),
+			right: Math.max.apply(null, xvalues),
+			bottom: Math.max.apply(null, yvalues),
+		};
+	};
+
+	function getDistanceFromCenter(center, vertices) {
+		var distanceFromCenter;
+		var distanceArray = [];
+		for (var i = 0, il = vertices.length; i < il; i++) {
+			distanceFromCenter = distanceBetweenPoints(center, vertices[i]);
+			distanceArray.push(distanceFromCenter);
+		}
+		return distanceArray;
+	};
+
+	function getHandleLength(numPoints, radius) {
+		return radius * getMagicNumber() * 4 / numPoints;
+	};
+
+	function getMagicNumber() {
+		// http://stackoverflow.com/questions/1734745/how-to-create-circle-with-bézier-curves
+		// https://people-mozilla.org/~jmuizelaar/Riskus354.pdf
+		return (4 / 3) * Math.tan(Math.PI / (2 * 4));
 	}
 
-	function pointsToCircle(center, radius, pointsArray) {
-		var angle, anchor, handle;
+	function getPointCoordinates(center, radius, angle) {
+		return [
+			center[0] + radius * Math.cos(angle),
+			center[1] + radius * Math.sin(angle)
+		]
+	};
 
-		var numPoints = pointsArray.length;
+	function getPoligonArea(vertices) {
+		var area = j = 0;
+		for (var i = 0, il = vertices.length; i < il; i++) {
+			j = (i + 1) % vertices.length;
+			area += vertices[i][0] * vertices[j][1];
+			area -= vertices[j][0] * vertices[i][1];
+		}
+		return area / 2;
+	};
+
+	function pointsToCircle(center, radius, pathValue) {
+		var angle, anchor, handle;
+		var vertices = pathValue.vertices;
+		var numPoints = vertices.length;
 		var slice = 2 * Math.PI / numPoints;
 		var handleLength = getHandleLength(numPoints, radius);
-		var angleOffset = getAngle(center, pointsArray[0]);
+		var angleOffset = getAngle(center, vertices[0]);
 
 		var newCoordinates = {
 			vertices: [],
 			outTangents: []
 		};
 
-		if (clockwiseDirection(pointsArray)) {
+		if (clockwiseDirection(vertices)) {
 			for (var i = 0; i < numPoints; i++) {
 				angle = slice * i + angleOffset;
 
 				newCoordinates.inTangents.push(handle - anchor);
 				newCoordinates.outTangents.push(anchor - handle);
 			}
-		}
 
-		return newCoordinates;
-	}
+		}
 
-	function getPointCoordinates(center, radius, angle) {
-		return [
-			center[0] + radius * Math.cos(angle),
-			center[1] + radius * Math.sin(angle)
-		]
-	}
+		// Add additional point to close the circle
+		if (!pathValue.closed) {
+			newCoordinates.vertices.push(newCoordinates.vertices[0]);
+			newCoordinates.inTangents.push(newCoordinates.inTangents[0]);
+			newCoordinates.outTangents.push(newCoordinates.outTangents[0]);
+		}
 
-	function getHandleLength(numPoints, radius) {
-		return radius * getMagicNumber() * 4 / numPoints;
-	}
+		return newCoordinates;
+	};
 
-	function getMagicNumber() {
-		// http://stackoverflow.com/questions/1734745/how-to-create-circle-with-bézier-curves
-		// https://people-mozilla.org/~jmuizelaar/Riskus354.pdf
-		return (4 / 3) * Math.tan(Math.PI / (2 * 4));
-	}
+	function quadraticEquation(a, b, c) {
+		var D = b * b - 4 * a * c; // D is the discriminant of the quadratic equation
+		if (D < 0) return null; // If the discriminant is negative, then there are no real roots
+		var sqrtD = Math.sqrt(D); // If the discriminant is positive, then there are two distinct roots
 
-	function getAverageInArray(verticesArray) {
-		return sumArray(verticesArray) / verticesArray.length;
-	}
+		return {
+			root1: (-b + sqrtD) / (2 * a),
+			root2: (-b - sqrtD) / (2 * a)
+		}
+	};
 
 	function sumArray(array) {
 		var totalSum = array[0];
 		for (var i = 1, il = array.length; i < il; i++)
 			totalSum += array[i];
 		return totalSum;
-	}
+	};
 
-	function clockwiseDirection(vertices) {
-		// http://stackoverflow.com/questions/14505565/detect-if-a-set-of-points-in-an-array-that-are-the-vertices-of-a-complex-polygon
-		var polygonArea = getPoligonArea(vertices);
-		return polygonArea > 0;
-	}
+	function toBezierPoints(pathValue) {
+		var valuesArray = [],
+			p1, p2, p3, p4;
 
-	function getPoligonArea(vertices) {
-		var area = 0;
-		for (var i = 0; i < vertices.length; i++) {
-			j = (i + 1) % vertices.length;
-			area += vertices[i][0] * vertices[j][1];
-			area -= vertices[j][0] * vertices[i][1];
+		for (var i = 0, il = pathValue.vertices.length - 1; i < il; i++) {
+			p1 = pathValue.vertices[i];
+			p2 = pathValue.vertices[i] + pathValue.outTangents[i];
+			p3 = pathValue.inTangents[i + 1] + pathValue.vertices[i + 1]
+			p4 = pathValue.vertices[i + 1];
+
+			valuesArray.push([p1, p2, p3, p4]);
 		}
-		return area / 2;
-	}
 
-	function getAngle(point1, point2) {
-		var distanceX = point2[0] - point1[0];
-		var distanceY = point2[1] - point1[1];
-		var theta = Math.atan2(distanceY, distanceX);
-		return theta;
-	}
+		if (pathValue.closed) {
+			p1 = pathValue.vertices[pathValue.vertices.length - 1];
+			p2 = pathValue.vertices[pathValue.vertices.length - 1] + pathValue.outTangents[pathValue.outTangents.length - 1];
+			p3 = pathValue.inTangents[0] + pathValue.vertices[0]
+			p4 = pathValue.vertices[0];
 
-	function calculateDistancesFromCenter(center, vertices) {
-		var distanceFromCenter;
-		var distanceArray = [];
-		for (var i = 0, il = vertices.length; i < il; i++) {
-			distanceFromCenter = distanceBetweenPoints(center, vertices[i]);
-			distanceArray.push(distanceFromCenter);
+			valuesArray.push([p1, p2, p3, p4]);
 		}
-		return distanceArray;
-	}
 
-	function distanceBetweenPoints(point1, point2) {
-		var deltaX = point1[0] - point2[0];
-		var deltaY = point1[1] - point2[1];
-		var distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
-		return distance;
-	}
+		return valuesArray;
+	};
 })(this);
Updated by renderTom -

File Shape to Circle.js Modified

  • Ignore whitespace
  • Hide word diff
 /**********************************************************************************************
-    Shape to Circle
+    Shape to Circle.jsx
     Copyright (c) 2017 Tomas Šinkūnas. All rights reserved.
     www.rendertom.com
 
Created by renderTom -

File Shape to Circle.js Added

  • Ignore whitespace
  • Hide word diff
+/**********************************************************************************************
+    Shape to Circle
+    Copyright (c) 2017 Tomas Šinkūnas. All rights reserved.
+    www.rendertom.com
+
+    Description:
+    	Morpth any shape into a circle.
+
+        This script is provided "as is," without warranty of any kind, expressed
+        or implied. In no event shall the author be held liable for any damages
+        arising in any way from the use of this script.
+**********************************************************************************************/
+
+(function(thisObj) {
+
+	scriptBuildUI(thisObj)
+
+	function scriptBuildUI(thisObj) {
+		var win = (thisObj instanceof Panel) ? thisObj : new Window("palette", "Shape to Circle", undefined, {
+			resizeable: true
+		});
+		win.alignChildren = ["fill", "top"];
+		win.spacing = 5;
+		var grpCustomRadius = win.add("group");
+		var checkCustomRadius = grpCustomRadius.add("checkbox", undefined, "Custom Radius");
+		checkCustomRadius.value = true;
+		checkCustomRadius.onClick = function() {
+			etCustomRadius.enabled = this.value;
+			ddRadius.enabled = !this.value;
+		}
+		var etCustomRadius = grpCustomRadius.add("edittext", undefined, "200");
+		etCustomRadius.alignment = ["fill", "top"];
+
+		var ddRadius = win.add("dropdownlist", undefined, ["Average from center", "Maximum from center", "Minimum from center"])
+		ddRadius.selection = 0;
+
+		var button = win.add("button", undefined, "Do It!");
+		button.onClick = function() {
+			if (etCustomRadius.enabled) {
+				if (isNaN(etCustomRadius.text) || parseInt(etCustomRadius.text) < 0) {
+					etCustomRadius.active = true;
+					return alert("Please enter vadid radius value.");
+				} else {
+					main(parseInt(etCustomRadius.text));
+				}
+			} else {
+				main(ddRadius.selection.text);
+			}
+		}
+		win.onShow = function() {
+			checkCustomRadius.onClick();
+		}
+		win.onResizing = win.onResize = function() {
+			this.layout.resize();
+		};
+
+		win instanceof Window
+			? (win.center(), win.show())
+			: (win.layout.layout(true), win.layout.resize());
+	}
+
+	function main(radiusFromUI) {
+		try {
+			var radius = radiusFromUI;
+			var distanceArray = [];
+
+			var composition = getActiveComposition();
+			var selectedLayers = getSelectedLayers(composition);
+			var allShapeProperties = getAllShapeProperties(selectedLayers);
+			if (allShapeProperties.length === 0)
+				throw new Error ("Please select Path property")
+
+			app.beginUndoGroup("Shape to Circle");
+
+			for (var j = 0, jl = allShapeProperties.length; j < jl; j ++) {
+				var pathValue = allShapeProperties[j].value;
+				var vertices = pathValue.vertices;
+				var centerCoordinates = getAverageInArray(vertices);
+
+				if (isNaN(radiusFromUI)) {
+					distanceArray = calculateDistancesFromCenter(centerCoordinates, vertices);
+					if (radiusFromUI.match("Average")) 	  	radius = getAverageInArray(distanceArray);
+					else if (radiusFromUI.match("Maximum")) radius = Math.max.apply(null, distanceArray);
+					else if (radiusFromUI.match("Minimum")) radius = Math.min.apply(null, distanceArray);
+				}
+
+				var circlePoints = pointsToCircle(centerCoordinates, radius, vertices);
+
+				var circleShape = new Shape();
+					circleShape.vertices = circlePoints.vertices;
+					circleShape.inTangents = circlePoints.inTangents;
+					circleShape.outTangents = circlePoints.outTangents;
+					circleShape.closed = pathValue.closed;
+
+				allShapeProperties[j].numKeys === 0
+					? allShapeProperties[j].setValue(circleShape)
+					: allShapeProperties[j].setValueAtTime(composition.time, circleShape);
+
+			}
+
+			app.endUndoGroup();
+
+		} catch (e) {
+			alert(e.message);
+		}
+
+	}
+
+	function getActiveComposition() {
+		var composition = app.project.activeItem;
+		if (!composition || !(composition instanceof CompItem))
+			throw new Error("Please select composition first.");
+		return composition;
+	}
+
+	function getSelectedLayers(composition) {
+		var selectedLayers = composition.selectedLayers;
+		if (selectedLayers.length === 0)
+			throw new Error("Please select layer first.");
+		return selectedLayers;
+	}
+
+	function getLayerShapeProperties(layer) {
+		var shapeProperties = [];
+		var prop;
+		var selectedProperties = layer.selectedProperties;
+		for (var i = 0, il = selectedProperties.length; i < il; i ++) {
+			prop = selectedProperties[i];
+			
+			if (prop.matchName === "ADBE Vector Shape - Group") {
+				if (prop.property("ADBE Vector Shape").selected === true) {
+					continue
+				} else {
+					shapeProperties.push(prop.property("ADBE Vector Shape"))
+				}
+			} else if (prop.matchName === "ADBE Mask Atom") {
+				if (prop.property("ADBE Mask Shape").selected === true) {
+					continue
+				} else {
+					shapeProperties.push(prop.property("ADBE Mask Shape"))
+				}
+			} else if (prop.matchName.match("ADBE Vector Shape|ADBE Mask Shape")) {
+				shapeProperties.push(prop)
+			}
+		}
+		return shapeProperties;
+	}
+
+	function getAllShapeProperties(selectedLayers) {
+		var allShapeProperties = [];
+		var layerShapeProperties = [];
+		for (var i = 0, il = selectedLayers.length; i < il; i ++) {
+			layerShapeProperties = getLayerShapeProperties(selectedLayers[i]);
+			if (layerShapeProperties.length > 0) {
+				allShapeProperties = allShapeProperties.concat(layerShapeProperties);
+			}
+		}
+		return allShapeProperties;
+	}
+
+	function pointsToCircle(center, radius, pointsArray) {
+		var angle, anchor, handle;
+
+		var numPoints = pointsArray.length;
+		var slice = 2 * Math.PI / numPoints;
+		var handleLength = getHandleLength(numPoints, radius);
+		var angleOffset = getAngle(center, pointsArray[0]);
+
+		var newCoordinates = {
+			vertices: [],
+			inTangents: [],
+			outTangents: []
+		};
+
+		if (clockwiseDirection(pointsArray)) {
+			for (var i = 0; i < numPoints; i++) {
+				angle = slice * i + angleOffset;
+
+				anchor = getPointCoordinates(center, radius, angle);
+				handle = getPointCoordinates(anchor, handleLength, angle + Math.PI / 2);
+
+				newCoordinates.vertices.push(anchor);
+				newCoordinates.inTangents.push(anchor - handle);
+				newCoordinates.outTangents.push(handle - anchor);
+			}
+		} else {
+			for (var i = numPoints; i > 0; i--) {
+				angle = slice * i + angleOffset;
+
+				anchor = getPointCoordinates(center, radius, angle);
+				handle = getPointCoordinates(anchor, handleLength, angle + Math.PI / 2);
+
+				newCoordinates.vertices.push(anchor);
+				newCoordinates.inTangents.push(handle - anchor);
+				newCoordinates.outTangents.push(anchor - handle);
+			}
+		}
+
+		return newCoordinates;
+	}
+
+	function getPointCoordinates(center, radius, angle) {
+		return [
+			center[0] + radius * Math.cos(angle),
+			center[1] + radius * Math.sin(angle)
+		]
+	}
+
+	function getHandleLength(numPoints, radius) {
+		return radius * getMagicNumber() * 4 / numPoints;
+	}
+
+	function getMagicNumber() {
+		// http://stackoverflow.com/questions/1734745/how-to-create-circle-with-bézier-curves
+		// https://people-mozilla.org/~jmuizelaar/Riskus354.pdf
+		return (4 / 3) * Math.tan(Math.PI / (2 * 4));
+	}
+
+	function getAverageInArray(verticesArray) {
+		return sumArray(verticesArray) / verticesArray.length;
+	}
+
+	function sumArray(array) {
+		var totalSum = array[0];
+		for (var i = 1, il = array.length; i < il; i++)
+			totalSum += array[i];
+		return totalSum;
+	}
+
+	function clockwiseDirection(vertices) {
+		// http://stackoverflow.com/questions/14505565/detect-if-a-set-of-points-in-an-array-that-are-the-vertices-of-a-complex-polygon
+		var polygonArea = getPoligonArea(vertices);
+		return polygonArea > 0;
+	}
+
+	function getPoligonArea(vertices) {
+		var area = 0;
+		for (var i = 0; i < vertices.length; i++) {
+			j = (i + 1) % vertices.length;
+			area += vertices[i][0] * vertices[j][1];
+			area -= vertices[j][0] * vertices[i][1];
+		}
+		return area / 2;
+	}
+
+	function getAngle(point1, point2) {
+		var distanceX = point2[0] - point1[0];
+		var distanceY = point2[1] - point1[1];
+		var theta = Math.atan2(distanceY, distanceX);
+		return theta;
+	}
+
+	function calculateDistancesFromCenter(center, vertices) {
+		var distanceFromCenter;
+		var distanceArray = [];
+		for (var i = 0, il = vertices.length; i < il; i++) {
+			distanceFromCenter = distanceBetweenPoints(center, vertices[i]);
+			distanceArray.push(distanceFromCenter);
+		}
+		return distanceArray;
+	}
+
+	function distanceBetweenPoints(point1, point2) {
+		var deltaX = point1[0] - point2[0];
+		var deltaY = point1[1] - point2[1];
+		var distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
+		return distance;
+	}
+})(this);
HTTPS SSH

You can clone a snippet to your computer for local editing. Learn more.