Copy link to clipboard
Copied
var item = doc.pageItems[i];
var itemWidth = item.width;
var newX = (docWidth - itemWidth) / 2;
var newY = 0;
item.position = [newX, newY];
When the items are simple shapes the above script works as expected i.e. the top border aligned at top artboard side. In case the items are cplipping mask with contents which is higher than the mask then the script fails i.e. the top artbord side aligned with top mask contents. What is possible to do to fix it? I expect that the clipping mask top border will be aligned at the artboard's top side.
Hey, here's a handy function for getting the bounds of objects that accounts for clipping masks and compound paths. Let me know if you have any questions?
GetVisibleBounds: https://github.com/joshbduncan/illustrator-scripts/blob/ada54de802cb950f711f2b78ae93e231a426998c/jsx/utils/GetVisibleBounds.jsxinc
Copy link to clipboard
Copied
Hey, here's a handy function for getting the bounds of objects that accounts for clipping masks and compound paths. Let me know if you have any questions?
GetVisibleBounds: https://github.com/joshbduncan/illustrator-scripts/blob/ada54de802cb950f711f2b78ae93e231a426998c/jsx...
Copy link to clipboard
Copied
As I understand it, the item.position property always places the object on the boundaries of the real content (e.g. what is hidden in the mask)?
Copy link to clipboard
Copied
Correct the position property (and same for the multiple bounds properties). The function linked above returns an array of the provided object bounds [left, top, right, bottom] accounting for the mask. Running the script below in the provided file (with linked function included) presents a dialog with the info from the screenshot. As you can see the hidden ovals are included in the info from the API, whereas the custom function returns what I think you are looking for...
var o = app.activeDocument.selection[0];
alert(
"Placement Info\nPosition: " +
o.position +
"\nGeometric Bounds: " +
o.geometricBounds +
"\nVisible Bounds: " +
o.visibleBounds +
"\ngetVisibleBounds: " +
getVisibleBounds(o)
);
Copy link to clipboard
Copied
Bonjour,
L'objet qui sert de masque est le premier objet du groupe masque, c'est lui qui doit servir pour le positionnement.
Un exemple de script pour comprendre...
// JavaScript Document
// De elleere
// Place les objets sélectionnés en haut et au centre du plan de travail actif
// Places the selected objects at the top and center of the active artboard
// Contours or not to be taken into account (var visibleBds)
// -------
var visibleBds = false;
// -------
var doc = activeDocument,
Origin = doc.rulerOrigin,
largDoc = doc.width,
hautDoc = doc.height,
origX = -Origin[0],
origY = -Origin[1]+hautDoc,
objRef,
item,
dxy,
sel = selection;
for (var i = 0; i < sel.length; i++) {
item = sel[i];
objRef = item.typename == "GroupItem" && item.pageItems[0].clipping ? item.pageItems[0] : item;
dxy = visibleBds ? TopCenterV(objRef,origX,origY) : TopCenterP(objRef,origX,origY);
//dxy = TopCenterVG(objRef,origX,origY,visibleBds)
// -------
// for info
var mes = "the two functions TopCenterG() and TopCenterP() are equivalent"+"\r";
if (i == 0) alert(mes+TopCenterG(objRef,origX,origY)+"\r"+TopCenterP(objRef,origX,origY));
// -------
item.translate(dxy[0],dxy[1]);
item.selected = false;
}
// -------
function TopCenterG(obj,X,Y) {
var v = obj.geometricBounds;
var dx = X - v[0] + (activeDocument.width-(v[2]-v[0]))/2,
dy = Y - v[1];
return [dx,dy];
}
// -------
function TopCenterP(obj,X,Y) {
var dx = X - obj.position[0] + (activeDocument.width-obj.width)/2,
dy = Y - obj.position[1];
return [dx,dy];
}
// -------
function TopCenterV(obj,X,Y) {
var v = obj.visibleBounds;
var dx = X - v[0] + (activeDocument.width-(v[2]-v[0]))/2,
dy = Y - v[1];
return [dx,dy];
}
// -------
// To use this function, replace line 23 with
// dxy = TopCenterVG(objRef,origX,origY,visibleBds)
// and remove the other three functions
function TopCenterVG(obj,X,Y,at) { // at for visibleBds
var v = at ? obj.visibleBounds : obj.geometricBounds;
var dx = X - v[0] + (activeDocument.width-(v[2]-v[0]))/2,
dy = Y - v[1];
return [dx,dy];
}
// -------
René
Copy link to clipboard
Copied
@renél80416020, yes this works perfectly for simple objects where the mask is the first item that encompasses all. The main purpose of the function I linked above is to cover more edge cases. For example, if the selected object is a group that includes a mask along with other objects (like the attached file), the helper function still gives the correct info so that you can calculate a proper translation matrix. Your script probably covers most people's needs, I just needed more coverage for a few more involved scripts I was working on. Cheers!
Copy link to clipboard
Copied
Bonjour @jduncan,
Merci pour votre remarque qui est très pertinente, mon script est forcément limité, c'était surtout dans un but pédagogique afin de permettre à @andyf65867865 de progresser dans sa compréhension des propriétés position, geometriqueBounds et visibleBounds (en bref tout simplement tenter de répondre à la question posée).
j'avoue ne pas avoir pensé à chercher plus loin.
Déformation professionnelle, j'ai enseigné pendant 38 années...
Cependant j'ai une question, si un objet masque est mini d'un contour, ce qui est possible, ne doit on pas en tenir compte dans visiblebounds?
Exemple: Un des masques du groupe sélectionné possède un contour noir de 11pts,
la bonne réponse serait plutôt celle du bas...
Le non de la fonction getVisibleBounds peut prêter à confusion?
René
Copy link to clipboard
Copied
@renél80416020, you have a keen eye and are definitely right. This has come up before to be honest but I wasn't sure how to handle it... I should have picked a different name for the function initially but by the time I realized that it might be confusing (my definition of visible bounds doesn't match the official API) others had found the function on Github and were referencing it by the name `getVisibleBounds` so I didn't want to create confusion by changing it. It is really silly actually, since the function deals exclusively with geometric bounds. It probably should have been called something like `getGeometricBoundsOfClippedItems()`.
I was kind of new to scripting when I wrote this function but a long-time Ai user, so to me, the size of an item is the size of the path (excluding any strokes) like displayed in the transform palette. So the reason I choose `visible` was to say the potential visible parts of a clipped collection of items.
A fix would be simple so maybe I'll make another utility function that does just that... Thanks for the feedback, I really appreciate it!
Copy link to clipboard
Copied
Vous êtes vraiment modeste quand vous écrivez:
"J’étais un peu novice en matière de script quand j’ai écrit cette fonction"
René
Copy link to clipboard
Copied
Voici ma version faite pour l'occasion.
// JavaScript Document
// pos masque 4 test plus.js
// Author Landry René elleere@aliceadsl.fr
// Wed, 23 October 2024 11:02:00 GMT
// Place les objets sélectionnés en haut et au centre du plan de travail actif
// Places the selected objects at the top and center of the active artboard
// Contours or not to be taken into account (variable visibleBds)
/*Note: If the variable AdjustMask = true the framing is done on the masked object
false The clipping group. */
// INIT -------
var visibleBds = true; // alignment mode visibleBounds or geometricBounds
var AdjustMask = true; // Adjust masks
var entourRect = false; // draws an ungrouped frame around the object
// ------------
var result = false;
var res = "dialog { orientation: 'column', alignChildren: 'right', \
info: Group { orientation: 'column', alignChildren: 'left', \
text: 'Choice of mode:', \
modeType: Group { orientation: 'column', alignChildren: 'left', margins: 8, \
rbtn1: RadioButton { text: 'GeometricBounds'}, \
rbtn2: RadioButton { text: 'VisibleBounds'}, \
ajkbox: Checkbox { text: 'Adjust masks?'}, \
}, \
} \
buttons: Group { orientation: 'row', \
ok: Button { text: 'OK', properties:{name:'ok'} }, \
} \
}";
var win = new Window(res);
win.frameLocation = [100,150];
win.text = "TopCenter de LR";
win.info.modeType.rbtn1.value = !visibleBds;
win.info.modeType.rbtn2.value = visibleBds;
win.info.modeType.ajkbox.value = AdjustMask;
win.buttons.ok.onClick = function() {
visibleBds = win.info.modeType.rbtn2.value;
AdjustMask = win.info.modeType.ajkbox.value;
result = true;
win.close();
}
win.show();
// -------
if (result){
var doc = activeDocument,
Origin = doc.rulerOrigin,
hautDoc = doc.height,
origX = -Origin[0],
origY = -Origin[1]+hautDoc,
objRef,
dxy,
tabs,
mask,
VG,
selg,
sel = selection;
for (var j = 0; j < sel.length; j++){
mask = false;
objRef = sel[j];
if (objRef.guides) continue;
tabs = [];
if (objRef.typename == "GroupItem" && AdjustMask) {
selg = objRef.pageItems;
detectclippingInGrp(selg,tabs);
}
VG = visibleBds ? objRef.visibleBounds : objRef.geometricBounds;
if (tabs.length > 0 && mask) {
getCadre(VG,tabs);
}
dxy = TopCenter(VG,origX,origY);
// -------
objRef.translate(dxy[0],dxy[1]);
objRef.selected = false;
if (entourRect) {
var cadre= enCadre(doc,VG,4);
cadre.translate(dxy[0],dxy[1]);
}
}
}
// -------
function TopCenter(v,X,Y) {
var dx = X - v[0] + (activeDocument.width-(v[2]-v[0]))/2,
dy = Y - v[1];
return [dx,dy];
}
// -------
function getCadre(VG,tabs) {
var xmin, ymin, xmax, ymax, vi;
xmin = ymin = Infinity;
xmax = ymax = -Infinity;
for (var i = 0; i < tabs.length; i++){
vi = visibleBds ? tabs[i].visibleBounds : tabs[i].geometricBounds;
xmin = Math.min(vi[0],xmin);
ymax = Math.max(vi[1],ymax);
xmax = Math.max(vi[2],xmax);
ymin = Math.min(vi[3],ymin);
}
if (VG[0] < xmin) VG[0] = xmin;
if (VG[1] > ymax) VG[1] = ymax;
if (VG[2] > xmax) VG[2] = xmax;
if (VG[3] < ymin) VG[3] = ymin;
}
// -------
function detectclippingInGrp (grp,tabs){
// Global variable mask
var item;
for(var i = 0; i < grp.length; i++){
item = grp[i];
if (item.clipping) {tabs.push(item); mask = true; return;}
if (item.typename == "GroupItem") {
if (item.clipped) {
tabs.push(item.pageItems[0]); mask = true; // alert('clipping');
}
else {detectclippingInGrp(item,tabs);}
}
else tabs.push(item);
}
}
// -------
function enCadre(doc,VG,color){
var cadre = doc.pathItems.rectangle(VG[1],VG[0],VG[2]-VG[0],VG[1]-VG[3]);
cadre.filled = false;
//cadre.selected = true;
cadre.stroked = true;
cadre.strokeWidth = 2;
cadre.strokeColor = doc.swatches[color].color;
return cadre;
}
// -------
Moins complète que la votre @jduncan ...
René