Making Illustrator layer combination script - loops

I need to make a jsx script for illustrator. I need to a script that do the following:
Take all sublayers from main layers, and recursive make layer combinations.
Layer hierarchi would be like this:
Main layer 1
White text
Black text
Purple text
Main layer 2
Red background
Green background
Blue background
E.g.:
Red background + white text
Red background + Black text
Red background + Red text
Green background + white text
Green background + Black text
Green background + Red text
Blue background + white text
Blue background + Black text
Blue background + Red text
So I would like to recursively go through the layers sublayers, and make combination from those. There could be many layers with sublayers in them, this is just an example.
I hope all this makes sense.

As far as I can tell it doesn't even need any recursion. You can just copy all the items from a top layer to the next bottom layer and remove the top layer afterward. And repeat it while there is more than one layer in the document.
var doc = app.activeDocument;
// repeat the function for two top layers while there is more that one layer
while (doc.layers.length > 1) main();
// paste every sublayer of a top layer into the syblayers of the second layer
function main() {
// top layer (source) and its sublayers
var src_layer = doc.layers[0];
var src_sublayers = src_layer.layers;
var src_sublayers_names = [];
for (var i=0; i<src_sublayers.length; i++) src_sublayers_names.push(src_sublayers[i].name);
// second layer (destination) and its sublayers
var dest_layer = doc.layers[1];
var dest_sublayers = dest_layer.layers;
var dest_sublayers_names = [];
for (var i=0; i<dest_sublayers.length; i++) dest_sublayers_names.push(dest_sublayers[i].name);
// put objects from the source sublayers into the destination sublayers
for (var i=0; i<dest_sublayers_names.length; i++) {
var dest_sublayer = dest_sublayers[dest_sublayers_names[i]];
for (var j=0; j<src_sublayers_names.length; j++) {
var src_sublayer = src_layer.layers[src_sublayers_names[j]];
// select all items on the source sublayer and the destination sublayer
src_sublayer.hasSelectedArtwork = true;
dest_sublayer.hasSelectedArtwork = true;
app.copy();
app.selection = null;
// make a new destination sublayer and paste the items into it
var new_dest_sublayer = dest_sublayers.add();
new_dest_sublayer.name = (dest_sublayer.name + ' + ' + src_sublayer.name);
app.activeDocument.activeLayer = new_dest_sublayer;
app.executeMenuCommand('pasteFront');
app.selection = null;
}
}
// remove the source layer with its sublayers
src_layer.remove();
// remove the old destination sublayers
for (var i=0; i<dest_sublayers_names.length; i++) {
dest_layer.layers[dest_sublayers_names[i]].remove();
}
}
Start (3 layers, 8 sublayers):
The result (18 combinations):
If you want to save all the sublayers as separate AI files here is the function:
function save_sublayers() {
var doc = app.activeDocument;
var sublayers = doc.layers[0].layers;
for (var i=0; i<sublayers.length; i++) {
var sublayer = sublayers[i];
sublayer.hasSelectedArtwork = true;
app.copy();
app.selection = null;
var file = File(doc.path + '/' + sublayer.name + '.ai');
var new_doc = app.documents.add();
app.paste();
new_doc.saveAs(file);
new_doc.close(SaveOptions.DONOTSAVECHANGES);
}
}
You can call the function save_sublayers() after the line while (doc.layers.length > 1) main();
It saves all items from the sublayers of the first (and only) layer into separate AI files into the parent folder of current document. File names are the same as sublayers names.

Related

WPF event within a frame stored on a stackpanel

I need to recreate a program similar to whatsapp that can send and receive messages, images videos and audio. I have created a WPF form to show messages that looks like this:
I have a stack panel that contains text bubbles on them. Text messages work fine but if I send an image I want the user to be able to click on the image text bubble and it must become full screen. The image text bubble consists of a label that has a frame in it and then within that frame the image is stored. The label was used since we could resize the label.
However, because the image is done like this dynamically, we cannot seem to register an on-click event on this image bubble. If you have any better ways that we can display the image or how to log this event it would be much appreciated. Here is the method used to add the image.
public void AddMessage_Image(string path, string displayName, int role, string date = "")
{
//Create an image from the path
ImageBrush image = new ImageBrush();
image.ImageSource = new BitmapImage(new Uri(path, UriKind.Absolute));
image.Stretch = Stretch.Uniform;
//Create a frame in which to place the image
Frame fr = new Frame();
fr.Background = image;
fr.MinHeight = 120;
fr.MinWidth = 160;
//Ensure scalabilty of the image
Viewbox vb = new Viewbox();
vb.Child = fr;
vb.Stretch = Stretch.Uniform;
//Place the image in a sizable container
Label lbl = new Label();
lbl.MinHeight = 10;
lbl.MinWidth = 10;
lbl.MaxHeight = 300;
lbl.MaxWidth = 400;
lbl.Content = vb;
if (role == (int) Role.Sender)
lbl.HorizontalAlignment = HorizontalAlignment.Right;
else
lbl.HorizontalAlignment = HorizontalAlignment.Left;
lbl.Background = Brushes.Black;
//Place the image in the chat
chatbox.Children.Add(lbl);
}

Hide some points in Oxyplot Line series

I need to show/hide some data points in oxyplot line series. Is it possible?
Though some markers are invisible, the line should go through the invisible markers.
You could make use of two series to achieve this. The first one would draw the complete points (and line) without the marker. The second series would draw the visible points(with marker,but with line style set to none). For Example
DataPoint[] points = new DataPoint[]
{
new DataPoint(1,12),
new DataPoint(2,10),
new DataPoint(3,9),
new DataPoint(4,13),
new DataPoint(5,14),
new DataPoint(6,10)
};
var seriesComplete = new OxyPlot.Series.LineSeries();
seriesComplete.Points.AddRange(points);
var seriesVisible = new OxyPlot.Series.LineSeries();
seriesVisible.Points.AddRange(points.Where(x => x.Y % 2 == 0));
seriesVisible.MarkerFill = OxyColors.Blue;
seriesVisible.MarkerType = MarkerType.Circle;
seriesVisible.MarkerSize = 10;
seriesVisible.LineStyle = LineStyle.None;
this.MyModel.Series.Add(seriesComplete);
this.MyModel.Series.Add(seriesVisible);
Result is attached as image

laying out images in Word document programatically

I'm trying to generate a Word document through my application coded in WPF. In that document, I also need to layout few images along with caption as shown in the image below.
All the images are stored in database as base64 string. I'm able to load the images as "BitmapImage" object in the document however not sure how to layout the images as shown in image. Code snippet to load the images in document is as below :
var bookmarks = wordDoc.Bookmarks;
var range = bookmarks["ExternalImage"].Range;
foreach (var image in ExternalImages) // here image is "BitmapImage" object
{
float scaleHeight = (float)250 / (float)image.Image.PixelHeight;
float scaleWidth = (float)250 / (float)image.Image.PixelWidth;
var min = Math.Min(scaleHeight, scaleWidth);
var bitmap = new TransformedBitmap(image, new ScaleTransform(min, min));
System.Windows.Clipboard.SetImage(bitmap);
range.Paste();
}
How can I lay out the images as shown in image above along with caption? Note that I'm not loading images from file but from memory object.
Based on the direction provided by #CindyMeister in comments, following is the working code snippet to layout the images using code :
imageTable = wordDoc.Tables.Add(sel.Range, rows, cols, ref oMissing, ref oMissing);
imageTable.AllowAutoFit = true;
row = 1; col = 1;
foreach (var image in Images)
{
float scaleHeight = (float)475 / (float)image.PixelHeight;
// here 475 is approx image size I want in word document
float scaleWidth = (float)475 / (float)image.PixelWidth;
var min = Math.Min(scaleHeight, scaleWidth);
var bitmap = new TransformedBitmap(image, new ScaleTransform(min, min));
System.Windows.Clipboard.SetImage(bitmap);
//more efficient/faster in C# if you don't "drill down" multiple times to get an object
Word.Cell cel = imageTable.Cell(row, col);
Word.Range rngCell = cel.Range;
Word.Range rngTable = imageTable.Range;
rngCell.Paste();
cel.VerticalAlignment = WdCellVerticalAlignment.wdCellAlignVerticalCenter;
rngCell.ParagraphFormat.Alignment = WdParagraphAlignment.wdAlignParagraphCenter;
// set caption below image
rngTable.ParagraphFormat.SpaceAfter = 6;
rngCell.InsertAfter(image.Caption);
rngTable.Font.Name = "Arial Bold";
row++;
}
This code I have posted for reference, only, to let people have some starting point. Any suggestions welcome.

Scale components in FlowLayout as big as possible

How can I scale PictureBox components to best fit the given space on the screen while keeping their aspect ratio (interdependent of the actual image or its SizeMode) ?
I tested setting the Dock of the FlowLayout and the PictureBox to Fill. I also tested using a Panel as a wrapper and tested different settings for AutoSize and AutoSizeMode.
To give more information about the background: I want to dynamically add and remove images in the viewport of the application, so a TableLayout is in the first step sort of to static. I have to admit I'm was also thinking of calculating the size an position manually - or to dynamically adapt the row and column count of the TableLayout - but it seems to me prone to errors. I thought having a FlowLayout and automatically sized components should be the correct way - but it seems not to work that way. (To speak as web developer, I simply want to "float the images left", "with width and height set to 'auto'" and no scrolling.)
The images should visualize this a bit: the first figure should point out the layout, if there is only one PictureBox - it takes the whole space (or as big as possible with the given aspect ratio). The second shows how I would like the Layout to be, if there are two (three or four) images. The third figure is showing basically a resized window with three (to six) images.
Is there some point I'm missing?
This code snippet do this:
It arranges the visible controls inside a container in respect of the aspect ratio (see R variable in the code), and uses the container margin values to get horizontal and vertical gaps between items. The padding of the container is also handled.
public static void Arrange(Control container)
{
var H = container.DisplayRectangle.Height;
var W = container.DisplayRectangle.Width;
var N = container.Controls.OfType<Control>().Count(c => c.Visible);
var R = 4 / 3d; // item aspect ratio
var margin = container.Margin;
var padding = container.Padding;
var horizontalGap = margin.Left + margin.Right;
var verticalGap = margin.Top + margin.Bottom;
if (N == 0)
return;
var bestSizedItem = (
// Try n rows
Enumerable.Range(1, N).Select(testRowCount =>
{
var testItemHeight = (H - verticalGap * (testRowCount - 1)) / testRowCount;
return new
{
testColCount = (int)Math.Ceiling((double)N / testRowCount),
testRowCount = testRowCount,
testItemHeight = (int)testItemHeight,
testItemWidth = (int)(testItemHeight * R)
};
})
// Try n columns
.Concat(
Enumerable.Range(1, N).Select(testColCount =>
{
var testItemWidth = (W - horizontalGap * (testColCount - 1)) / testColCount;
return new
{
testColCount = testColCount,
testRowCount = (int)Math.Ceiling((double)N / testColCount),
testItemHeight = (int)(testItemWidth / R),
testItemWidth = (int)testItemWidth
};
})))
// Remove when it's too big
.Where(item => item.testItemWidth * item.testColCount + horizontalGap * (item.testColCount - 1) <= W &&
item.testItemHeight * item.testRowCount + verticalGap * (item.testRowCount - 1) <= H)
// Get the biggest area
.OrderBy(item => item.testItemHeight * item.testItemWidth)
.LastOrDefault();
Debug.Assert(bestSizedItem != null);
if (bestSizedItem == null)
return;
int x = container.DisplayRectangle.X;
int y = container.DisplayRectangle.Y;
foreach (var control in container.Controls.OfType<Control>().Where(c => c.Visible))
{
control.SetBounds(x, y,
bestSizedItem.testItemWidth,
bestSizedItem.testItemHeight);
x += bestSizedItem.testItemWidth + horizontalGap;
if (x + bestSizedItem.testItemWidth - horizontalGap > W)
{
x = container.DisplayRectangle.X;
y += bestSizedItem.testItemHeight + verticalGap;
}
}
}
I put this snippet on Gist so you can contribute if you wish.

Silverlight specific Image shifting to the right

I am generating a set images to form a human body so that I can use for a physics engine.
The images generated are in a specific user control in where I set the dimentions and co-ordinates of each image. That usercontrol is then loaded in another user control but for some reason when the images are loaded, one specific image which I named (rightBicep) is shifting to the right. Here is a screenshot :
alt text http://img193.imageshack.us/img193/592/imageshift.jpg
I illustrated the positions of the images with dotted lines, the green dotted line is refering to where the image should be located, and the red dotted line is where the image is being shown.
The weird thing is the image beneath it (called rightForearm) take's it's LeftPosition from it, and when during debugging they have the exact same leftProperty value. Here's the syntax :
public void generateRightBicep(string imageUrl)
{
rightBicep = new Image();
rightBicep.Name = CharacterName + "rightbicep";
Uri imageUri = new Uri(imageUrl, UriKind.Relative);
LayoutRoot.Children.Add(rightBicep);
rightBicep.Source = new BitmapImage(imageUri);
rightBicep.ImageOpened += new EventHandler<RoutedEventArgs>(bodyPart_ImageOpened);
}
public void rightBicepLoaded()
{
var bi = waitTillImageLoad(rightBicep.Name);
rightBicep.Height = elbowToArmpit + (2 * palm);
rightBicep.Width = ratio(bi.PixelHeight, bi.PixelHeight, rightBicep.Height); // to be determined
Vector2 topVector;
topVector.X = (float)(Convert.ToDouble(torso.GetValue(Canvas.LeftProperty)) - palm);
topVector.Y = (float)(Convert.ToDouble(neck.GetValue(Canvas.TopProperty)) + neck.Height);
if (!faceRight)
{
perspectiveVectorHeight(ref topVector, ref rightBicep, torso.Width);
rightBicep.Width = ratio(bi.PixelHeight, bi.PixelHeight, rightBicep.Height);
}
rightBicep.SetValue(Canvas.LeftProperty, Convert.ToDouble(topVector.X));
rightBicep.SetValue(Canvas.TopProperty, Convert.ToDouble(topVector.Y));
rightBicep.SetValue(Canvas.ZIndexProperty, rightBicepZindex);
generateRightShoulder();
}
public void generateRightForearm(string imageUrl)
{
rightForearm = new Image();
rightForearm.Name = CharacterName + "rightforearm";
Uri imageUri = new Uri(imageUrl, UriKind.Relative);
LayoutRoot.Children.Add(rightForearm);
rightForearm.Source = new BitmapImage(imageUri);
rightForearm.ImageOpened += new EventHandler<RoutedEventArgs>(bodyPart_ImageOpened);
}
public void rightForearmLoaded()
{
var bi = waitTillImageLoad(rightForearm.Name);
rightForearm.Height = (elbowToHandTip - handLength) + palm;
rightForearm.Width = ratio(bi.PixelHeight, bi.PixelWidth, rightForearm.Height);
Vector2 topVector;
if (faceRight)
{
topVector.X = (float)(Convert.ToDouble(rightBicep.GetValue(Canvas.LeftProperty)));
topVector.Y = (float)(Convert.ToDouble(rightBicep.GetValue(Canvas.TopProperty)) + rightBicep.Height - palm);
}
else
{
topVector.X = (float)(Convert.ToDouble(leftBicep.GetValue(Canvas.LeftProperty)));
topVector.Y = (float)(Convert.ToDouble(leftBicep.GetValue(Canvas.TopProperty)) + leftBicep.Height - palm);
perspectiveVectorHeight(ref topVector, ref rightForearm, torso.Width);
rightForearm.Width = ratio(bi.PixelHeight, bi.PixelWidth, rightForearm.Height);
}
rightForearm.SetValue(Canvas.LeftProperty, Convert.ToDouble(topVector.X));
rightForearm.SetValue(Canvas.TopProperty, Convert.ToDouble(topVector.Y));
rightForearm.SetValue(Canvas.ZIndexProperty, rightForearmZIndex);
generateRightElbow();
}
Now all the values I am adding together are a group of doubles I preset, and the property faceRight is to dertmine if the human body is facing right or left to determine where the positions of the body parts (since if the right hand looks on the left hand side when the human body turns the other way).
If you notice the rightforearm is taking the leftproperty of the rightbicep, so technically it should display direcrly underneath which it isn't. I also debugged the user control and both have the left property of -3.
PS. I call the methods rightbicepLoaded and rightforearmLoaded when an event is called when all the imageOpened events all have been triggered.
Any ideas on why this is happening?
Found out why , in my method ratio it should take hieght and width, and I put and i put 2 hieghts instead

Resources