I managed to select every thing I want in Illustrator with a ExtendScript Toolkit javascript code: lots of things (text, path, symbols, ...) in several layers. Now I want to resize them the same way, then move them.
I know how to apply a transformation to one object by code, but I want to avoid looping on each element because it would be very very long and transformation would be applied from the anchor point of each element, so my drawings wouldn't be cohesive.
So, I am looking for a way to do like in Illustrator UI: right click > transform > scale. Is there any UI command like this I could access from code (javascript)?
There are at least three ways to do this:
Record AI Actions that perform required transformations, and then play these Actions (by DoScript) from your script
Group selected objects and apply required transformations to the group as #Dane proposed. You need to backup the Layer object property to allow objects to be restored in original layers, as shown in VBA example below:
For i = Index_Lo To Index_Hi
Call Layers_Backup.Add(Item:=SelectedItems(i).Layer, Key:=Format(i))
Call SelectedItems(i).Move(Temp_Group, AiElementPlacement.aiPlaceAtEnd)
Next i
Call Temp_Group.Resize(scaleX:=120, scaleY:=120, changeLineWidths:=120)
For i = Index_Lo To Index_Hi
Call SelectedItems(i).Move(Layers_Backup(Format(i)), AiElementPlacement.aiPlaceAtEnd)
Next i
Call Windows API functions (like PostMessage (..., WM_COMMAND, ..., ...), SendDlgItemMessage, FindWindowEx etc) to show, fill and execute required AI transformations dialog boxes
IMHO, item #1 is the easiest to implement while item#2 is the most reliable
So I dont know if you can get away with not looping in some form or other. that said, if you dont mind putting your selection in to a group, it might be faster to loop through your selection adding to a group which might be faster than looping and through the selection and scale and moving each element. With the group object you can then do groupObject.scale() and .translate().
Here is a snippet i took from a script of mine.
#target "illustrator"
var aiApp = app.activeDocument;
var aSelection = aiApp.selection;
var aGroup = app.activeDocument.groupItems.add();
for (i=0; i < aSelection.length; i++){
aSelection[i].move( aGroup, ElementPlacement.INSIDE);
aSelection[i].move( aGroup, ElementPlacement.PLACEATEND);
}
//moves up 700 points and scales by 200
aGroup.translate(0,700)
aGroup.resize(200, 200, true , true, true, true, 200)
Related
My 2D game uses a "wide" floor, seen below, which requires objects to move behind and in front of each other as they move vertically.
See me!
In JavaScript I would simply apply the "y" position to the z-index property of the object, effectively layering the elements. I've been experimenting with AS3's indexing, addChildAt and setChildIndex, but have not yet figured out a solution. Note that objects will added and modified dynamically as the game updates, and will be numerous.
What is the simplest method to reproduce this in AS3?
This seems to be best matching what you are describing:
Using Matrix3D objects for reordering display
As mentioned previously, the layering order of display objects in the display list determines the display layering order, regardless of their relative z-axes. If your animation transforms the properties of display objects into an order that differs from the display list order, the viewer might see display object layering that does not correspond to the z-axis layering. So, an object that should appear further away from the viewer might appear in front of an object that is closer to the viewer.
To ensure that the layering of 3D display objects corresponds to the relative depths of the objects, use an approach like the following:
Use the getRelativeMatrix3D() method of the Transform object to get the relative z-axes of the child 3D display objects.
Use the removeChild() method to remove the objects from the display list.
Sort the display objects based on their relative z-axis values.
Use the addChild() method to add the children back to the display list in reverse order.
This reordering ensures that your objects display in accordance with their relative z-axes.
The following code enforces the correct display of the six faces of a 3D box. It reorders the faces of the box after rotations have been applied to the it:
public var faces:Array; . . .
public function ReorderChildren()
{
for(var ind:uint = 0; ind < 6; ind++)
{
faces[ind].z = faces[ind].child.transform.getRelativeMatrix3D(root).position.z;
this.removeChild(faces[ind].child);
}
faces.sortOn("z", Array.NUMERIC | Array.DESCENDING);
for (ind = 0; ind < 6; ind++)
{
this.addChild(faces[ind].child);
}
}
It's essentially what you're doing already, using addChild. You could use setChildIndex() instead of removing the objects from the display list as well.
I am looking for a library preferably in JavaScript, that will allow a user to draw a plot (simple one consisting of vertical and horizontal steps) like this one:
The idea is that when the user is done with the plot I can generate data points from the graph and process them.
I don't know where to start, I am looking to start learning to do this within a JS based framework (meteor) but I can't find a library that allows for something like this. The closest library I found is d3.js but I couldn't find any example that allows for this.
Would anyone be able to point out to me a sample example to start from? Would you know of a better suited library to accomplish what I am asking for?
Here is a relatively simple fiddle which accomplishes some of what you asked for, excluding axis (which are relatively easy and has plenty of examples). It uses D3 for all the drawing and mouse event handling. On click it simply executes svg.append("circle").attr("r", 5), and if it's not the first click (i.e. linking points) then it also will create a path element using the previous mouse click coordinates:
svg.insert("path", "circle").attr("d", function () {
return [
"M", prevClickLoc[0], prevClickLoc[1],
"L", prevClickLoc[0], y,
"L", x, y].join(" ");
})
Where x and y are the current mouse coordinates. Also has an export button that will output a list in the form of cx,cy,cx,cy,... :: d,d,d,d,.... On import, you could easily split this array into two using indexOf("::") or whatever you choose if you want to change the formatting. Then just exectue for (x in circles) {svg.append("circle").attr("cx", function...).attr("cy", function...);} and do something similar for paths for (y in paths) {svg.append("path").attr("d", function(){return paths[y];});}. It would be even easier if on export you made the cxcy array in the format cx;cy,cx;cy since then you could simply split the array at each comma and then split each index of the resulting array at the semicolon for a nice nested array.
Small update in this version, you can only place points if the current mouse x is greater than the previous x coordinate, and it also has the line d3.event.stopPropagation(); which prevents accidental highlighting of the page.
By using the simple array app.selection[x], you can apply a transformation to any object in the selection, independently. But how do I apply a transformation to the entire selection together?
For example: inside InDesign, I can select two side-by-side objects and flip them horizontally, causing them to switch places and flip.
Inside a script, I can target each object in the selection, but they will not switch places; they will remain in the same place and flip.
for ( var x = 0; x < app.selection.length; x++ ){
app.selection[x].absoluteFlip = Flip.HORIZONTAL;
}
I could possibly group the selection, apply a transformation, then ungroup when finished, but this seems like unnecessary bulk that could slow down the code. I can easily do it manually inside InDesign, so it should follow that there's some way to access app.selection as a single object instead of an array. Does such an object exist?
Not really a solution, but it's worth noting that I don't think absoluteFlip is the action being performed, but a state indicating if the item has ben flipped. It's writable so you can set the state, but I think what's happening when using the menu to flip is flipItem: http://jongware.mit.edu/idcs6js/pc_PageItem.html#flipItem,
in which you can set "around" coordinates.
Now getting the origin of the selection box isn't straightforward for some reason (or maybe it is but I don't know how), but you can either use first object coordinates to set the origin so you can flip it around different coordinates depending on order of selection. Or you can sort the array to find left most coordinates (or whichever is needed), like this:
var selection_array = app.selection;
selection_array.sort(function(a, b){return a.visibleBounds[1]-b.visibleBounds[1]})
var flip_origin = [selection_array[0].visibleBounds[1],selection_array[0].visibleBounds[0]]
for(i=0;i<app.selection.length;i++){
app.selection[i].flipItem(Flip.HORIZONTAL, flip_origin);
}
Not sure it's easier or faster than grouping and ungrouping though.
Consider resize. It has a "individual/global" parameter :
void resize (in: varies, from: varies, by: ResizeMethods, values: Array of varies[, resizeIndividually: bool=true][, consideringRulerUnits: bool=false])
Resize the page item.
I am creating a polygon with multiple event handlers -- I can click-drag to draw freehand, hold shift and click to snap to roads, and do a normal click as well. Originally, I had one array that I pushed each of these points to. However, since the event handlers are unique, debugging an array that I pass everywhere and modify everywhere seems like bad form.
I've made it so that the polygon paths is an array of arrays of points. When I shift-click, it makes an array of points, when I click-drag, it makes an array of points, and then I push these to the array of arrays.
However, how do I connect these together? I am currently not able to connect the endpoints of one array to the start of the next. Because I'm trying to modularize my code, I don't want to have to pass the last point of an array to the event listener that makes the next array, because that defeats the purpose of having separate arrays.
The documentation is pretty thin on multiple paths for a polygon.
Here's what I have so far.
http://jsfiddle.net/skitterm/fn4g5/1/
Pseudocode:
array C = new Array();
addEventListener for click {
populate array A
C.push(A);
}
addEventListener for shift-click {
populate array B
C.push(B);
}
addEventListener for right-click {
create polygon {
paths: C
}
}
I can create a polygon by just shift-clicking, or by clicking, or clicking and dragging, but I can't make a polygon where one side is done by shift-clicking and the rest by normal clicks.
Any ideas?
i wonder how this is made: http://workshop.chromeexperiments.com/examples/gui/#1--Basic-Usage
Those spots form a text and when clicked , they explode.
How can shapes form a text? Can i achieve this with processing.js?
Thanks.
I guess one way is to have a model for each character. For example, manually create the letter 'A' via shapes. Then for each letter in the message, you would display the character model for the letters
I'm not sure if you want to write this in processing or in javascript. This is a javascript library, so you can use it in a javascript context. To make it on processing and use processingjs to display it on web you got to use processing resources to achieve the same result or manage to pass data from/to javascript and processing. There is this example from processingjs site on connecting your page with a processing sketch. Note that processingjs does not support use of processing libraries, so you can't use any of Precessing typography libraries in this workflow. I think that they could be very handy though...
As how doing this on processing, i think i would go for drawing letters in a not displayed PGraphics, and using a pixel color test on this surface to drive the drawings of the circles. This would allow to alter the text in run time. Like, in pseudo code
PGraphics matteImage = new PGraphics(size, size, render);//
matteImage.background(black);
matteImage.fill(255);
matteImage.text("A", x,y);
matteImage.loadPixels();
for(i; i < matteImage.length;i++)
{
color c = matteImage.pixels[i];
if ( c == white)
{
doDrawEllipses();
}
}
In drawing circles method/class i would add some noise/randomness, exploding handle...