How do I add a class to a Rect in bonsaijs? - 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

Related

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

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:

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

Center label around anchor point in RelativeLayout with Xamarin

I have a RelativeLayout, and multiple polygons/labels in it. Now I want to place the labels inside the polygons, in the center of it. However, I don't know the width and height of the label, so placing them in the center results in something like this:
However, this is wrong, as I want the label to be in the exact middle of the polygon (of which the width and height is known), no matter what the text is.
I've looked into methods to try to center it, for example using HorizontalTextAlignment and VerticalTextAlignment, but so far no success. What is the right way to do that? HorizontalOptions doesn't work either, as it's not relative to a parent or something.
Code I'm currently using:
var MainGrid = new RelativeLayout();
var path = new PointCollection();
// add points to path
var coords = new Point(100, 100);
var TILE_WIDTH = 70;
var TILE_HEIGHT = 80;
// Here the real code begins
polygon = new Polygon
{
Points = path,
<other arguments>
};
var text = new Label
{
Text = "5",
HorizontalTextAlignment = TextAlignment.Center,
VerticalTextAlignment = TextAlignment.Center,
TextColor = Color.Black,
};
MainGrid.Children.Add(
polygon,
Constraint.Constant(coords.X),
Constraint.Constant(coords.Y)
);
MainGrid.Children.Add(
text,
Constraint.Constant(coords.X + TILE_WIDTH / 2),
Constraint.Constant(coords.Y + TILE_HEIGHT / 2)
);
set an arbitrary width and height on the label, as long as it is big enough to fit your text and smaller than the polygon. Also set a background color to help visualize the placement of the label (remove this once you get the alignment right)
var text = new Label
{
Text = "5",
HorizontalTextAlignment = TextAlignment.Center,
VerticalTextAlignment = TextAlignment.Center,
TextColor = Color.Black,
BackgroundColor = Color.White,
HeightRequest = 50,
WidthRequest = 50
};
then factor in the label width and height when placing it
MainGrid.Children.Add(
text,
Constraint.Constant(coords.X + (TILE_WIDTH / 2) - 25),
Constraint.Constant(coords.Y + (TILE_HEIGHT / 2) - 25)
);

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]

Adding a legend title

I am new to Leaflet and I am currently struggling with tutorials. So far I managed to create an interactive clorophet map, like in the example http://leafletjs.com/examples/choropleth.html.
Is it possible to add a title (simple text, not dynamic) to the legged located on the bottomright of the page? Could anyone tell me how, by just referring to the linked example?
You just need to add your title under "THE TITLE"...
var legend = L.control({position: 'topleft'});
legend.onAdd = function (map) {
var div = L.DomUtil.create('div', 'info legend'),
grades = [50, 100, 150, 200, 250, 300],
labels = ['<strong> THE TITLE </strong>'],
from, to;
for (var i = 0; i < grades.length; i++) {
from = grades [i];
to = grades[i+1]-1;
labels.push(
'<i style="background:' + getColor(from + 1) + '"></i> ' +
from + (to ? '–' + to : '+'));
}
div.innerHTML = labels.join('<br>');
return div;
};

Resources