Shape layer height = composition height in ExtendScript - extendscript

I have a plugin that detects whether the pixel width or height of an object is greater.
It works great for other objects, but with shape layers, it just says the composition size.
My code is
pixelWidth = +currentLayer.width * +width / 100;
pixelHeight = +currentLayer.height * +height / 100;
Variables width and height are the scale property, and I make it apply the percentage of Scale property affect the outcome, so its the appearing scale.
Thanks

Well I guess your question is "How to get the width and height of a shape layer in After Effects?". Am I right? Why don't you say so. As you found out the width and height properties only return the width and height of the comp. For text and shape layers you need to use the sourceRectAtTime(timeT, extents) method. It will return an object like this {top, left, width, height} these are measured from the layers anchor.
var layer = app.project.activeItem.selectedLayers[0];
$.writeln(layer.width); // gives the comp width
$.writeln(layer.height);// gives the comp height
// from the After Effects Scripting Guide
// AVLayer sourceRectAtTime() method
// app.project.item(index).layer(index).sourceRectAtTime(timeT, extents) Description
// Retrieves the rectangle bounds of the layer at the specified time index,
// corrected for text or shape layer content.
// Use, for example, to write text that is properly aligned to the baseline.
/**
* sourceRectAtTime
* #param {Number} The time index, in seconds. A floating-point value.
* #param {Boolean} True to include the extents, false otherwise. Extents apply to shape layers, increasing the size of the layer bounds as necessary.
*
* #return {Object} A JavaScript object with four attributes, {top, left, width, height}.
*/
var bounds = layer.sourceRectAtTime(0, true);
$.writeln(bounds.toSource());

Related

Pixel space depth offset in vertex shader

I'm trying to draw simple scaled points in my custom graphics engine. The points are scaled in pixel space, and the radius of the points are in pixels, but the position of the points fed to the draw function are in world coordinates.
So far, everything is working great, except for a depth clipping issue. The points are of constant size, regardless of how far away they are, which is done by offsetting the vertices in projected/clip space. However, when they are close to surfaces, they partially intersect them in the depth buffer.
Since these points represent world coordinates, I want them to use the depth buffer, and be hidden behind objects that are in front of them. However, when the point is close to a surface, I want to push it toward the camera, so it doesn't partially intersect it. I think it is easier to just always do this push, regardless of the point being close to a surface. What makes the most sense to me is to just push it by its radius, so that all of its vertices are exactly far enough away to avoid clipping into nearby surfaces.
The easiest way I've found to do this is to simply subtract from the Z value in the vertex shader, after transforming into view-projection space. However, I'm having some trouble converting my pixel radius into a depth offset. Regardless of the math I use, what works close up never seems to work far away. I'm thinking maybe this is due to how the z buffer is non-linear, but could be wrong.
Currently, the closest I've been to solving this is the following:
proj_vertex_pos.z -= point_pixel_radius / proj_vertex_pos.w * 100.0
I'm honestly not sure why 100.0 helps make this work yet. I added it simply because dividing the radius by w was too small of a value. Can anyone point me in the right direction? How do I convert my pixel distance into a depth distance? Especially if the depth distance changes scale depending on which depth you are at? Or am I just way off?
The solution was to convert my pixel space radius into world space units, since the z-buffer is still in world space, even after transforming by the view-projection transform. This can be done by converting pixels into a factor (factor = pixels / screen_size), then convert the factor into world space units, which was a little more involved - I had to calculate the world-space size of the screen at a given distance, then multiply the factor by that to get world units. I can post the related code if anyone needs it. There's probably a simpler way to calculate it, but my brain always goes straight for factors.
The reason I was getting different results at different distances was mainly because I was only offsetting the z component of the clip position by the result. It's also necessary to offset the w component, to make the depth offset work at any distance (linear). However, in order to offset the w component, you first have to scale xy by w, modify w as needed, then divide xy by the new w. This resulted in making the math pretty involved, so I changed the strategy to offset the vertex before clip space, which requires calculating the distance to the camera in Z space manually, but it honestly ended up being about the same amount of math either way.
Here is the final vertex shader at the moment. Hopefully the global values make sense. I did not modify this to post it, so please forgive any sillyness in my comments. EDIT: I had to make some edits to this, because I was accidentally moving the vertex along the camera-Z direction instead of directly toward the camera:
lerpPoint main(vinBake vin)
{
// prepare output
lerpPoint pin;
// extract radius/size from input
pin.InRadius = vin.TexCoord.y;
// compute offset from vertex to camera
float3 to_cam_offset = Scene.CamPos - vin.Position.xyz;
// compute the Z distance of the camera from the vertex
float cam_z_dist = -dot( Scene.CamZ, to_cam_offset );
// compute the radius factor
// + this describes what percentage of the screen is covered by our radius
// + this removes it from pixel space into factor-space
float radius_fac = Scene.InvScreenRes.x * pin.InRadius;
// compute world-space radius by scaling with FieldFactor
// + FieldFactor.x represents the world-space-width of the camera view at whatever distance we scale it by
// + here, we scale FieldFactor.x by the camera z distance, which gives us the world radius, in world units
// + we must multiply by 2 because FieldFactor.x only represents HALF of the screen
float radius_world = radius_fac * Scene.FieldFactor.x * cam_z_dist * 2.0;
// finally, push the vertex toward the camera by the world radius
// + note: moving by radius will only work with surfaces facing the camera, since we are moving toward the camera, rather than away from the surface
// + because of this, we also multiply by another 4, to compensate for nearby surface angles, but there is no scale that would work for every angle
float3 offset = normalize(to_cam_offset) * (radius_world * -4.0);
// generate projected position
// + after this, x=-1 is left, x=+1 is right, y=-1 is bottom, and y=+1 is top of screen
// + note that after this transform, w represents "distance from camera", and z represents "distance from near plane", both in world space
pin.ClipPos = mul( Scene.ViewProj, float4( vin.Position.xyz + offset, 1.0) );
// calculate radius of point, in clip space from our radius factor
// + we scale by 2 to convert pixel radius into clip-radius
float clip_radius = radius_fac * 2.0 * pin.ClipPos.w;
// compute scaled clip-space offset and apply it to our clip-position
// + vin.Prop.xy: -1,-1 = bottom-left, -1,1 = top left, 1,-1 = bottom right, 1,1 = top right (note: in clip-space, +1 = top, -1 = bottom)
// + we scale by clipping depth (part of clip_radius) to retain constant scale, but this will give us a VERY LARGE result
// + we scale by inverter resolution (clip_radius) to convert our input screen scale (eg, 1->1024) into a clip scale (eg, 0.001 to 1.0 )
pin.ClipPos.x += vin.Prop.x * clip_radius;
pin.ClipPos.y += vin.Prop.y * clip_radius * Scene.Aspect;
// return result
return pin;
}
Here is the other version that offsets z & w instead of changing things in world space. After edits above, this is probably the more optimal solution:
lerpPoint main(vinBake vin)
{
// prepare output
lerpPoint pin;
// extract radius/size from input
pin.InRadius = vin.TexCoord.y;
// generate projected position
// + after this, x=-1 is left, x=+1 is right, y=-1 is bottom, and y=+1 is top of screen
// + note that after this transform, w represents "distance from camera", and z represents "distance from near plane", both in world space
pin.ClipPos = mul( Scene.ViewProj, float4( vin.Position.xyz, 1.0) );
// compute the radius factor
// + this describes what percentage of the screen is covered by our radius
// + this removes it from pixel space into factor-space
float radius_fac = Scene.InvScreenRes.x * pin.InRadius;
// compute world-space radius by scaling with FieldFactor
// + FieldFactor.x represents the world-space-width of the camera view at whatever distance we scale it by
// + here, we scale FieldFactor.x by the camera z distance, which gives us the world radius, in world units
// + we must multiply by 2 because FieldFactor.x only represents HALF of the screen
float radius_world = radius_fac * Scene.FieldFactor.x * pin.ClipPos.w * 2.0;
// offset depth by our world radius
// + we scale this extra to compensate for surfaces with high angles relative to the camera (since we are moving directly at it)
// + notice we have to make the perspective divide before modifying w, then re-apply it after, or xy will be off
pin.ClipPos.xy /= pin.ClipPos.w;
pin.ClipPos.z -= radius_world * 10.0;
pin.ClipPos.w -= radius_world * 10.0;
pin.ClipPos.xy *= pin.ClipPos.w;
// calculate radius of point, in clip space from our radius factor
// + we scale by 2 to convert pixel radius into clip-radius
float clip_radius = radius_fac * 2.0 * pin.ClipPos.w;
// compute scaled clip-space offset and apply it to our clip-position
// + vin.Prop.xy: -1,-1 = bottom-left, -1,1 = top left, 1,-1 = bottom right, 1,1 = top right (note: in clip-space, +1 = top, -1 = bottom)
// + we scale by clipping depth (part of clip_radius) to retain constant scale, but this will give us a VERY LARGE result
// + we scale by inverter resolution (clip_radius) to convert our input screen scale (eg, 1->1024) into a clip scale (eg, 0.001 to 1.0 )
pin.ClipPos.x += vin.Prop.x * clip_radius;
pin.ClipPos.y += vin.Prop.y * clip_radius * Scene.Aspect;
// return result
return pin;
}

threejs - creating "cel-shading" for objects that are close by

So I'm trying to "outline" 3D objects. Standard problem, for which the answer is meant to be that you copy the mesh, color it the outline color, scale it up, and then set it to only render faces that are "pointed in the wrong direction" - for us that means setting side:THREE.BackSide in the material. Eg here https://stemkoski.github.io/Three.js/Outline.html
But see what happens for me
Here's what I'd like to make
I have a bunch of objects that are close together - they get "inside" one another's outline.
Any advice on what I should do? What I want to be seeing is everywhere on the rendered frame that these shapes touch the background or each other, there you have outline.
What do you want to happen? Is that one mesh in your example or is it a bunch of intersecting meshes. If it's a bunch of intersecting meshes do you want them to have one outline? What about other meshes? My point is you need some way to define which "groups" of meshes get a single outline if you're using multiple meshes.
For multiple meshes and one outline a common solution is to draw all the meshes in a single group to a render target to generate a silhouette, then post process the silhouette to expand it. Finally apply the silhouette to the scene. I don't know of a three.js example but the concept is explained here and there's also many references here
Another solution that might work, should be possible to move the outline shell back in Z so doesn't intersect. Either all the way back (Z = 1 in clip space) or back some settable amount. Drawing with groups so that a collection of objects in front has an outline that blocks a group behind would be harder.
For example if I take this sample that prisoner849 linked to
And change the vertexShaderChunk in OutlineEffect.js to this
var vertexShaderChunk = `
#include <fog_pars_vertex>
uniform float outlineThickness;
vec4 calculateOutline( vec4 pos, vec3 objectNormal, vec4 skinned ) {
float thickness = outlineThickness;
const float ratio = 1.0; // TODO: support outline thickness ratio for each vertex
vec4 pos2 = projectionMatrix * modelViewMatrix * vec4( skinned.xyz + objectNormal, 1.0 );
// NOTE: subtract pos2 from pos because BackSide objectNormal is negative
vec4 norm = normalize( pos - pos2 );
// ----[ added ] ----
// compute a clipspace value
vec4 pos3 = pos + norm * thickness * pos.w * ratio;
// do the perspective divide in the shader
pos3.xyz /= pos3.w;
// just return screen 2d values at the back of the clips space
return vec4(pos3.xy, 1, 1);
}
`;
It's easier to see if you remove all references to reflectionCube and set the clear color to white renderer.setClearColor( 0xFFFFFF );
Original:
After:

Positioning digital signatures on pdfs using custom coordinates (using bouncy castle framework)

Following is the code I am using to place digital signatures on standard four coordinates ie top left, top right, bottom left, bottom right. However, its not working correctly for top left.
PdfReader reader = new PdfReader(src);//src being file path of pdf being signed
Rectangle cropBox = reader.getCropBox(1);
if(signaturePosition.equalsIgnoreCase("bottom-left"))
appearance.setVisibleSignature(new Rectangle(cropBox.getLeft(), cropBox.getBottom(),
cropBox.getLeft(width), cropBox.getBottom(height)), 1, "first");
if(signaturePosition.equalsIgnoreCase("bottom-right"))
appearance.setVisibleSignature(new Rectangle(cropBox.getRight(width), cropBox.getBottom(),
cropBox.getRight(), cropBox.getBottom(height)), 1, "first");
if(signaturePosition.equalsIgnoreCase("top-left"))
appearance.setVisibleSignature(new Rectangle(72, 732, 144, 780), 1, "first");
if(signaturePosition.equalsIgnoreCase("top right"))
appearance.setVisibleSignature(new Rectangle(cropBox.getRight(width), cropBox.getTop(height),
cropBox.getRight(), cropBox.getTop()), 1, "first");
Its required to position the signature anywhere on pdf depending upon coordinates provided by the user. To get user's coordinates, we are using a pdf xpro template with a textbox placed over it(user will upload the template and place the textbox where he requires signature to be placed), using that textbox coordinates with its height and width as reference we will position the signature on the pdf.
I need help on understanding how appearance.setVisibleSignature() method works wrt the Rectangle object passed to it, because for each quadrant of the page(considering the center of the page as origin) the parameters are being passed differently for top left, top right, bottom left and bottom right respectively.
The OP clarified in a (meanwhile deleted) comment:
I am asking for explanation of passing height width as parameter to getTop(),getLeft() etc functions. Its not being clear.
Those methods are defined as:
// Returns the lower left y-coordinate, considering a given margin.
public float getBottom(final float margin) {
return lly + margin;
}
// Returns the lower left x-coordinate, considering a given margin.
public float getLeft(final float margin) {
return llx + margin;
}
// Returns the upper right x-coordinate, considering a given margin.
public float getRight(final float margin) {
return urx - margin;
}
// Returns the upper right y-coordinate, considering a given margin.
public float getTop(final float margin) {
return ury - margin;
}
(Rectangle.java)
I.e. these methods return the maximum or minimum x or y coordinate moved inwards by the given margin distance. In the calculations you copied above these methods were used merely as a replacement of some additions or subtractions.
ideally rectangle is top left coordinate as first two coords and height and width as other 2 coords,
What one considers ideal depends on circumstances. What we have here is the constructor
/**
* Constructs a <CODE>Rectangle</CODE> -object.
*
* #param llx lower left x
* #param lly lower left y
* #param urx upper right x
* #param ury upper right y
*/
public Rectangle(final float llx, final float lly, final float urx, final float ury)
(ibidem)
So, first you have the lower left x and y, then the upper right x and y. In other words, you have left x, bottom y, right x, top y.
but its being done differently. Request insights into this.
If you have the upper left coordinates and the width and height, those required coordinates are easy to calculate.
BTW, I hope you are aware that in PDF the default user space coordinate system can have its origin anywhere on or off the page (check the crop box), and that (as common in mathematics) x coordinates are increasing to the right and y coordinates are increasing upwards.

Scaling a rotated object to fit specific rect

How can I find the scale ratio a rotated Rect element in order fit it in a bounding rectangle (unrotated) of a specific size?
Basically, I want the opposite of getBoundingClientRect, setBoundingClientRect.
First you need to get the transform applied to the element, with <svg>.getTransformToElement, together with the result of rect.getBBox() you can calculate the actual size. Width this you can calculate the scale factor to the desired size and add it to the transform of the rect. With this I mean that you should multiply actual transform matrix with a new scale-matrix.
BUT: This is a description for a case where you are interested in the AABB, means axis aligned bounding box, what the result of getBoundingClientRect delivers, for the real, rotated bounding box, so the rectangle itself in this case, you need to calculate (and apply) the scale factor from the width and/or height.
Good luck…
EDIT::
function getSVGPoint( x, y, matrix ){
var p = this._dom.createSVGPoint();
p.x = x;
p.y = y;
if( matrix ){
p = p.matrixTransform( matrix );
}
return p;
}
function getGlobalBBox( el ){
var mtr = el.getTransformToElement( this._dom );
var bbox = el.getBBox();
var points = [
getSVGPoint.call( this, bbox.x + bbox.width, bbox.y, mtr ),
getSVGPoint.call( this, bbox.x, bbox.y, mtr ),
getSVGPoint.call( this, bbox.x, bbox.y + bbox.height, mtr ),
getSVGPoint.call( this, bbox.x + bbox.width, bbox.y + bbox.height, mtr ) ];
return points;
};
with this code i one time did a similar trick... this._dom refers to a <svg> and el to an element. The second function returns an array of points, beginning at the top-right edge, going on counter clockwise arround the bbox.
EDIT:
the result of <element>.getBBox() does not include the transform that is applied to the element and I guess that the new desired size is in absolute coordinates. So the first thing you need to is to make the »BBox« global.
Than you can calculate the scaling factor for sx and sy by:
var sx = desiredWidth / globalBBoxWidth;
var sy = desiredHeight / globalBBoxHeight;
var mtrx = <svg>.createSVGMatrix();
mtrx.a = sx;
mtrx.d = sy;
Than you have to append this matrix to the transform list of your element, or concatenate it with the actual and replace it, that depends on you implementation. The most confusion part of this trick is to make sure that you calculate the scaling factors with coordinates in the same transformation (where absolute ones are convenient). After this you apply the scaling to the transform of the <element>, do not replace the whole matrix, concatenate it with the actually applied one, or append it to the transform list as new item, but make sure that you do not insert it before existing item. In case of matrix concatenation make sure to preserve the order of multiplication.
The last steps depend on your Implementation, how you handle the transforms, if you do not know which possibilities you have, take a look here and take special care for the DOMInterfaces you need to implement this.

How does one get the height/width of an SVG group element?

I need to know the width and height of a SVG element? Im trying to use the following:
$('g#myGroup').height()
...but the result is always zero?
svg <g> elements don't have explicit height and width attributes, they auto size to whatever they contain. You can get their actual height/width by calling getBBox on the element though:
var height = document.getElementById("myGroup").getBBox().height;
If you're really into jquery you could write it as
$('g#myGroup').get(0).getBBox().height;
according to Reed Spool
I wasn't able to get any of the answers above to work, but did come across this solution for finding it with d3:
var height = d3.select('#myGroup').select('svg').node().getBBox().height;
var width = d3.select('#myGroup').select('svg').node().getBBox().width;
getBBox() here will find the actual width and height of the group element. Easy as that.
Based on the above answer, you can create jQuery functions .widthSVG() and .heightSVG()
/*
* .widthSVG(className)
* Get the current computed width for the first element in the set of matched SVG elements.
*/
$.fn.widthSVG = function(){
return ($(this).get(0)) ? $(this).get(0).getBBox().width : null;
};
/*
* .heightSVG(className)
* Get the current computed height for the first element in the set of matched SVG elements.
*/
$.fn.heightSVG = function(){
return ($(this).get(0)) ? $(this).get(0).getBBox().height : null;
};

Resources