I am having problems in a very simple task in photoshop script, but I am afraid this is not documented elsewhere on the web.
I just want to invert an existing selection, delete the content of the current layer, and then invert back the selection.
Of course this is part of a larger program, and if you are interested, i can provide all details.
Up to know i am making:
// Get current document and current layer
var doc = app.activeDocument;
var activeLay = doc.activeLayer;
var a=0
// find the current layer and assign its code to the variable a
for(i=doc.layers.length-1; i >=0; )
{
if(doc.layers[i]==activeLay)
{
a=i;
alert("a"+a);
break;
}
else{ i--; }
alert ("i"+i);
}
// Now cycle remaining layer under the exsiting one, and jump the selection
// and delete the outer area of selection for each layer
for(i=a-1; i >=0;)
{
// make layer i active
doc.activeLayer=doc.layers[i];
alert ("active layer"+i);
// where is my selection in regards to the active layer?
var s = app.activeDocument.selection.bounds;
var xSo=s[0];
var ySo=s[1];
var xLo = activeLay.bounds[0].value;
var yLo = activeLay.bounds[1].value;
// I have to go from actual selection poisition to the NEXT layer... which i just made active...
DeltaX=xLo-s[0];
DeltaY=ySo-s[1];
doc.selection.translateBoundary(DeltaX,DeltaY);
//Now invert selection and delete
doc.selection.invert
doc.selection.fill (fillType, mode, 0, preserveTransparency) // ??? here what i cannot do?
doc.selection.invert
i--;
}
I found the answer myself...
it is merely
//Now invert selection and delete
doc.selection.invert;
doc.selection.cut();
doc.selection.invert;
my problem was that i didn't know that javascript is case sensitive, and hence .cut() didn't' work for when i misspelled it.
Related
Firstly - I am a novice who has picked away at getting something functional for me over the past few years. My code below basically works - it's just complex and I believe there must be a better approach. Looking for a kick in different direction.
I am using javascript and leaflet to display an array of moving markers (players). The data on the markers comes to me in a table - listing an ID for each marker, a location and other information not needed for this purpose. The table of data will have a changing number of markers (ie: just one (MYID), or that plus one other, two or even more 'others'), and it will have multiple entries for the each marker (same ID and location, just the other data is different).
One of the markers ids is the same as "MYID" which I then treat differently, but for the others (if any) I have a function that puts them on my map. And each time the function is called it should move them to their new location data, but I currently don't know how to do that elegantly. So I am presently looking to delete all and recreate them each time the function is called (triggered by each time anything changes). Not so good coding.
function updatemap(displaytable,MYID) { // Update other player locations on map
OPN=[]; //reset OPN array
OPX=[]; //reset OPX array
OPY=[]; //reset OPY array
for (var r=0;r<OPLoc.length;r++){
// Need to remove all OPloc off map first and then re-create them?
};
OPLoc=[];
var q=0;
for (var p=0; p<displaytable.length; p++){ // for every line of the displaytable
if ((!OPN.includes(displaytable[p].id)) && (displaytable[p].id != MYID)){ // ... create a unique other player entry, once only
OPN.push(displaytable[p].id);
OPX.push(displaytable[p].lat);
OPY.push(displaytable[p].lon);
OPLoc[q] = new L.marker([displaytable[p].lat,displaytable[p].lon], {icon: oplayericon})
.addTo(mymap)
.bindPopup(displaytable[p].id + "<br>" + displaytable[p].lat + "," + displaytable[p].lon);
q++;
};
};
//...other code not relevant
};
None of the variable/arrays in the function are needed anywhere else for anything - I've just created all this mess to do this one thing. And the arrays are created globally so that they are available next time it's called.
I assume there are functions/capabilities of the array commands that I am just not aware of. Can anyone suggest a better way? please?
well, still not sure it's elegant - but it's functional. Not sure if there are edge cases that I'm not covering - but this appears to work and do what I need. I only use two arrays now (one for the markers and one for the ids so I can keep track of what's active). I tried making a 2D array but that made me sad, so it's two arrays now.
for (var s=0;s<displaytable.length;s++) { // display all other players
if (OPid.includes(displaytable[s].id)) { //if the player is already on the map, just update location
OPLoc[OPid.indexOf(displaytable[s].id)].setLatLng([displaytable[s].lat,displaytable[s].lon]);
} else { // if the player is not MYID and not already on the map, create it
if (displaytable[s].id != MYID) {
OPid.push(displaytable[s].id);
OPLoc[OPid.indexOf(displaytable[s].id)] = new L.marker([displaytable[s].lat,displaytable[s].lon], {icon: oplayericon})
.addTo(mymap)
.bindPopup(displaytable[s].id);
};
};
};
for (var r=0;r<OPid.length;r++) { //check if the OPlayer is still active
var found = false;
for(var q=0;q<displaytable.length;q++) {
if (displaytable[q].id == OPid[r]) {
found = true;
};
};
if (!found) { //if the OPlayer id is no longer in the displaytable
mymap.removeLayer(OPLoc[r]); //remove it from the map
OPLoc.splice(r,1); //remove it from the OPLoc array
OPid.splice(r,1); //remove it form the OPid array
};
};
```
I want to be able to go to the layer above. I have over 3000 files that i need to select the layer above from and I cannot seem to work out how to do it.
It always has a different name too. But i always start from the same layer and it's always in the same position.
I need this to get the contents of a text layer.
Any ideas? I've been at it a while now but my Javascript knowledge is limited.
Thanks
There might be a smarter way to do this, but the following should work. You can basically tell the layer's z-position by its itemIndex property. So once you have that you can search the one with an itemIndex which is one higher than the current one. When you found it, you can make sure it's a text layer and if so, retrieve it's text contents.
var textContent = "";
var doc = app.activeDocument;
var ix = doc.activeLayer.itemIndex;
for(var i = 0; i < doc.layers.length; i++) {
if(doc.layers[i].itemIndex === ix + 1 && doc.layers[i].kind === LayerKind.TEXT) {
textContent = doc.layers[i].textItem.contents;
break;
}
}
alert("The text content is: " + textContent);
I want to write a script that does the following:
select some textframes
center the selection horizontally
distribute the frames horizontally
the whole routine has to be done on several pages
My first problem is, that I only want to select textframes from a unlocked layer. I found several solutions where all textframes, even on locked layers, were selected.
And the only solution I found so far that just selects the textframes from my unlocked layer (app.menuActions.item("$ID/Select &All").invoke();) doesn't provide an object I can work with (adjust, distribute) afterwards.
Is there a solution to my requirements?
Edit:
My last attempt looked like this (for a single page, I didn't used a loop for several pages while testing):
// 'allPageItems' erfasst alle Rahmen, zusätzlich Gruppen und Bilder
var allObjects = app.activeDocument.layoutWindows[0].activeSpread.allPageItems;
// eine Schleife durch die Objekte
for (var n=0; n<allObjects.length; n++) {
var curObject = allObjects[n];
// prüfen, ob Textrahmen
if (curObject.constructor.name == "TextFrame") {
// verankerte Textrahmen ausschliessen
if (curObject.parent != "[object Character]") {
// zur Auswahl hinzufügen
curObject.select(SelectionOptions.ADD_TO);
}
}
}
Generally, selection is something that is intended for UI interaction, not for scripting. Therefore you should avoid handling all the selection stuff in your script and collect the textFrames as objects in an array that you then can use to do the other stuff.
This should work:
#target indesign
var doc = app.activeDocument;
var curSpread = doc.layoutWindows[0].activeSpread;
var spreadItems = curSpread.allPageItems;
var distObjects = [];
// collect all relevant objects in distObjects
for (var i = 0; i < spreadItems.length; i += 1) {
var si = spreadItems[i];
// skip if itemLayer is locked
if (si.itemLayer.locked) continue;
// skip if item is not a textFrame
if (!(si instanceof TextFrame)) continue;
// skip if item is anchored
if (si.parent.constructor.name === "Character") continue;
distObjects.push(si);
};
// group all collected objects to center them, then ungroup
var distGroup = curSpread.groups.add(distObjects);
doc.align([distGroup], AlignOptions.HORIZONTAL_CENTERS, AlignDistributeBounds.SPREAD_BOUNDS);
distGroup.ungroup();
// distribute all objects horizontally
doc.distribute(distObjects, DistributeOptions.HORIZONTAL_CENTERS, AlignDistributeBounds.ITEM_BOUNDS);
Note: If this is used with an older ID version (prior to CC2014 I believe), after ungrouping, all pageItems will stay on the same layer. The feature for them to move back to the original layer was only introduced recently. If you need a solution for an older InDesign version, you would need to calculate the bounds of the group of objects you found, then offset them all one by one, so the entire "selection" can get centered.
I am writing a script for After Effects where as one of the steps I need to clear all the keyframes in . I currently have
for (highestIndex = prop.numKeys; highestIndex > 0; highestIndex--) {
prop.removeKey(highestIndex);
}
which works fine, except that it takes a few (very noticeable) seconds to run. In the GUI, there is the little stopwatch next to the property that can clear all the keyframes quickly. The property isTimeVarying (bool indicating if there are any keyframes) is read only, and I can't seem to find a setTimeVarying or similar method. Is there a way to do said thing?
You can do it using menu commands, but you've got to be very careful about what is selected and what is not, and be very sure that the comp is open in a viewer, and the viewer is active. For that, you'll need at least CS6.
function removeAllKeys(props){
var deselectAllId = app.findMenuCommandId("Deselect All");
var clearId = app.findMenuCommandId("Clear");
var comp, oldSelection, i;
// assumed: all props belong to the same comp
if (props.length===0) return;
comp = props[0].propertyGroup(props[0].propertyDepth).containingComp;
oldSelection = comp.selectedProperties;
app.beginUndoGroup("Remove All Keys");
// make sure that the comp is open in a viewer (essential, otherwise: catastrophy)
comp.openInViewer();
// deselect everything:
app.executeCommand(deselectAllId);
for (i=0; i<props.length; i++){
if (props[i].numKeys>0){
props[i].selected = true;
app.executeCommand(clearId);
app.executeCommand(deselectAllId);
};
};
for (i=0; i<oldSelection.length; i++){
oldSelection[i].selected = true;
};
app.endUndoGroup();
return;
};
removeAllKeys(app.project.activeItem.selectedProperties);
Give this a try:
var targetComp = app.project.activeItem; // Collect the active composition
var selectedLayer = targetComp.selectedLayers; // Collect the selected layers
// Identify the target parameter to be deleted
var targetParam = selectedLayer[0].transform.position; // Target the Position paramter of the first selected layer
// A. Delete the Keyframes forward from FIRST frame to LAST frame
while (targetParam.numKeys != 0) { // While there are still Keyframes, continue looping
targetParam.removeKey(1); // Delete the first Keyframe
}
// B. Delete the Keyframes backward from LAST frame to FIRST frame
for (i = targetParam.numKeys; i != 0; i--) { // Cycle through the Keyframes
targetParam.removeKey(i); // Remove the current Keyframe
}
You only need either A or B depending on whether you want to cycle forward and end up with the value of the last keyframe or cycle backward and end up with the value of the first keyframe.
As it says in the After Effects Scripting Guide page 140 :
To remove more than one keyframe, you
must start with the highest index number and work down to the lowest to ensure that the remaining indices
reference the same keyframe after each removal.
So you can't delete more than one keyframe at a time which is really a shame, but if you are looking for a fastest way, I searched and I found nothing so I think there is no better way..
But here is what you can do, you can't delete the keyframes but you can make the animation not change during all the time, you just have to add the expression valueAtTime(0)
to your property like this :
yourProperty.expression = "valueAtTime(0)";
See the After Effects Scripting Guide page 129.
I hope it helped you :)
I have two disconnected components. One of them is a "control panel" and each node in it, when clicked, is to trigger an event that removes certain edges from the other, based on a weight the edges have.
cy.on('tap', 'node', function(evt){
var node = evt.cyTarget;
var clicked_val = node.data('value');
// What is the value of the clicked node in the "control" graph?
if (typeof(clicked_val) != "undefined"){
// Only "control panel" graph nodes have 'value'
var to_restore = cy.edges("[weight > 0]");
to_restore.restore();
// Restore everything, then...
var to_remove = cy.edges("[weight < "+clicked_val+"]");
cy.remove(to_remove);
// Remove edges whose weight is less than those you want.
}
});
The line cy.edges("[weight > 0]"); should grab every edge (in the non-control graph), and in some tests does seem to. However, to_restore.restore(); doesn't bring them all back.
All edges have unique ids, that shouldn't be a problem.
Any thoughts appreciated. Am I not using restore(); correctly?
You're querying the graph and then calling restore on the elements in the result of that query. That means you're calling restore() on elements already in the graph -- having no effect. Keep refs to removed elements to use restore() properly.