How can I group and animate individual rows on this raphael canvas? - javascript

The following code creates a 10 x 15 grid of 40 pixel squares. I would like to be able to group the squares into rows for animating later. Specifically, I want to animate the rows dropping to the bottom and then apply animated matrix transforms to them. Eventually they will just replicate the same grid at a new location.
<script type="text/javascript">
var squares = [];
var paperHeight = 600;
var paperWidth = 400;
var squareSize = 40;
var cols = paperWidth / squareSize;
var rows = paperHeight / squareSize;
var paper = Raphael($("#grid-test")[0],paperWidth,paperHeight);
for (var i = 0; i < cols; i++) {
// Begin loop for rows
for (var j = 0; j < rows; j++) {
// Scaling up to draw a rectangle at (x,y)
var x = i * squareSize;
var y = j * squareSize;
// For every column and row, a square is drawn at an (x,y) location
paper.rect(x,y,squareSize,squareSize);
}
}
</script>

You can keep track of the elements with a 2 dimensional array [row][column]. Then iterate over each column in a row to get the appropriate elements; then just animate them. Note that you need to swap i/j when bullding; currently you're building [column][row].
http://jsfiddle.net/7ncHM/
var rowsArr = [];
for (var i = 0; i < cols; i++) {
// Begin loop for rows
rowsArr[i] = [];
for (var j = 0; j < rows; j++) {
// Scaling up to draw a rectangle at (x,y)
var x = j * squareSize; // swap i/j
var y = i * squareSize;
// For every column and row, a square is drawn at an (x,y) location
rowsArr[i][j] = paper.rect(x,y,squareSize,squareSize);
// draw and store directly, isn't that elegant? :)
}
}
function animateRow(i) {
var row = rowsArr[i];
for(var j = 0; j < row.length; j++) { // iterate elements in row
row[j].animate({y: 400}, 2000);
}
}
animateRow(2);

You'll have difficulty animating individual rows if you don't store a reference to the squares which make up a row (presumably this is what the unused squares[] array is for...)
Each time you call paper.rect assign the result to an element in squares[]
You could then have an function which takes a given row of your array and applies Raphael's .animate to each square - giving the impression of the whole row being animated.
http://raphaeljs.com/reference.html#Element.animate
In response to your comment - you can add elements to a Raphael set and then animate the set -eg...
var squares = [];
var paperHeight = 600;
var paperWidth = 400;
var squareSize = 40;
var cols = paperWidth / squareSize;
var rows = paperHeight / squareSize;
var paper = Raphael($("#grid-test")[0],paperWidth,paperHeight);
var rowSets = [];
// *** GRID CONSTRUCTION CODE STARTS ***///
// I have swapped the usage of i and j within the loop so that cells
// in a row are drawn consecutively.
// Previously, it was column cells which were drawn
// consecutively
for (var i = 0; i < rows; i++) {
//create a new set which will hold this row
rowSets[i] = paper.set();
squares[i] = [];
// Begin loop for cells in row
for (var j = 0; j < cols; j++) {
// Scaling up to draw a rectangle at (x,y)
var x = j * squareSize;
var y = i * squareSize;
// For every column and row, a square is drawn at an (x,y) location
var newRect = paper.rect(x,y,squareSize,squareSize);
// add the new cell to the row
rowSets[i].push(newRect);
squares[i][j] = newRect;
}
}
// *** GRID CONSTRUCTION CODE ENDS ***
squares[5][5].attr({fill : '#f00'}); // colour an individual cell
rowSets[6].attr({fill : '#00f'}); // colour an entire row
rowSets[6].animate({y : 10 * squareSize},400); // animate an enitre row
This means you could build a set of rows (and of columns) and animate them as individual units.
UPDATE
As a Raphael set is just a way of associating elements together to provide an easy way to apply operations en masse. It does not work in the same way as "Group" in Visio for example - ie They are still individual elements and will be animated independently.
Raphael will not find the centre of the set to do a rotation around. Each element of the set will be rotated around its own centre.
This problem can be solved by using a path to define an entire row at once as an SVG path. However this means that rows are now "one entity" and you will have difficulty addressing one cell, or one column. However you could switch the indiviual cells for a "row path" before performing the transform. The user/player hopefully wouldn't notice.
You can replace the // *** GRID CONSTRUCTION *** code with the following code to build rows as paths... (JSFiddle Demo)
//Build a row path - this path will be used as the basis of all the rows
var sPath = "lpw 0 l0 sq l-pw 0 l0 -sq " //outline of the whole row
for (var cell = 1; cell < cols; cell++){
sPath += "msq 0 l0 sq Z "; //insert an "upright" cell separator
};
sPath = sPath.replace(/sq/g,squareSize).replace(/pw/g,paperWidth);
//we have no 'for' loop for columns now as we are creating entire row .paths
for (var j = 0; j < rows; j++) {
//we apply the 'M' (Move) offset here to the beginning of the path
//This is simply to adjust the Y-offset of the start of the path
//ie to put the the path in a new row position
rowSets[j] = paper.path("M0 " + j * squareSize + sPath);
}

Related

InDesign - Add column numbers and row letters text under an set of images

I'm stuck with a script and can't find a solution online, hope someone can help me.
In the place where I work, we print on the tiles. One of the main jobs consist in splitting a large image into several squares and print them on tiles like this:
Then the tiler will install the tiles by joining them to form the image. To do this he must have some indications on how to place the tiles.
We usually use a grid like the one in the battleship game, with letters on one side and numbers on the other like the red ones you see in the image (e.g. A1, A2...).
They would not be inside the image, it's just for reference.
I have put together a script to place the square images one per page of an InDesign document, now what I am looking for is insert some text under the images where it marks which tile it is (e.g. A1, C5 ...).
There is a way to manually choose a custom number for the number of horizontal tiles (e.g. 4) so that the script inserts the text A1 to A4, and then starts inserting the letter B all the way to B4, then C, D, etc. etc. until the tile images runs out?
Here is my script so far:
var Pagewidth =app.activeDocument.documentPreferences.pageWidth;
var Pageheight = app.activeDocument.documentPreferences.pageHeight;
var Imagefolder = Folder.selectDialog("Select a folder");
var Images = Imagefolder.getFiles(/.+\.(?:gif|jpe?g|eps|tiff?|psd|pdf|bmp|png)$/i);
for(var i =0; i < Images.length; i++) {
var Placed = app.activeDocument.pages.item(-1).place(Images[i]);
app.activeDocument.align(Placed[0], AlignOptions.VERTICAL_CENTERS, AlignDistributeBounds.PAGE_BOUNDS);
app.activeDocument.align(Placed[0], AlignOptions.HORIZONTAL_CENTERS, AlignDistributeBounds.PAGE_BOUNDS);
Placed[0].parent.fit(FitOptions.FRAME_TO_CONTENT);
app.activeDocument.pages.add(LocationOptions.AT_END);
}
app.activeDocument.pages.item(-1).remove();
EDIT
Just in case I'll try to be more clear, what I'm looking for is adding some text under the images on every page, like this:
I already know how to add the textframe for every image and place it under them.
What don't understand is how to add this kind of textframe content.
UPDATE
Thanks to the code provided by Yuri, I adapted it for my purposes.
Here is the final code:
var Pagewidth =app.activeDocument.documentPreferences.pageWidth;
var Pageheight = app.activeDocument.documentPreferences.pageHeight;
var Columns = prompt("Type the number of columns","");
if(!Columns){
exit();
}
if(!Number(Columns)){
alert("You can only type numbers");
exit();
}
var Imagefolder = Folder.selectDialog("Select a folder");
if(!Imagefolder){
exit();
}
var Images = Imagefolder.getFiles(/.+\.(?:gif|jpe?g|eps|tiff?|psd|pdf|bmp|png)$/i);
for(var i =0; i < Images.length; i++){
var Placed = app.activeDocument.pages.item(-1).place(Images[i]);
app.activeDocument.align(Placed[0], AlignOptions.VERTICAL_CENTERS, AlignDistributeBounds.PAGE_BOUNDS);
app.activeDocument.align(Placed[0], AlignOptions.HORIZONTAL_CENTERS, AlignDistributeBounds.PAGE_BOUNDS);
Placed[0].parent.fit(FitOptions.FRAME_TO_CONTENT);
app.activeDocument.pages.add(LocationOptions.AT_END);
}
app.activeDocument.pages.item(-1).remove();
var Lettersnumber = 26;
var Arr = [];
var Letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
for(var j = 0; j < Lettersnumber; j++){
for(var k = 1; k <= Columns; k++){
Arr.push(Letters[j] + k);
}
}
for(var l = 0; l < app.activeDocument.allGraphics.length; l++){
if(app.activeDocument.allGraphics[l].parentPage != null){
try{
var Subtext = app.activeDocument.allGraphics[l].parentPage.textFrames.add();
Subtext.textFramePreferences.verticalJustification = VerticalJustification.TOP_ALIGN;
Subtext.contents = Arr[l];
Subtext.paragraphs[0].appliedFont = app.fonts.item("Arial");
Subtext.paragraphs[0].pointSize = "30";
Subtext.paragraphs[0].justification = Justification.CENTER_ALIGN;
var Margin = app.activeDocument.allGraphics[l].parent.visibleBounds;
Subtext.visibleBounds = [Margin[2]+12, Margin[1], Margin[2]+4, Margin[3]];
Subtext.fit(FitOptions.FRAME_TO_CONTENT);
Subtext.fit(FitOptions.FRAME_TO_CONTENT);
Subtext.move(undefined, ["0", "12"]);
}
catch(e){
alert("The text is not enough for the number of images in the document");
break;
}
}
}
Here is the code that asks number columns and rows, makes the captions 'A1', 'A2', 'A3', 'B1', 'B2', 'B3'... etc, and put the texts on pages:
var doc = app.activeDocument;
// ask the number of columns and rows
var cols_rows = prompt('Columns Rows', '4 3');
if (cols_rows == null) exit();
cols_rows = cols_rows.match(/\d+/g);
var cols = cols_rows[0];
var rows = cols_rows[1];
// make the captions A1, A2, A3, B1, B2, B3... etc
var captions = [];
var abc = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
for (var r=0; r<rows; r++) for (var c=1; c<=cols; c++) captions.push(abc[r] + c);
// add pages if it's need
while(doc.pages.length < captions.length) doc.pages.add();
// set text frames with the captions on the pages
for (var i=0; i<captions.length; i++) {
var page = doc.pages[i];
var caption = captions[i];
var frame = page.textFrames.add();
frame.geometricBounds = [0,0,20,20];
frame.contents = caption;
frame.paragraphs[0].justification = Justification.CENTER_ALIGN;
frame.paragraphs[0].pointSize = 30;
frame.move([0,200]);
doc.align(frame, AlignOptions.HORIZONTAL_CENTERS, AlignDistributeBounds.PAGE_BOUNDS);
}
Input for the grid with 4 columns and 3 rows (A, B, C):
Results:
...
But if you have more than 26 rows it's need to decide how to mark the rows after 'Z'.
Update
Here is the full implementation that takes selected picture, ask the number of columns, rows, and the gap, sliced the picture and put every slice on the page with the caption:
var doc = app.activeDocument;
if (app.selection.length == 0) { alert('Nothing selected'); exit() }
var sel = app.selection[0];
// ask the number of columns and rows
var input = prompt('Columns, Rows, Gap', '4 3 2');
if (input == null) exit();
input = input.match(/\d+/g);
var cols = +input[0];
var rows = +input[1];
var gap = +input[2];
// calculate tile width and height
var sel_gb = sel.geometricBounds;
var sel_w = sel_gb[3] - sel_gb[1];
var sel_h = sel_gb[2] - sel_gb[0];
var w = (sel_w - (cols-1) * gap) / cols; // tile width
var h = (sel_h - (rows-1) * gap) / rows; // tile height
// make the tiles
var tiles = [];
var gb = sel_gb.slice(0);
gb[2] = gb[0] + h;
gb[3] = gb[1] + w;
for (var r=0; r<rows; r++) {
for (var c=0; c<cols; c++) {
var tile = sel.duplicate();
tiles.push(tile);
tile.geometricBounds = gb;
gb[1] = gb[3] + +gap;
gb[3] = gb[1] + w;
}
gb[1] = sel_gb[1];
gb[3] = gb[1] + w;
gb[0] = gb[2] + gap;
gb[2] = gb[0] + h;
}
sel.remove();
// make the captions A1, A2, A3, B1, B2, B3... etc
var captions = [];
var abc = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
for (var r=0; r<rows; r++) for (var c=1; c<=cols; c++) captions.push(abc[r] + c);
// add pages if it's need
while(doc.pages.length <= captions.length) doc.pages.add();
// set text frames with the captions on the pages
for (var i=0; i<captions.length; i++) {
var page = doc.pages[i+1];
// get a tile and copy the tile on the page
var tile = tiles[i].duplicate(page);
doc.align(tile, AlignOptions.HORIZONTAL_CENTERS, AlignDistributeBounds.PAGE_BOUNDS);
doc.align(tile, AlignOptions.VERTICAL_CENTERS, AlignDistributeBounds.PAGE_BOUNDS);
// get a caption and put the caption on the page
var caption = captions[i];
var frame = page.textFrames.add();
frame.geometricBounds = [0,0,20,20];
frame.contents = caption;
frame.paragraphs[0].justification = Justification.CENTER_ALIGN;
frame.paragraphs[0].pointSize = 30;
frame.move([0, tile.geometricBounds[2] + 20]);
doc.align(frame, AlignOptions.HORIZONTAL_CENTERS, AlignDistributeBounds.PAGE_BOUNDS);
}
Input:
Output:
The row 'A B C D ...' and the column '1 2 3 4 ...' on the first page can be added with a script as well. Let me know if you need it.

How do I create bounds of a 2darray tilemap in Javascript?

I am creating a 2D ASCII game. Currently I have the world (leveldata) stored in a 2D array. Example:
"test2": [
["╔","═","═","═","═","═","═","═","═","═","╗","´","´","´","´","`","´","´","´","´","´"],
["║","▓","▓","▓","▓","▓","▓","▓","▓","▓","║","´","`","`","´","`","´","´","`","´","´"],
["║","▓","▓","▓","▓","▓","▓","▓","▓","▓","║","´","´","´","´","`","´","´","´","´","´"],
["║","▓","▓","▓","▓","▓","▓","▓","▓","▓","║","´","`","´","´","´","´","´","`","´","´"],
["╚","═","═","▓","▓","▓","▓","═","═","═","╝","´","´","´","´","´","`","´","´","´","´"],
["´","´","´","´","´","`","´","´","´","´","`","´","´","`","´","´","`","´","´","´","´"],
["´","´","`","´","´","´","´","´","´","╔","╗","´","´","´","´","´","´","´","`","´","´"],
["´","´","´","´","´","´","´","´","`","║","║","´","`","´","´","`","´","´","`","`","´"],
["´","´","´","´","´","´","´","`","´","║","║","´","´","´","´","`","´","`","`","´","´"],
["´","´","`","´","´","´","´","´","´","║","║","´","´","`","´","´","`","`","´","´","´"],
["´","´","´","´","´","´","`","´","`","║","║","`","´","´","´","´","`","´","´","`","´"]
]
I take this data, replace the tile where the player is and then paste it to the screen, like so:
I want to take this array that I paste to the screen, and crop it around the player with variable
bounds. Essentially remove any tiles that are a certain distance from a box around the player, so that the camera "follows" the player.
The problem is, tiles aren't removing the way I want them to and I am stuck on what I am supposed to do. Here is all I have so far, it takes in map data, bounds, and player position, and dumps out a custom array. I am trying just the upper bounds so far...
// Cropping the game screen using a camera.
function crop(width=5, height=5, x=1, y=1, map=[]) {
let topBound = y-height;
let bottomBound = y + height;
let rowsRemoved = [];
// Loop through and remove rows that are above or below the max.
for (let row = 0; row < map.length; row++) {
if (row < topBound) {
rowsRemoved.push(row);
}
}
for (let i = 0; i < rowsRemoved.length; i++) {
console.log(rowsRemoved[i]);
map.splice(rowsRemoved[i], 1);
}
console.log(rowsRemoved)
// Loop through and remove columns that are above or below the max.
// for (let row = 0; row < map.length; row++) {
// for (let column = 0; column < map[row].length; column++) {
// if (column < x-width || column > x+width) {
// map[row].splice(map[row].indexOf(map[row][column]), 1);
// }
// }
// }
console.log("----")
return map;
}
It turned out it was to slice the array instead of splice it, and take a totally different approach.
// Cropping the game screen using a camera.
function crop(width=5, height=5, x=1, y=1, map=[]) {
// Get the bounds and make sure they stay within the map's bounds.
let topBound = y - height;
if (topBound < 0) {
topBound = 0;
}
let bottomBound = y + height + 1;
let leftBound = x - width;
if (leftBound < 0) {
leftBound = 0;
}
let rightBound = x + width + 1;
let rowsRemoved = [];
// Remove rows that are above or below the max.
map = map.slice(topBound, bottomBound);
console.log(map)
// Remove columns that are above or below the max.
for (let row = 0; row < map.length; row++) {
map[row] = map[row].slice(leftBound, rightBound);
}
console.log("----");
return map;
}

setAttribute() in a multi dimensional array

What I'm trying to figure out is how to give another class to certain div's in a two-dimensional array that has a value of "3" . This is what I tried at first.
numRows are the amount rows in the array.
numSeatsPerRow are the number of array items in every "numRows".
r and t are the positions in the two-dimensional array.
for(var r = 0; r<numRows; r++){
for(var t = 0; t<numSeatsPerRow; t++){
if(myArray[r][t] === 3){
document.getElementsByTagName("div")[r][t].setAttribute("class", "busySpot");
}
}
}
I soon understood that this wouldn't work. I tried to make a formula which could calculate every position as an ordinary array but couldn't make it work. Is there any other way to solve my problem?
In the code below I create a two-dimensional array based on two different inputs
var myArray = new Array(numRows);
for(var i = 0; i <numRows; i++){
myArray[i] = new Array(numSeatsPerRow);
}
In the next piece of code I give all array items the value 1 and then draw a green box for every item.
for(var h = 0; h<numRows; h++){
document.body.appendChild(//efter varje rad sker ett <br> i utskriften
document.createElement("br"));
document.body.appendChild(
document.createElement("br"));
for(var g = 0; g<numSeatsPerRow; g++){
myArray[h][g] = 1
var vSpot = document.createElement("div");
vSpot.className = "vacantSpot";
document.body.appendChild(vSpot);
}
}
What I'm trying to do in the top code is give the random boxes that I talked about another class.
this is how i came up with the random coordinates:
for(var v = 0; v < 15; v++){
var test = true;
while(test){
var x = Math.floor((Math.random() * numSeatsPerRow) + 0 );
var y = Math.floor((Math.random() * numRows) + 0);
if(myArray[x][y] === 1) {
myArray[x][y] = 3;
test = false;
}
}
}
Thanks!

Can someone explain me this function for creating grid with JS/jQuery?

I am learning jquery/js and I want to create grid made of divs. This is script doing just that, but I cant fully understand it..
function displayGrid (n) {
var size = 960;
var boxSize = (960 - 4*n)/n;
var wrap = $(".wrap").html("");
for (var j = 0; j < n; j++) {
for (var i = 0; i < n; i++) {
wrap.append( $("<div></div>").addClass("square").height(boxSize).width(boxSize) );
}
wrap.append($("<div></div>").css("clear", "both"));
}
}
In html I have empty wrap class.
Also, there is this function which I understand :)
function setGrid() {
var col = prompt("Enter number of columns: ");
displayGrid(col);
clean();
}
Thanks
Here is a basic line by line explanation..
function displayGrid (n) {
var size = 960; //MAX SIZE TO BE COVERED BY ALL DIVS ,EVEN IF THERE ARE N NO OF DIV'S
var boxSize = (960 - 4*n)/n; //FORMULA TO DECIDE WIDTH OF EACH DIV,CAN BE DIFFERENT TOO.
var wrap = $(".wrap").html(""); //THIS IS A DIV PROBABLY WHERE YOU ARE ADDING THE NEW SMALLER DIVS
for (var j = 0; j < n; j++) {
for (var i = 0; i < n; i++) {
//TWO FOR LOOPS ,1 IS FOR LOOP THROUGH ROWS , OTHER FOR COLUMNS.
wrap.append( $("<div></div>").addClass("square").height(boxSize).width(boxSize) );
//THIS APPENDS A BLANK DIV TO DIV WITH CLASS .WRAP, AND ADDS A CLASS SQAURE, AND ALSO WITH WIDTH AND HEIGHT PROPERTY SETS EACH DIVS PROPERTY OF WIDTH AND HEIGHT.
}
wrap.append($("<div></div>").css("clear", "both"));
//THIS SHOULD BE TO MOVE TO NEXT COLUMN.
} }
Here is commented code:
//n-> seems to be the number of times to divide the grid
//1-> will get 1 square
//2-> will get 4 square
//3-> will get 9 square and so on... n^2
function displayGrid (n) {
var size = 960;
//This seems to calculate size of the squares to fit inside the give area
// of 960: 960/4
//-4*n I guess is the border size to remove from each square in order to have an exact match
var boxSize = (960 - 4*n)/n;
//this get the grit container, empties it
var wrap = $(".wrap").html("");
//now print each square on the document
for (var j = 0; j < n; j++) { //columns
for (var i = 0; i < n; i++) { //rows
wrap.append( $("<div></div>").addClass("square").height(boxSize).width(boxSize) );
}
//this is done to go in the next row, since we are using divs...
wrap.append($("<div></div>").css("clear", "both"));
}
}

algorithm for generating grid of points

I've got array of coordinates in following schema (x,y):
array = [[1,2],[1,5],[1,1],[2,2],[2,5],[2,1]]
I would like to do some process to achieve effect:
array1 = [[[1,2],[1,5],[1,1]],[[2,2],[2,5],[2,1]]]; array2 = [[[2,2],[2,5],[2,1]]]
and
array1a = [[[1,2],[2,2]]]; array2a = [[[1,5],[2,5]]]; array3a=[[[1,1],[2,1]]]
in other words I would like to get coordinates of all parallel and perpendicular lines.
I've came up with two double loops (one by x, and another by y) but maybe there is another faster(better) way
--
pseudo code:
for (var i = 0; i < length; i++) {
for (var j = 0; j < length2; j++) {
var x = points[i][j][0];
var y = points[i][j][1];
};
};
for (var i = 0; i < length2; i++) {
for (var j = 0; j < length; j++) {
var x = points[i][j][0] ;
var y = points[i][j][1] ;
};
};
EDIT
OK, here's the situation:
I've got this kind of rectangle, I've got coordinates of points (red mark) as array:
array = [[1,2],[1,5],[1,1],[2,2],[2,5],[2,1]]
and I want to make antoher array which will be like this:
array1 = [[[1,2],[1,5],[1,1]],[[2,2],[2,5],[2,1]]]
in above array there are coordinates of points thats one of green line containts. Those lines are parallel or perpendicular lines to sides of rectangle.
If you sort the points by X coordinate and then group them together by X coordinate, each group is a set of points on a vertical line.
Similarly, if you sort the points by Y coordinate and then group them together by Y coordinate, each group is a set of points on a horizontal line.
If this sounds simplistic, I apologize. But your initial description and the added diagram with your further explanation leads me to believe that it's what you're asking for.

Categories

Resources