I have 6 groups in Photoshop that contain a number of layers within each group. I'm looking to turn on/off a layer within each group to create every possible combination of the image.
Can someone point me in the right direction?
I've never scripted in Photoshop but trying to figure this out on my own.
I'm quite new to CS5 scripting myself, but I think I can explain how it works. The code examples may not be the most effective way to do it, but it does the trick.
There is a big difference between a Group of layers or the individual layer itself.
All layers and groups are ordered in the DOM format. To get your root you can use the global instance app to get the active document: app.activeDocument.
The messy part is that there are two separate arrays for single layers and groups.
To get the array of single layers use app.activeDocument.layers and app.activeDocument.layerSets for the groups.
To go deeper down in the hieralcy, use the layerSets array to iterate down.
For example, let us assume the following hieralcy:
-Border
+Icons
+Left
-Star
-Home
+Right
-Add
-Remove
Here Border, Star, Home, Add and Remove are all single layers while Icons, Left and Right are Groups.
To turn on group Left we need to iterate down the Icon group:
Icons = app.activeDocument.layerSets.getByName("Icons");
Left = Icons.layerSets.getByName("Left");
Left.visible = true;
If you show a layer/group in CS5 by clicking with your mouse, all parent groups will automatically be shown too. By scripting this isn't the case, you have to enable all parents as well.
Icons = app.activeDocument.layerSets.getByName("Icons");
Icons.visible = true;
Left = Icons.layerSets.getByName("Left");
Left.visible = true;
To show the Border layer you need to use the layers array instead.
app.activeDocument.layers.getByName("Border").visible = true;
Same things apply if you want to show the Add layer.
Icons = app.activeDocument.layerSets.getByName("Icons");
Icons.visible = true;
Right = Icons.layerSets.getByName("Right");
Right.visible = true;
AddLayer = Right.layers.getByName("Add");
AddLayer.visible = true;
This can be a bit messy if you have a lot of groups and layers. I created a function that follows a supplied path to get the end object. It will determine by itself if it is a layer or a group.
//******************************************
// GET BY PATH
// Author: Max Kielland
//
// Gets the LayerSet or Layer at the path's end.
// Example path "Icons/left" will return the LayerSet object "Left"
// while "Icons/left/Star" will return the Layer object "Star"
// If fSetPath is true, all the parents will be visible as well.
function GetByPath(fPath,fSetPath) {
var lGroup = null;
var lPathArray = new Array();
lPathArray = fPath.split('/');
try {
lGroup = app.activeDocument.layers.getByName(lPathArray[0]);
} catch (err) {
lGroup = app.activeDocument.layerSets.getByName(lPathArray[0]);
}
if (fSetPath)
lGroup.visible = true;
for (n=1; n<lPathArray.length; n++) {
try {
lGroup = lGroup.layerSets.getByName(lPathArray[n]);
} catch(err) {
lGroup = lGroup.layers.getByName(lPathArray[n]);
}
if (fSetPath == true)
lGroup.visible = true;
}
return lGroup;
}
...and one function to simply set or clear a group or layer by its path.
//******************************************
// SET STATUS
// Author: Max Kielland
//
// Sets the Group or Layer's visible property
// at the end of the path to fStatus.
function SetStatus(fPath, fStatus) {
Obj = GetByPath(fPath,false);
Obj.visible = fStatus;
}
..and at last I wrote this function to hide all groups and/or layers from a user specified root.
/******************************************
// CLEAR GROUP
// Author: Max Kielland
//
// Clears the visible property in a single
// group/layer with the option to clear all
// its children as well (fRecurs = true).
// fLayerSet can be a layerSet object or a
// String path.
function ClearGroup(fLayerSet,fRecurs) {
var n;
var TargetGroup;
// Get LayerSet Object if reference is a string.
if (typeof fLayerSet == "string")
TargetGroup = GetByPath(fLayerSet);
else
TargetGroup = fLayerSet;
// Iterate through all LayerSets
for (n=0; n<TargetGroup.layerSets.length; n++) {
if (fRecurs == true)
ClearGroup(TargetGroup.layerSets[n],true);
else
TargetGroup.layerSets[n].visible = false;
}
// Iterate through all layers
for (n=0; n<TargetGroup.layers.length; n++) {
TargetGroup.layers[n].visible = false;
}
// Clear self
TargetGroup.visible = false;
}
Here is an example of how to use the functions
// Hide group "Icon" and its children
ClearGroup("Icons",true);
//Show the layer "Home"
GetByPath("Icons/Left/Home",true);
// To just get the object "Right"
var MyGroup = GetByPath("Icons/Right");
// Save the current document as a PNG file
app.activeDocument.saveAs(File("Scripted Document.png"),PNGSaveOptions);
I hope this is useful to someone more than just me :)
Related
How would I adapt #ghettovoice JSFiddle that saves a map to PDF to save the map to a JPEG or PNG? I have no idea how to attempt this problem so ideally if you know hoe to do it you can explain the logic behind it.
exportMap: function () {
var map = this.$refs.map
map.once('rendercomplete', function () {
var mapCanvas = document.createElement('canvas');
var size = map.getSize();
mapCanvas.width = size[0];
mapCanvas.height = size[1];
var mapContext = mapCanvas.getContext('2d');
Array.prototype.forEach.call(
document.querySelectorAll('.ol-layer canvas'),
function (canvas) {
if (canvas.width > 0) {
var opacity = canvas.parentNode.style.opacity;
mapContext.globalAlpha = opacity === '' ? 1 : Number(opacity);
var transform = canvas.style.transform;
// Get the transform parameters from the style's transform matrix
var matrix = transform
.match(/^matrix\(([^(]*)\)$/)[1]
.split(',')
.map(Number);
// Apply the transform to the export map context
CanvasRenderingContext2D.prototype.setTransform.apply(
mapContext,
matrix
);
mapContext.drawImage(canvas, 0, 0);
}
}
);
if (navigator.msSaveBlob) {
// link download attribuute does not work on MS browsers
navigator.msSaveBlob(mapCanvas.msToBlob(), 'map.png');
} else {
var link = document.getElementById('image-download');
link.href = mapCanvas.toDataURL();
link.click();
}
});
map.renderSync();
}
The problem was a combination of missing dependencies (namely FileSaver.js and fakerator.js) and a cross origin server block (CORS block) (Browsers automatically prevent httpRequests to a different domain name unless the server allows it). The first one is fixed by installing the packages while the second one is resolved by setting the crossOrigin Attribute of the ImageWMSLayer to null in my case but possibly to 'Anonymous' for other sources. Hope this helped someone else :)
I have 1 group in photoshop in which there are multiple images which are hide by default except the first one, I looking to turn show every image one by one except first one and then export as image with merged first layer
Note : Please see the screenshots Layer
Can someone point me in the direction whether it is possible with script or any action?
I've never scripted in Photoshop but trying to figure this out on my own.
Whole post edited, yet this script covers usability of the original script (that worked only on a single layer group) and also extends itself onto all layer groups.
Script takes the layers from each layer groups in the file and iterates through all of them except the first one in the given group turning them on and off for saving. First layer in each group is always visible for a given group iteration. Exports as PNG but you may adjust anything you need - export options, path, file name etc. Created for CS6 but should work with any version, just check it out.
Any more information you may need is in the reference manual. Just Google for Photoshop CS6/CC Javascript Reference.
The code's below, just copy-paste it using Notepad and save as jsx file. You may test it using ExtendScript Toolkit (it has been installed together with anything from Adobe, you just need to find it). If it works as intended, place the file within Adobe\Adobe Photoshop CS6 (64 Bit)\Presets\Scripts folder, restart Photoshop and the script will be accessible through File > Scripts menu. You may also bind a keyboard shortcut to it if this action needs to be repeated over and over many times. Cheers!
#target Photoshop
if (documents.length == 0) {
alert("nothing opened");
} else {
// start
//setup
var file = app.activeDocument;
var groupsAmount = file.layerSets.length; // get the layer groups
// iterate through groups
for (var k=0; k < groupsAmount; k++) {
hideAllLayers();
var images = file.layerSets[k].layers; // get the layers from the given group
file.layerSets[k].visible = true;
images[0].visible = true; // show the first layer in this group
// begin "i" from 1 to start from the 2nd layer
for (var i=1; i < images.length; i++) {
images[i].visible = true;
exportimage(images[i].name);
images[i].visible = false;
}
}
// function used to export image (adjust as you want accoring to the manual)
function exportimage(name){
var options = new ExportOptionsSaveForWeb();
options.format = SaveDocumentType.PNG;
options.PNG8 = false;
options.transparency = true;
options.optimized = true;
// adjust path & name
file.exportDocument(File(file.path+"/"+name+".png"),ExportType.SAVEFORWEB, options);
}
// function to hide all layers
// simplified version of http://morris-photographics.com/photoshop/scripts/downloads/Hide%20All%20Layers%202-1-0.jsx
function hideAllLayers() {
var ref = new ActionReference();
ref.putEnumerated(cTID('Lyr '), cTID('Ordn'), cTID('Trgt'));
var desc = new ActionDescriptor();
desc.putReference(cTID('null'), ref);
executeAction(sTID('selectAllLayers'), desc, DialogModes.NO);
var ref = new ActionReference();
ref.putEnumerated(cTID('Lyr '), cTID('Ordn'), cTID('Trgt'));
var list = new ActionList();
list.putReference(ref);
var desc = new ActionDescriptor();
desc.putList(cTID('null'), list);
executeAction(cTID('Hd '), desc, DialogModes.NO);
}
function cTID(s) {return app.charIDToTypeID(s);}
function sTID(s) {return app.stringIDToTypeID(s);}
//end
}
From the sample I created
I am unable to set the circle's alpha / visiblity when mousedown draw line. But I am able to console log it when its detect hitTest.. Is there advise in this?
Below is the block of codes:
var canvas, stage;
var drawingCanvas;
var oldPt;
var oldMidPt;
var title;
var color;
var stroke;
var colors;
var index;
var rect, circle1;
var currMidPt;
$(document).ready(function() {
init();
});
function init() {
canvas = document.getElementById("canvas");
index = 0;
colors = ["#828b20", "#b0ac31", "#cbc53d", "#fad779", "#f9e4ad", "#faf2db", "#563512", "#9b4a0b", "#d36600", "#fe8a00", "#f9a71f"];
//check to see if we are running in a browser with touch support
stage = new createjs.Stage(canvas);
stage.autoClear = false;
stage.enableDOMEvents(true);
createjs.Touch.enable(stage);
createjs.Ticker.setFPS(24);
createjs.Ticker.on("tick", tick);
drawingCanvas = new createjs.Shape();
stage.addEventListener("stagemousedown", handleMouseDown);
stage.addEventListener("stagemouseup", handleMouseUp);
title = new createjs.Text("Click and Drag to draw", "36px Arial", "#777777");
title.x = 300;
title.y = 200;
rect = new createjs.Shape();
rect.graphics.beginFill("#000").drawRect(0, 0, stage.canvas.width, stage.canvas.height);
var container = new createjs.Container();
container.x = 0;
container.y = 0;
stage.addChild(container, title);
stage.addChild(drawingCanvas);
circle1 = new createjs.Shape();
circle1.graphics.beginFill("#990000").drawCircle(120,120,40);
container.addChild(circle1);
stage.update();
}
function handleMouseDown(event) {
if (!event.primary) { return; }
if (stage.contains(title)) {
stage.clear();
stage.removeChild(title);
}
color = colors[(index++) % colors.length];
stroke = Math.random() * 30 + 10 | 0;
oldPt = new createjs.Point(stage.mouseX, stage.mouseY);
oldMidPt = oldPt.clone();
stage.addEventListener("stagemousemove", handleMouseMove);
}
function handleMouseMove(event) {
if (!event.primary) { return; }
var midPt = new createjs.Point(oldPt.x + stage.mouseX >> 1, oldPt.y + stage.mouseY >> 1);
drawingCanvas.graphics.clear().setStrokeStyle(stroke, 'round', 'round').beginStroke(color).moveTo(midPt.x, midPt.y).curveTo(oldPt.x, oldPt.y, oldMidPt.x, oldMidPt.y);
oldPt.x = stage.mouseX;
oldPt.y = stage.mouseY;
oldMidPt.x = midPt.x;
oldMidPt.y = midPt.y;
currMidPt = midPt;
if(circle1.hitTest(currMidPt.x, currMidPt.y)) {
console.log('test');
circle1.alpha = 0.6;
circle1.visible = false;
}
stage.update();
}
function tick(event) {
// console.log(ndgmr.checkPixelCollision(drawingCanvas,circle1,0,false));
stage.update(event);
}
function handleMouseUp(event) {
if (!event.primary) { return; }
stage.removeEventListener("stagemousemove", handleMouseMove);
}
The main reason this doesn't work is because you are using a stage that never clears itself. This gives you the benefit of a "paint brush", since it just adds new curves as you draw, but the circle can never be removed, since it is painted on to the canvas. If you change the alpha, it just draws on top of the current circle. This is also why your circle gets all aliased, as it constantly draws on top of itself, multiplying the alpha.
Here is a quick edit that shows the circle moving its x position instead of adjusting the alpha. Any time you roll over the original position it will move 10 pixels to the right.
http://codepen.io/lannymcnie/pen/redrgo?editors=0010
The hitTest code you have used is not correct though, since it always checks the x/y position of the pen against the local coordinates of the circle -- this means the position of the circle doesn't matter. To fix this, you need to find the local coordinates instead:
currMidPt = circle1.globalToLocal(midPt.x, midPt.y);
Here is an updated fiddle showing that behaviour: http://codepen.io/lannymcnie/pen/grejVg?editors=0010
However, to get the effect you are probably looking for, you have to take a different approach. Instead of using the stage auto-clear, use a cached Shape, and update the cache when you draw. This will provide the paint effect, but let the rest of the stage get updated as usual.
// Remove this!
stage.autoClear = false;
// Cache the drawingCanvas once after creating it
drawingCanvas = new createjs.Shape();
drawingCanvas.cache(0,0,canvas.width,canvas.height);
// After drawing, update the cache. This is at the end of the mousemove function.
// Use "source-over" to apply the new contents on top, instead of clearing the cache.
drawingCanvas.updateCache("source-over");
stage.update();
I also made a few other changes:
Removed the Ticker listener that updates the stage. Your mousemove already updates the stage when the contents change. If you want to reintroduce the ticker update for other content, then remove the stage.update() calls everywhere else, as they are redundant.
Moved the drawingCanvas below the circle/container. This just makes sure the circle is always visible for the demo
Here is a final demo with these changes:
http://codepen.io/lannymcnie/pen/NNYLPX?editors=0010
Hope that helps!
I want to limit map extent to the initial extent of the map and limit user from panning more than certain extent.
I tried following but nothing has changed:
map = new Map( "map" , {
basemap: "gray",
center: [-85.416, 49.000],
zoom : 6,
logo: false,
sliderStyle: "small"
});
dojo.connect(map, "onExtentChange", function (){
var initExtent = map.extent;
var extent = map.extent.getCenter();
if(initExtent.contains(extent)){}
else{map.setExtent(initExtent)}
});
Just to flesh out Simon's answer somewhat, and give an example. Ideally you need two variables at the same scope as map:
initExtent to store the boundary of your valid extent, and
validExtent to store the last valid extent found while panning, so that you can bounce back to it.
I've used the newer dojo.on event syntax as well for this example, it's probably a good idea to move to this as per the documentation's recommendation - I assume ESRI will discontinue the older style at some point.
var map;
var validExtent;
var initExtent;
[...]
require(['dojo/on'], function(on) {
on(map, 'pan', function(evt) {
if ( !initExtent.contains(evt.extent) ) {
console.log('Outside bounds!');
} else {
console.log('Updated extent');
validExtent = evt.extent;
}
});
on(map, 'pan-end', function(evt) {
if ( !initExtent.contains(evt.extent) ) {
map.setExtent(validExtent);
}
});
});
You can do the same with the zoom events, or use extent-change if you want to trap everything. Up to you.
It looks like your extent changed function is setting the initial extent variable to the maps current extent and then checking if that extent contains the current extents centre point - which of course it always will.
Instead, declare initExtent at the same scope of the map variable. Then, change the on load event to set this global scope variable rather than a local variable. In the extent changed function, don't update the value of initExtent, simply check the initExtent contains the entire of the current extent.
Alternatively you could compare each bound of the current extent to each bound of the initExtent, e.g. is initExtent.xmin < map.extent.xmin and if any are, create a new extent setting any exceeded bounds to the initExtent values.
The only problem is these techniques will allow the initExtent to be exceeded briefly, but will then snap the extent back once the extent changed function fires and catches up.
I originally posted this solution on gis.stackexchange in answer to this question: https://gis.stackexchange.com/a/199366
Here's a code sample from that post:
//This function limits the extent of the map to prevent users from scrolling
//far away from the initial extent.
function limitMapExtent(map) {
var initialExtent = map.extent;
map.on('extent-change', function(event) {
//If the map has moved to the point where it's center is
//outside the initial boundaries, then move it back to the
//edge where it moved out
var currentCenter = map.extent.getCenter();
if (!initialExtent.contains(currentCenter) &&
event.delta.x !== 0 && event.delta.y !== 0) {
var newCenter = map.extent.getCenter();
//check each side of the initial extent and if the
//current center is outside that extent,
//set the new center to be on the edge that it went out on
if (currentCenter.x < initialExtent.xmin) {
newCenter.x = initialExtent.xmin;
}
if (currentCenter.x > initialExtent.xmax) {
newCenter.x = initialExtent.xmax;
}
if (currentCenter.y < initialExtent.ymin) {
newCenter.y = initialExtent.ymin;
}
if (currentCenter.y > initialExtent.ymax) {
newCenter.y = initialExtent.ymax;
}
map.centerAt(newCenter);
}
});
}
And here's a working jsFiddle example: http://jsfiddle.net/sirhcybe/aL1p24xy/
In one of my projects I use a dijit.Tree control. I need to add a search to the tree and show only those nodes/leafs which have the searched term in them. However I can't seem to figure out how that can be achieved. Could anybody please help me?
im not entirely sure that your question entirely but it should give hint whereas to go.
Lets use reference documentation example as offset, there is 1) a store 2) a model and 3) the tree
var store = new ItemFileReadStore({
url: "{{dataUrl}}/dijit/tests/_data/countries.json"
});
var treeModel = new ForestStoreModel({
store: store,
query: {"type": "continent"}, // note, this bit
rootId: "root",
rootLabel: "Continents",
childrenAttrs: ["children"]
});
new Tree({
model: treeModel
}, "treeOne");
Interpret the above as such; You have loaded all known countries and continents but 'user' has selected only to show continents by using query on the model - and the hierachy is then represented in a tree structure.
You want a textbox with searching capeabilities, so we hook into onChange
new dijit.form.TextBox({
onChange: function() {
...
}
});
First bit, getting variables
var searchByName = this.get("value");
var oQuery = treeModel.query;
Next, set a new query on the model - preserving the old ones with an object mixin
treeModel.query = dojo.mixin(oQuery, { name: '*'+searchByName+'*' });
Last, notify the model and its tree that changes has occurred - and requery the visible items.
treeModel._requeryTop();
NB If the top-level item (for ForestModel) is not visible, none of its child elements will show, even if the search-string matches those. (Examplewise, Alabama is not shown if US Continent is not matched by query)
EDIT
As OP has the agenda to go by the 'NB', this may not fit needs 100% but its what dojo offers with dijit.Tree.. As it will get rather a lengthy process to recode the model/store queries to include parentbranches up until root i will not do this here - but there are a few tricks still ;)
var tree = new dijit.Tree( {
/**
* Since TreeNode has a getParent() method, this abstraction could be useful
* It sets the dijit.reqistry id into the item-data, so one l8r can get parent items
* which otherwise only can be found by iterating everything in store, looking for item in the parent.children
*
*/
onLoad : function() {
this.forAllNodes(function(node) {
// TreeNode.item <-- > store.items hookup
node.item._NID = node.domNode.id
});
},
/* recursive iteration over TreeNode's
* Carefull, not to make (too many) recursive calls in the callback function..
* fun_ptr : function(TreeNode) { ... }
*/
forAllNodes : function(parentTreeNode, fun_ptr) {
parentTreeNode.getChildren().forEach(function(n) {
fun_ptr(n);
if(n.item.children) {
n.tree.forAllNodes(fun_ptr);
}
})
}
});
(non-tested, but might just work) Example:
// var 'tree' is your tree, extended with
tree.forAllNodes = function(parentTreeNode, fun_ptr) {
parentTreeNode.getChildren().forEach(function(n) {
fun_ptr(n);
if(n.item.children) {
n.tree.forAllNodes(fun_ptr);
}
})
};
// before anything, but the 'match-all' query, run this once
tree.forAllNodes(tree.rootNode, function(node) {
// TreeNode.item <-- > store.items hookup
node.item._NID = node.domNode.id
});
// hopefully, this in end contains top-level items
var branchesToShow = []
// run fetch every search (TextBox.onChange) with value in query
tree.model.store.fetch(query:{name:'Abc*'}, onComplete(function(items) {
var TreeNode = null;
dojo.forEach(items, function(item) {
TreeNode = dijit.byId(item._NID+'');
while(TreeNode.getParent()
&& typeof TreeNode.getParent().item._RI == 'undefined') {
TreeNode = TreeNode.getParent();
}
branchesToShow.push(TreeNode.item);
});
}});
// Now... If a success, try updating the model via following
tree.model.onChildrenChange(tree.model.root, branchesToShow);