how to make an arc rotating around a center in ExtendScript After Effects? - adobe

I want to make a loading animation in After Effects using scripts
I get no arc only white screen with shape layer there
and this is what gets rendered in the app
This is my code that doesn't work
app.project.close(CloseOptions.DO_NOT_SAVE_CHANGES);
app.newProject();
app.beginUndoGroup("Create Comp");
// Create a new composition with a solid layer
var comp = app.project.items.addComp("My Composition", 1920, 1080, 1, 10, 24);
comp.openInViewer();
var solid = comp.layers.addSolid([1, 1, 1], "My Solid", 1920, 1080, 1, 10);
// Create a new shape layer
var shapeLayer = comp.layers.addShape();
// shapeLayer.moveToStart();
shapeLayer.enabled= true;
// Set position of the shape layer
var shapePosition = shapeLayer.property("ADBE Transform Group").property("ADBE Position");
shapePosition.setValue([960,540]);
var path = shapeLayer.property("ADBE Root Vectors Group").addProperty("ADBE Vector Shape - Group");
// make a arc
path.property("ADBE Vector Shape").setValue(new Shape());
path.property("ADBE Vector Shape").value.vertices = [[0, 0], [100, 100], [200, 0]];
path.property("ADBE Vector Shape").value.inTangents = [[-50, -50], [0, 0]];
path.property("ADBE Vector Shape").value.outTangents = [[0, 0], [50, 50]];
path.property("ADBE Vector Shape").value.closed = true;
path.enabled = true;
var stroke = shapeLayer.property("ADBE Root Vectors Group").addProperty("ADBE Vector Graphic - Stroke");
stroke.property("ADBE Vector Stroke Width").setValue(5);
stroke.property("ADBE Vector Stroke Color").setValue([1, 0, 0]);
stroke.enabled = true;
var fill = shapeLayer.property("ADBE Root Vectors Group").addProperty("ADBE Vector Graphic - Fill");
fill.property("ADBE Vector Fill Color").setValue([1, 0, 0]);
fill.enabled = true;
// Create an animation for the rotation property
var rotation = shapeLayer.property("ADBE Transform Group").property("ADBE Rotate Z");
rotation.setValueAtTime(0, 0);
rotation.setValueAtTime(5, 180);
// Set the composition duration to 10 seconds
comp.duration = 10;
app.endUndoGroup();

Properties in AE can't be set by assigning values to them. You need to use the setValue() method. Additionally in this instance you want to set the values of the shape object before assigning it to the path property.
var theShape = new Shape();
theShape.vertices = [[0, 0], [100, 100], [200, 0]];
theShape.inTangents = [[-50, -50], [0, 0]];
theShape.outTangents = [[0, 0], [50, 50]];
theShape.closed = true;
path.property("ADBE Vector Shape").setValue(theShape);
Also, as an AE developer:

Related

How to calculate rotation for a box based on a set of four points?

I’m a bit new to working with 3D space and rotation. I have a list of four Vector3 points that represent the corners of a rectangle. What I want to do is use those points to create a box mesh that is rotated to exactly match the angle of rotation of the points.
Here is a babylonjs playground demo showing what I mean. In it you can see I’ve drawn a simple line mesh between the points. That is great and the rectangle drawn is at the expected angle given the data. I’ve also created a box mesh and configured its dimensions to match and placed its center point in the proper center of the points. So far so good, however I cannot figure out how to rotate the box so that it’s top face is parallel with the face of the rectangle.
https://playground.babylonjs.com/#SN5K8L#2
var createScene = function () {
// This creates a basic Babylon Scene object (non-mesh)
var scene = new BABYLON.Scene(engine);
// This creates and positions a free camera (non-mesh)
var target = new BABYLON.Vector3(1.5, 4, 0)
var camera = new BABYLON.ArcRotateCamera("camera1", Math.PI / 2 + Math.PI, Math.PI / 4, 10, target, scene)
// This attaches the camera to the canvas
camera.attachControl(canvas, true);
// This creates a light, aiming 0,1,0 - to the sky (non-mesh)
var light = new BABYLON.HemisphericLight("light", new BABYLON.Vector3(0, 1, 0), scene);
// Default intensity is 1. Let's dim the light a small amount
light.intensity = 0.7;
const axes = new BABYLON.AxesViewer(scene)
const points = [
new BABYLON.Vector3(1, 5, 1),
new BABYLON.Vector3(2, 5, 1),
new BABYLON.Vector3(2, 3, -1),
new BABYLON.Vector3(1, 3, -1)
]
const lines = BABYLON.MeshBuilder.CreateLines("lines", {
points: [...points, points[0]] // add a duplicate of first point to close polygon
}, scene)
const centerPoint = new BABYLON.Vector3(
(points[0].x + points[1].x + points[2].x + points[3].x) / 4,
(points[0].y + points[1].y + points[2].y + points[3].y) / 4,
(points[0].z + points[1].z + points[2].z + points[3].z) / 4
)
const width = Math.sqrt(
Math.pow(points[1].x - points[0].x, 2) +
Math.pow(points[1].y - points[0].y, 2) +
Math.pow(points[1].z - points[0].z, 2)
)
const depth = Math.sqrt(
Math.pow(points[2].x - points[1].x, 2) +
Math.pow(points[2].y - points[1].y, 2) +
Math.pow(points[2].z - points[1].z, 2)
)
const height = 0.15
const box = BABYLON.CreateBox("box", { width, height, depth}, scene)
box.position = centerPoint
//box.rotation = ???
return scene;
};
You can use Vector3.RotationFromAxis to compute the required rotation in Euler angles:
const rotationAxisX = points[1].subtract(points[0])
const rotationAxisZ = points[1].subtract(points[2])
const rotationAxisY = rotationAxisZ.cross(rotationAxisX)
// RotationFromAxis has the side effect of normalising the input vectors
// so retrieve the box dimensions here
const width = rotationAxisX.length()
const depth = rotationAxisZ.length()
const height = 0.15
const rotationEuler = BABYLON.Vector3.RotationFromAxis(
rotationAxisX,
rotationAxisY,
rotationAxisZ
)
const box = BABYLON.CreateBox("box", { width, height, depth}, scene)
box.position = centerPoint
box.rotation = rotationEuler

How to get bounding box that contains all items in Paper.js?

How can we get the bounding Rectangle that contains all items in the project?
Currently I'm calculating them one by one:
calc-bounds = (scope) ->
fit = {}
for layer in scope.project.layers
for item in layer.children
for <[ top left ]>
if item.bounds[..] < fit[..] or not fit[..]?
fit[..] = item.bounds[..]
for <[ bottom right ]>
if item.bounds[..] > fit[..] or not fit[..]?
fit[..] = item.bounds[..]
#console.log "fit bounds: ", fit
top-left = new scope.Point fit.left, fit.top
bottom-right = new scope.Point fit.right, fit.bottom
new scope.Rectangle top-left, bottom-right
Rationale
A "Fit all" function that sets project.center and project.zoom will need this calculation.
You can just unite (merge) all the bounds from all elements.
This will return a Rectangle that tightly fits all the elements, a.k.a a Bounding Box.
Here's a Sketch.
And here's the code:
var items = [
new Path.Circle({
position: new Point(100, 200),
radius: 50,
fillColor: 'blue'
}),
new Path.Circle({
position: new Point(200, 70),
radius: 50,
fillColor: 'purple'
}),
new Path.Circle({
position: new Point(400, 100),
radius: 50,
fillColor: 'magenta'
})
]
// Unite all bounds from all items.
var unitedBounds = items.reduce((bbox, item) => {
return !bbox ? item.bounds : bbox.unite(item.bounds)
}, null)
// Draw the united bounds.
var bbox = new Path.Rectangle(unitedBounds)
bbox.strokeColor = 'black'
You can use the layer bounds or group the element you want to bound instead of doing the reduce trick from #nicholaswmin
Here is a sketch with both solutions
const items = [
new Path.Circle({
position: new Point(100, 200),
radius: 50,
fillColor: 'blue'
}),
new Path.Circle({
position: new Point(200, 70),
radius: 50,
fillColor: 'purple'
}),
new Path.Circle({
position: new Point(400, 100),
radius: 50,
fillColor: 'magenta'
})
]
// Use the layer bounds
const layerBounds = project.activeLayer.bounds
// Group all needed items and use the bounds
const groupBounds = (new Group(items)).bounds
// Draw the bounds.
const bbox = new Path.Rectangle(layerBounds)
const bbox2 = new Path.Rectangle(groupBounds)
bbox.strokeWidth = 3
bbox.strokeColor = 'blue'
bbox2.strokeWidth = 6
bbox2.strokeColor = 'red'
bbox2.dashArray = [10, 10]

paperjs - draw a line with arrows

Im trying to draw a line with arrow using paperjs but it does not produce the desired output.
http://jsfiddle.net/1j6nLa7c/
$(document).ready(function(){
paper.install(window)
paper.setup('myCanvas')
x1 = 100;
y1 = 100;
r = 100;
theta = .7;
var path = new Path();
path.strokeColor = 'black';
var start = new Point(100, 100);
path.moveTo(start);
path.lineTo(x1+r*Math.cos(theta), y1+r*Math.sin(theta));
path.simplify(300)
var vector = path.getPointAt(path.length).subtract(path.getPointAt(path.length-25))
var arrowVector = vector.normalize(18);
var path2 = new Path({
segments: [path.getPointAt(path.length) + arrowVector.rotate(145), path.getPointAt(path.length), path.getPointAt(path.length) + arrowVector.rotate(-145)],
fillColor: 'black',
strokeWidth: 6,
});
path2.scale(1.3);
})
Here's some code that creates an arrow. The object is initialized with mouse down point and draws an arrow with the tip at the point the mouse is dragged to.
function Arrow (mouseDownPoint) {
this.start = mouseDownPoint;
this.headLength = 20;
this.tailLength = 9;
this.headAngle = 35;
this.tailAngle = 110
}
Arrow.prototype.draw = function (point) {
var end = point;
var arrowVec = this.start.subtract(end);
// parameterize {headLength: 20, tailLength: 6, headAngle: 35, tailAngle: 110}
// construct the arrow
var arrowHead = arrowVec.normalize(this.headLength);
var arrowTail = arrowHead.normalize(this.tailLength);
var p3 = end; // arrow point
var p2 = end.add(arrowHead.rotate(-this.headAngle)); // leading arrow edge angle
var p4 = end.add(arrowHead.rotate(this.headAngle)); // ditto, other side
var p1 = p2.add(arrowTail.rotate(this.tailAngle)); // trailing arrow edge angle
var p5 = p4.add(arrowTail.rotate(-this.tailAngle)); // ditto
// specify all but the last segment, closed does that
this.path = new paper.Path(this.start, p1, p2, p3, p4, p5);
this.path.closed = true;
this.path.strokeWidth = 1
this.path.strokColor = 'black'
this.path.fillColor = 'black'
return this.path
}
I like the tapered tail but you can get rid of that by fiddling with the constructor lengths.
Here's a sketch with the mouse handling

How do I add a class to a Rect in bonsaijs?

I need to add a class to a Rect. I can't seem to figure out how to do it.
bar = (new Rect(x, ySegment * 10 + 30 + margin, w, 0)
.attr('opacity', 0.8)
.attr('class', data[i].segments[j].color)
.addTo(stage));
the class attr is ignored.
A DisplayObject like Rect isn't the representation of an HTMLElement. That's why custom attributes like "class" don't work. If your intention is to re-use attributes for different DisplayObjects, then try the following:
var myAttrs = {
fillColor: 'red',
opacity: 0.5
};
new Rect(20, 20, 100, 100).attr(myAttrs).addTo(stage);
new Rect(20, 130, 100, 100).attr(myAttrs).addTo(stage);
Play with it here: Orbit

Draw polygon of 100 pixels on mouseclick

I need to draw a square polygon of 100x100 screenpixels whereever I click on the Google map (Amsterdam, lat 52, lng 4), on every zoomlevel, with e.latlng at the center of the polygon. I tried to figure it out using fromLatLngToPoint, fromPointToLatLng, scale and worldCoordinates, but I can't get the polygon drawn. If someone likes this puzzle I would appreciate the solution very much.
(I want to use this as a simple start to edit the polygon to a more complex shape, not using the DrawingManager)
I tried:
google.maps.event.addListener(map, 'click', function(e) {
var scale = Math.pow(2, map.getZoom());
var nw = new google.maps.LatLng(map.getBounds().getNorthEast().lat(),map.getBounds().getSouthWest().lng());
var worldCoordinateNW = map.getProjection().fromLatLngToPoint(nw);
var worldCoordinate = map.getProjection().fromLatLngToPoint(e.latLng);
var deX = Math.floor((worldCoordinate.x - worldCoordinateNW.x) * scale);
var deY = Math.floor((worldCoordinate.y - worldCoordinateNW.y) * scale);
// so far so good, deX and deY give the centerpixel
var deNW = map.getProjection().fromPointToLatLng(new google.maps.Point(deX-50,deY-50));
var deNO = map.getProjection().fromPointToLatLng(new google.maps.Point(deX+50,deY-50));
var deZO = map.getProjection().fromPointToLatLng(new google.maps.Point(deX+50,deY+50));
var deZW = map.getProjection().fromPointToLatLng(new google.maps.Point(deX-50,deY+50));
var dePathArray = [deNW, deNO, deZO, deZW];
deObjectNew = new google.maps.Polygon({
paths: dePathArray,
strokeColor: '#000000',
strokeWeight: 1,
fillColor: "#FF0000",
fillOpacity: 0.3,
});
deObjectNew.setMap(map);
});
Got it:
var deNW = deKaart.getProjection().fromPointToLatLng(new google.maps.Point((deX-50)/scale+worldCoordinateNW.x,(deY-50)/scale+worldCoordinateNW.y));
var deNO = deKaart.getProjection().fromPointToLatLng(new google.maps.Point((deX+50)/scale+worldCoordinateNW.x,(deY-50)/scale+worldCoordinateNW.y));
var deZO = deKaart.getProjection().fromPointToLatLng(new google.maps.Point((deX+50)/scale+worldCoordinateNW.x,(deY+50)/scale+worldCoordinateNW.y));
var deZW = deKaart.getProjection().fromPointToLatLng(new google.maps.Point((deX-50)/scale+worldCoordinateNW.x,(deY+50)/scale+worldCoordinateNW.y));

Resources