Find the position of an element in matrix - javascript

You have a matrix N*N. You move from trough the matrix by spiral. Now that you know how many steps you have taken, you want to know your position in the matrix.
Input/Output
[input] integer size
The size of the room.
[input] integer steps
The number of steps you have taken(1-based).
[output] an integer array
Array of length two, describing your position in the room(0-based).
Example
For size = 5 and steps = 20, the output should be [2, 3].
Your path can be shown as the following figure:
[ 1, 2, 3, 4, 5],
[16, 17, 18, 19, 6],
[15, x, x, 20, 7],
[14, x, x, x, 8],
[13, 12, 11, 10, 9]
The 20th step brought you to the second line and third column (0-based), so the answer is [2, 3].
I build a solution that can build such matrix for me
const initMatrix = size => {
const matrix = [];
for (var i = 0; i < size; i++) {
matrix[i] = new Array(size);
}
return matrix;
};
const blindfolded = (size, steps) => {
const matrix = initMatrix(size);
let nc = size;
let num = 1;
for (let z = 0; z < nc; z++) {
for (let i = z; i < nc; i++) {
matrix[z][i] = num;
num++;
}
for (let i = z + 1; i < nc; i++) {
matrix[i][nc - 1] = num;
num++;
}
for (let i = nc - 2; i >= z; i--) {
matrix[nc - 1][i] = num;
num++;
}
for (let i = nc - 2; i >= z + 1; i--) {
matrix[i][z] = num;
num++;
}
nc--;
}
for (let i = 0; i < matrix.length; i++) {
for (let j = 0; j < matrix[i].length; j++) {
console.log(matrix[i][j]);
}
}
};
blindfolded(7, 1);
But probably there should be another more optimal algorithm

This can be done in constant time. The spiral looks like this:
To find number of coils for a given steps, one need to
ceil(sqrt(steps)/2 - 0.5) - 1 (1)
Side length on i-th level is 2*i+1, we start count from zero.
Suppose steps = 64 and center is (5,5). Applying (1) one can find that number of full levels is three, so we are at (8,8+1) = (8,9) (bold black dot), and we did 49 steps for it. Then, side length on 4-th level is 2*4+1=9, so subtract 7 vertically, subtract 8, and we don't have any more steps to use, so finally we are at (1,1).
Somehow this is not surprising, because we went through square 8x8.
function calc() {
var steps = window.steps.value;
if (steps <= 0) return "must be positive";
var ci = 5, cj = 5;
if (steps == 1) return ci + ", " + cj;
var level = Math.ceil(Math.sqrt(steps)/2 - 0.5) - 1;
var ri = ci + level;
var rj = cj + level + 1;
var sideFull = 2 * level + 1;
steps -= sideFull * sideFull;
var side = sideFull + 2;
{
var verUp = Math.min(steps, side - 2);
steps -= verUp;
ri -= verUp;
}
{
var horLeft = Math.min(steps, side - 1);
steps -= horLeft;
rj -= horLeft;
}
{
var verDown = Math.min(steps, side - 1);
steps += verDown;
ri -= verDown;
}
{
var horRight = Math.min(steps, side - 1);
rj + horRight;
}
return ri + ", " + rj;
}
Center at (5,5), and steps = <input id="steps" />. <button onclick="answer.value = calc();" >Calc</button><br>
Answer: <input id="answer" disabled="disabled" />

Related

How to distort an image by noise in p5.js

I'm trying to produce a function that starts with source image, generates noise, and then uses the noise to distort the image.
I start with creating the noise, and turning it into a vector field, Then I remap the coordinates, and pull the pixels out of the image at the correct coordinates.
Finally I re-combine the extracted pixels into an image.
So far my code is as follows:
function distort(sourceImage){
let vectorField = [];
var amount = 100;
var scale = 0.01;
for (x = 0; x < sourceImage.width; x++){
let row = [];
for (y = 0; y < sourceImage.height; y++){
let vector = createVector(amount*(noise(scale*x,scale*y)-0.5), 4*amount*(noise(100+scale*x,scale*y)-0.5))
row.push(vector);
}
vectorField.push(row);
}
var result = [];
sourceImage.loadPixels();
for (i = 0; i < sourceImage.width; i++){ //sourceImage.width
for (j = 0; j < sourceImage.height; j += 4){ //sourceImage.height
var res = vectorField[i][j];
//console.log(res);
var ii = constrain(floor(i + res.x), 0, sourceImage.width - 1);
var jj = constrain(floor(j + res.y), 0, sourceImage.height - 1);
//console.log(ii, jj);
result[i * sourceImage.width + j] = color(sourceImage.pixels[ii * sourceImage.width + jj], sourceImage.pixels[ii * sourceImage.width + jj + 1], sourceImage.pixels[ii * sourceImage.width + jj + 2], sourceImage.pixels[ii * sourceImage.width + jj + 3]);
}
}
//console.log(result)
//console.log(sourceImage.pixels[0 + sourceImage.width * 0])
for (n=0; n<sourceImage.width; n++) {
for(m=0; m<sourceImage.height; m++){
index = (n * sourceImage.width + m) * 4;
if (index >= 4194300){
index = 4194300;
}
sourceImage.pixels[index] = red(result[index]);
sourceImage.pixels[index + 1] = green(result[index]);
sourceImage.pixels[index + 2] = blue(result[index]);
sourceImage.pixels[index + 3] = alpha(result[index]);
}
}
sourceImage.updatePixels();
image(sourceImage, 0, 0, size, size);
}
Except that as a result, I'm getting 4 panels of noise across the top 4th of the canvas. The noise notably includes a lot of pixels that I know weren't in the source image, too (namely blue pixels; the image I'm trying to distort is red and white). The noise is sort of identifiable as having started as the source image, but distorted and with the aforementioned artefacts.
For comparison:
You do not process the vector field completely, you have to read each vector from the field. Actually you read just each 4th element of the vector
for (j = 0; j < sourceImage.height; j += 4)
for (j = 0; j < sourceImage.height; j++)
Further the computation of the source index is wrong. Note the control variable for the row (jj) has to be multiplied by the height. The index of the pixel in the array has to be multiplied by 4, because each pixel consists of 4 color channels:
ii * sourceImage.width + jj
(jj * sourceImage.width + ii) * 4
The computation of the target index is wrong, too:
index = (n * sourceImage.width + m) * 4;
index = (m * sourceImage.width + n) * 4;
Note, result contains 1 element for each pixel, byut sourceImage.pixels contains 4 elements for each pixel. Thus the index which reads from result and the index which access the target are different:
let result_i = m * sourceImage.width + n;
let target_i = result_i * 4;
For instance:
let result = [];
for (let j = 0; j < sourceImage.height; j++) {
for (let i = 0; i < sourceImage.width; i++) {
let res = vectorField[i][j];
let ii = constrain(floor(i + res.x), 0, sourceImage.width - 1);
let jj = constrain(floor(j + res.y), 0, sourceImage.height - 1);
let source_i = (jj * sourceImage.width + ii) * 4;
let col = color(
sourceImage.pixels[source_i],
sourceImage.pixels[source_i + 1],
sourceImage.pixels[source_i + 2],
sourceImage.pixels[source_i + 3]);
result.push(col);
}
}
for(let m = 0; m < sourceImage.height; m++) {
for (let n = 0; n < sourceImage.width; n++) {
let result_i = m * sourceImage.width + n;
let target_i = result_i * 4;
let col = result[result_i];
sourceImage.pixels[target_i] = red(col);
sourceImage.pixels[target_i + 1] = green(col);
sourceImage.pixels[target_i + 2] = blue(col);
sourceImage.pixels[target_i + 3] = alpha(col);
}
}

Where is the error in InitializeGrid? It's returning wrong indices

Ok, so I'm trying to code a Rectangle with multiple triangle strips joined together. according to:
http://www.corehtml5.com/trianglestripfundamentals.php
You need to take care of the triangles wrapping around when you have more than one row. However using the suggested algorithm in my code example I'm getting incorrect indice results.
Here is my example, with outputs.
I've tried copy/pasting the suggested algorithm but it doesn't seem to be returning correct results.
// Create the Index Points for the buffer array.
var rows=2;
var cols=3;
var grid = rows*cols;
var offset;
var pos = [];
var index = 0;
var mpOffset = 1;
for (var row = 0; row <= rows; ++row)
{
offsetY = row * (mpOffset / rows);
for (var col = 0; col <= cols; ++col)
{
offsetX = col * (mpOffset / cols);
pos[index+0] = (offsetX);
pos[index+1] = (offsetY);
index+=2;
}
}
log.info("pos="+JSON.stringify(pos)); // <-- Correct working good.
log.info("pos.length="+pos.length);
function initializeGrid(cols,rows)
{
var trianglestrip = [];
var RCvertices=2*cols*(rows-1);
var TSvertices=2*cols*(rows-1)+2*(rows-2);
var numVertices=TSvertices;
var j=0;
for(var i = 1; i <= RCvertices; i += 2)
{
trianglestrip[ j ] = (1 +i)/2;
trianglestrip[ j +1 ] = (cols*2 + i + 1) / 2;
if( trianglestrip[ j +1 ] % cols == 0)
{
if( trianglestrip[ j +1 ] != cols && trianglestrip[ j +1 ] != cols*rows )
{
trianglestrip[ j +2 ] = trianglestrip[ j +1 ];
trianglestrip[ j +3 ] = (1 + i + 2) / 2;
j += 2;
}
}
j += 2;
}
return trianglestrip;
}
var triStrip = initializeGrid(cols,rows);
log.info("triStrip="+JSON.stringify(triStrip)); // <-- Bad Not working.
log.info("triStrip.length="+triStrip.length);
// Generating the actual Point strip.
var actualStrip = [];
for (var i = 0 ; i < triStrip.length; ++i)
{
actualStrip.push(pos[(triStrip[i]-1)*2+0]);
actualStrip.push(pos[(triStrip[i]-1)*2+1]);
}
log.info("actualStrip="+JSON.stringify(actualStrip));
log.info("actualStrip.length="+actualStrip.length);
Indices should be:
1, 5, 2, 6, 3, 7, 4, 8, 8, 5, 5, 9, 6, 10, 7, 11, 8, 12
I ended up re-creating the function to calculate the triangle strip indices. Have not fully tested it but it can re-create the 3x2 grid in the example from the website.
Here is the code:
// This calculates the triangle points in a rectangular triangle strip.
// Used for 3D Webgl texture mapping.
// Copyright Joshua Langley 2019.
var rows=2;
var cols=3;
var grid = rows*cols;
var offset;
var pos = [];
var index = 0;
var mpOffset = 1;
var offsetX, offsetY;
for (var row = 0; row <= rows; ++row)
{
offsetY = row * (mpOffset / rows);
for (var col = 0; col <= cols; ++col)
{
offsetX = col * (mpOffset / cols);
pos[index+0] = (offsetX);
pos[index+1] = (offsetY);
index+=2;
}
}
log.info("pos="+JSON.stringify(pos));
log.info("pos.length="+pos.length);
var rows=rows+1,cols=cols+1; // Important this counting Points not Squares.
var grid = rows*cols;
var offset;
var indices = [];
var indice = 0;
var offset;
var doublePoints = false;
var tPoint, bPoint;
for (var row = 0; row < rows; ++row)
{
for (var col = 0; col < (cols-1); ++col)
{
offset = row * rows + col;
tPoint = offset+1;
bPoint = offset+cols+1;
if (bPoint > grid)
continue;
indices.push(tPoint);
indices.push(bPoint);
if (offset > 0 && (bPoint+1) < grid && (offset+1) % cols == 0)
{
indices.push(bPoint);
indices.push(tPoint+1);
}
}
}
log.info("indices="+JSON.stringify(indices)); // Expected Result
log.info("indices.length="+indices.length);
var actualStrip = [];
for (var i = 0 ; i < indices.length; ++i)
{
actualStrip.push(pos[(indices[i]-1)*2+0]);
actualStrip.push(pos[(indices[i]-1)*2+1]);
}
log.info("actualStrip="+JSON.stringify(actualStrip));
log.info("actualStrip.length="+actualStrip.length);

Solving the seventh with O(n)

I have solved the seventh problem of Euler, it says:
By listing the first six prime numbers: 2, 3, 5, 7, 11, and 13, we can
see that the 6th prime is 13.
What is the 10 001st prime number?
I solved it using, and in the array in which I keep the cousins, when it reaches the length of 10001, I return that number. The algorithm takes 1300 ms, which I tink that is very inefficient, what am I doing particularly in my implementation?
var start = performance.now();
function eratosthenes(n) {
var arr = [2], acc = 0;
// matrix to save the found prime numbers and mark their multiples
for(var i = 3; true; i += 2) { // loop
if(arr.length === n) return arr[arr.length - 1]; // if the array length is equal to n return the last number
if(!resolve(arr, i)) { // check if is multiple of the prime numbers, already found.
arr.push(i); // if isnt multiple, save it
}
}
}
function resolve(array, n) {
return array.some(cur => !(n%cur));
}
console.log(eratosthenes(10001)); // Tooks 1300 ms
var end = performance.now();
var time = end - start;
console.log(time);
Euler sieve, Pham knows this one :) 12ms
Uchu, I don't see where your code is marking the multiples. Isn't that what Sieve of Eratosthenes is supposed to do?
JavaScript code (this code is actually an adaptation of code by btilly, optimizing an idea of mine):
var start = performance.now();
n = 115000
a = new Array(n+1)
total = 0
s = []
p = 1
count = 0
while (p < n){
p = p + 1
if (!a[p]){
count = count + 1
if (count == 10001){
console.log(p);
end = performance.now();
time = end - start;
console.log(time);
break;
}
a[p] = true
s.push(p)
limit = n / p
new_s = []
for (i of s){
j = i
while (j <= limit){
new_s.push(j)
a[j*p] = true;
j = j * p
}
}
s = new_s
}
}
As requested by JaromandaX, this is the code for Sieve of Eratosthenes. 51 ms on my browser (OP solution is 750 ms)
var max = 1000000;
function eratosthenes(n) {
var arr = [], count = 0;
for (var i = 0; i < max; i++){
arr.push(true);
}
for (var i = 2; i < max; i++){
if(arr[i]){
count++;
if(count == n){
return i;
}
for (var j = i + i; j < max; j += i ){
arr[j] = false;
}
}
}
}
var start = performance.now();
console.log(eratosthenes(10001));
var end = performance.now();
var time = end - start;
console.log(time);
This has a similar running time to גלעד ברקן's answer (actually about 10% faster on my machine), but doesn't rely on knowing an approximate max before starting. It performs a seive of Eratosthenes up to max (starting at 2) and then doubles max, initialises the new elements in the array per the previously found primes and repeats.
function eratosthenes(n) {
let prev_max = 1, max = 2, i, j;
const primes = [], is_prime = new Array(max+1).fill(true);
while( true ) {
for ( i = prev_max + 1; i <= max; i++){
if ( ! is_prime[i] ) continue;
primes.push( i );
if ( primes.length === n )
return i;
for ( j = i + i; j <= max; j += i )
is_prime[j] = false;
}
const next_max = max*2;
is_prime.length = next_max + 1;
is_prime.fill( true, max + 1, next_max );
for ( i = 0; i < primes.length; i++ ) {
const prime = primes[i];
for ( j = max + prime - max%prime; j <= next_max; j += prime )
is_prime[j] = false;
}
prev_max = max;
max = next_max;
}
}
var start = performance.now();
console.log(eratosthenes(10001));
var end = performance.now();
var time = end - start;
console.log(time);
If it is a code-writing exercise, it is better to explore the earlier answers.
But if you are after a simple and fast solution, here's how you can solve it using prime-lib, which I created:
import {generatePrimes} from 'prime-lib';
const seekIndex = 10_001; // index of the prime being sought
const start = Date.now();
let a, c = 0;
const i = generatePrimes({boost: seekIndex + 1});
while ((a = i.next()) && !a.done && c++ < seekIndex) ;
console.log(`Prime: ${a.value}, took ${Date.now() - start}ms`);
On my PC it spits out:
Prime: 104759, took 5ms
And with the modern RXJS this becomes even simpler still:
import {generatePrimes} from 'prime-lib';
import {from, last} from 'rxjs';
const seekIndex = 10_001; // index of the prime being sought
const i = generatePrimes({boost: seekIndex + 1});
from(i).pipe(last()).subscribe(console.log);
//=> 104759

javascript canvas: draw moving average line with curves

So basically, I want to draw a curved average line over a certain amount of points of a time-series line chart. Like this:
I want it to span the entire length of the chart but I can't figure out how to calculate the start and end points because the average would (I think) be a point in the middle of each section. Looking at a stock chart with moving average you can see what I want to acheive:
I calculate the averages first by splitting the data array up into chunks based on a period of time. So if I start with:
[
{ time: 1, value: 2 },
{ time: 2, value: 4 },
{ time: 3, value: 5 },
{ time: 4, value: 7 },
]
I get to:
var averages = [
{
x: 1.5,
y: 3,
},
{
x: 3.5 (the average time)
y: 6 (the average value)
},
]
This is what I've tried where I end up with an incomplete line, one that doesnt start at the beginning of the chart and doesnt stop at the end, but stars and ends inside the chart at the first average time:
ctx.moveTo((averages[0].x), averages[0].y);
for(var i = 0; i < averages.length-1; i ++)
{
var x_mid = (averages[i].x + averages[i+1].x) / 2;
var y_mid = (averages[i].y + averages[i+1].y) / 2;
var cp_x1 = (x_mid + averages[i].x) / 2;
var cp_x2 = (x_mid + averages[i+1].x) / 2;
ctx.quadraticCurveTo(cp_x1, averages[i].y ,x_mid, y_mid);
ctx.quadraticCurveTo(cp_x2, averages[i+1].y ,averages[i+1].x, averages[i+1].y);
}
ctx.stroke();
How would you do this?
To get a moving mean you need to just get the mean of n points either side of the current sample.
For example
// array of data points
const movingMean = []; // the resulting means
const data = [12,345,123,53,134,...,219]; // data with index representing x axis
const sampleSize = 5;
for(var i = sampleSize; i < data.length - sampleSize; i++){
var total = 0;
for(var j = i- sampleSize; j < i + sampleSize; j++){
total += data[j];
}
movingMean[i] = total / (sampleSize * 2);
}
This method does not pull the mean forward giving the most accurate mean for each data point.
The problem with this method is that you do not get a mean for the first n and last n samples, where n is the number of samples either side of the mean.
You can do an alternative that will pull the mean forward a little but by applying a weighted mean you can reduce the bias a little
for(var i = sampleSize; i < data.length + Math.floor(sampleSize / 4); i++){
var total = 0;
var count = 0;
for(var j = sampleSize; j > 0; j --){
var index = i - (sampleSize - j);
if(index < data.length){
total += data[index] * j; // linear weighting
count += j;
}
}
movingMean[i-Math.floor(sampleSize / 4)] = total / count;
}
This method keeps that mean closer to the current sample end.
The example show a random data set and the two types of means plotted over it. Click to get a new plot. The red line is the moving mean and the blue is the weighted mean. Note how the blue line tends to follow the data a little slow.
The green line is a weighted mean that has a sample range 4 times greater than the other two.
// helper functions
const doFor = (count, callback) => {var i = 0; while (i < count) { callback(i ++) } };
const setOf = (count, callback) => {var a = [],i = 0; while (i < count) { a.push(callback(i ++)) } return a };
const rand = (min, max = min + (min = 0)) => Math.random() * (max - min) + min;
const randG = (dis, min, max) => {var r = 0; doFor(dis,()=>r+=rand(min,max)); return r / dis};
function getMinMax(data){
var min = data[0];
var max = data[0];
doFor(data.length - 1, i => {
min = Math.min(min,data[i+1]);
max = Math.max(max,data[i+1]);
});
var range = max-min;
return {min,max,range};
}
function plotData(data,minMax){
ctx.beginPath();
for(var i = 0; i < data.length; i++){
if(data[i] !== undefined){
var y = (data[i] - minMax.min) / minMax.range;
y = y *(ctx.canvas.height - 2) + 1;
ctx.lineTo(i/2,y);
}
}
ctx.stroke();
}
function getMovingMean(data,sampleSize){
const movingMean = []; // the resulting means
for(var i = sampleSize; i < data.length - sampleSize; i++){
var total = 0;
for(var j = i- sampleSize; j < i + sampleSize; j++){
total += data[j];
}
movingMean[i] = total / (sampleSize * 2);
}
return movingMean[i];
}
function getMovingMean(data,sampleSize){
const movingMean = []; // the resulting means
for(var i = sampleSize; i < data.length - sampleSize; i++){
var total = 0;
for(var j = i- sampleSize; j < i + sampleSize; j++){
total += data[j];
}
movingMean[i] = total / (sampleSize * 2);
}
return movingMean;
}
function getWeightedMean(data,sampleSize){
const weightedMean = [];
for(var i = sampleSize; i < data.length+Math.floor(sampleSize/4); i++){
var total = 0;
var count = 0;
for(var j = sampleSize; j > 0; j --){
var index = i - (sampleSize - j);
if(index < data.length){
total += data[index] * j; // linear weighting
count += j;
}
}
weightedMean[i-Math.floor(sampleSize/4)] = total / count;
}
return weightedMean;
}
const dataSize = 1000;
const sampleSize = 50;
canvas.width = dataSize/2;
canvas.height = 200;
const ctx = canvas.getContext("2d");
function displayData(){
ctx.clearRect(0,0,ctx.canvas.width,ctx.canvas.height);
var dataPoint = 100;
var distribution = Math.floor(rand(1,8));
var movement = rand(2,20);
const data = setOf(dataSize,i => dataPoint += randG(distribution, -movement, movement));
const movingMean = getMovingMean(data, sampleSize);
const weightedMean = getWeightedMean(data, sampleSize*2);
const weightedMean1 = getWeightedMean(data, sampleSize*8);
var minMax = getMinMax(data);
ctx.strokeStyle = "#ccc";
plotData(data,minMax);
ctx.strokeStyle = "#F50";
plotData(movingMean,minMax);
ctx.strokeStyle = "#08F";
plotData(weightedMean,minMax);
ctx.strokeStyle = "#4C0";
plotData(weightedMean1,minMax);
}
displayData();
document.onclick = displayData;
body { font-family : arial; }
.red { color : #F50; }
.blue { color : #0AF; }
.green { color : #4C0; }
canvas { position : absolute; top : 0px; left :130px; }
<canvas id="canvas"></canvas>
<div class="red">Moving mean</div>
<div class="blue">Weighted mean</div>
<div class="green">Wide weighted mean</div>
<div>Click for another sample</div>

execute javascsript for loop alternately forward and backward

UPDATED BELOW
What I'm trying to do is iterate through an array in chunks, alternating the direction of iteration from chunk to chunk. Confused? So am I. For example, if I want to loop thru an array with 25 elements, but I want to do it in this order: 0, 1, 2, 3, 4, 9, 8, 7, 6, 5, 10, 11, 12, 13, 14, 19, 18, 17, 16, 15, 20, 21, 22, 23, 24, what would be the most efficient way of doing this? I'm looking for something scalable because the array I'm working with now is actually 225 elements and I want to traverse it in 15 element chunks, but that may change at some point. So far, the only method I've figured out that actually works is to hard-wire the iteration order into a second array and then iterate through that in the normal way to get the indices for the original array. But that sucks. Any help would be greatly appreciated.
#Bergi asked for some sample code. Please don't beat me up too much. I'm still a noob:
function zeroPadNumber(theNumber, thePadding) {
var thePaddedNumber = thePadding.substring(0, (thePadding.length - theNumber.length)) + theNumber;
return thePaddedNumber;
}
var thisTile = 225;
var waveLoop = 15;
function mosaicWave() {
var theStartNum = thisTile;
for (w = 0; w < 15; w++) {
var theNum = theStartNum - w;
var theNumString = String(theNum);
var thePaddedNum = zeroPadNumber(theNumString, "000");
var theImgName = "sm_" + thePaddedNum;
var theNewSrc = theImgFolder + theImgName + "bg.gif";
document.images[theImgName].src = theNewSrc;
thisTile = theNum - 1;
if (waveLoop < 15) {
var prevStartTile = theStartNum + 15;
var thePrevNum = prevStartTile - w;
var thePrevNumString = String(thePrevNum);
var thePrevPaddedNum = zeroPadNumber(thePrevNumString, "000");
var thePrevName = "sm_" + thePrevPaddedNum;
var thePrevSrc = theImgFolder + thePrevName + ".gif";
document.images[thePrevName].src = thePrevSrc;
}
}
if (waveLoop == 1) {
var lastWave = function() {
var theStartNum = 15;
for (c = 0; c < 15; c++) {
var theNum = theStartNum - c;
var theNumString = String(theNum);
var thePaddedNum = zeroPadNumber(theNumString, "000");
var theImgName = "sm_" + thePaddedNum;
var theNewSrc = theImgFolder + theImgName + ".gif";
document.images[theImgName].src = theNewSrc;
}
}
setTimeout(lastWave, 100);
waveLoop = 15;
thisTile = 225;
} else {
waveLoop--;
setTimeout(mosaicWave, 100);
}
}
This snippet does a different animation. It starts at the lower right corner of the matrix and "turns on" the 15 tiles in the bottom row. then it moves up a row, turns on the tiles in that row and turns off the tiles in the previous row. And so forth until the top row is turned on then off. not very far off really from the top-down serpentine effect I'm trying to achieve in the new function. The reversing order on each row was the main thing stumping me. That being said, any suggestions about optimizing the above code would also be greatly appreciated.
UPDATE 1:
To me, this seems like it should work, but it doesn't. Can anyone spot the problem?
var loopRange = 225;
var blockRange = 15;
var theDirection = 1;
var weaveLoop = 0;
function mosaicWeave() {
var curObj, curSrc, lastObj, lastSrc;
var toggleLeadTile = function() {
alert(curSrc);
curObj.src = curSrc;
};
var toggleLastTile = function() {
lastObj.src = lastSrc;
};
while (weaveLoop < loopRange) {
imgNum = weaveLoop + 1;
imgName = "sm_" + zeroPadNumber(String(imgNum), "000");
if (imgNum < 15) {
//handle first row
curObj = document.images[imgName];
curSrc = theImgFolder + imgName + "bg.gif";
window.setTimeout(toggleLeadTile, 100);
} else if (imgNum == 225) {
//handle last row
curObj = document.images[imgName].src;
curSrc = theImgFolder + imgName + "bg.gif";
window.setTimeout(toggleLeadTile, 100);
for (i = 211; i < 226; i++) {
lastImgName = "sm_" + ((weaveLoop + 1) - 15);
lastObj = document.images[lastImgName];
lastSrc = theImgFolder + lastImgName + ".gif";
window.setTimeout(toggleLastTile, 100);
}
} else {
//handle middle rows
lastImgName = "sm_" + ((weaveLoop + 1) - 15);
curObj = document.images[imgName];
curSrc = theImgFolder + imgName + "bg.gif";
lastObj = document.images[lastImgName];
lastSrc = theImgFolder + lastImgName + ".gif";
window.setTimeout(toggleLeadTile, 100);
window.setTimeout(toggleLastTile, 100);
}
if (weaveLoop % blockRange == (theDirection == -1 ? 0 : blockRange - 1)) {
theDirection *= -1;
weaveLoop += blockRange;
} else {
weaveLoop += theDirection;
}
}
}
UPDATE 2:
Thanks for everyone's input. This works:
var resetLoop = 1;
var weaveArray = new Array(225);
var weaveRange = 15, weaveDirection = 1, weaveIndex = 0, wInitLoop = 0;
function mosaicWeave() {
while (weaveIndex < 225) {
weaveArray[wInitLoop] = weaveIndex + 1;
if (weaveIndex % weaveRange == (weaveDirection == -1 ? 0 : weaveRange - 1)) {
weaveDirection *= -1;
weaveIndex += weaveRange;
} else {
weaveIndex += weaveDirection;
}
wInitLoop++;
}
mWeaveOn();
}
function mWeaveOff() {
var theNumString = String(weaveArray[resetLoop - 16]);
var theImgName = "sm_" + zeroPadNumber(theNumString, "000");
document.images[theImgName].src = "images/" + theImgName + ".gif";
mosaicArray[resetLoop - 1] = 0;
resetLoop++;
if (resetLoop < 226) {
setTimeout(mWeaveOn, 25);
} else if (resetLoop > 225 && resetLoop <= 240) {
setTimeout(mWeaveOff, 25);
} else {
resetLoop = 1;
}
}
function mWeaveOn() {
var theNumString = String(weaveArray[resetLoop - 1]);
var theImgName = "sm_" + zeroPadNumber(theNumString, "000");
document.images[theImgName].src = "images/" + theImgName + "bg.gif";
mosaicArray[resetLoop - 1] = 1;
if (resetLoop < 16) {
resetLoop++;
setTimeout(mWeaveOn, 25);
} else {
setTimeout(mWeaveOff, 25);
}
}
Does anyone have an opinion on if there is a more efficient way of doing this? Or a heads up on how this might break on different platforms/browsers or under different circumstances? Thanks again.
var arr = [0,1,2,3,4,5,6,7,8,9,10,11,11,13,14,15,16,17,18,19,20,21,22,23,24],
i = 0,
j = arr.length,
tmp,
chunk = 5;
while(i < j) {
tmp = arr.slice(i, i+=chunk);
if ((i / chunk) % 2 == 0) {
tmp = tmp.reverse();
}
console.log(tmp);
}
​The demo.
This is a flexible solution where you can change the blocksize as you need it.
var index, max = 25;
for (var i = 0; i < max; i++) {
if (parseInt(i / 5) % 2)
index = parseInt(i / 5)*5 + 4 - i % 5;
else
index = i;
// use index as array index
foo(index);
}
Fiddle
If you have always a multiple of five, you could hard code the iteration over five elements and do an outer loop which counts to max/5 and switch to the right hardcoded iteration.
var index, max = 25;
for ( var i=0; i<max/5; i++) {
if (i%2) {
foo(i*5+4);
foo(i*5+3);
foo(i*5+2);
foo(i*5+1);
foo(i*5+0);
} else {
foo(i*5+0);
foo(i*5+1);
foo(i*5+2);
foo(i*5+3);
foo(i*5+4);
}
}
Fiddle
I guess the simplest and clearest solution would be to nest two loos:
var arr = new Array(25),
chunksize = 5;
for (var i=0; i<arr.length; i+=chunksize)
if (i % (chunksize*2))
for (var j=i+chunksize-1; j>=i; j--)
exec(j);
else
for (var j=i; j<i+chunksize; j++)
exec(j);
However, you could also go with only one loop and loopcounter. In the right spots (4, 5, 14, 15, …) the direction (increment/decrement) would change and the counter jumps one chunksize (4→9, 5→10, 14→19, …):
var arr = new Array(25),
chunksize = 5;
var dir = 1,
i = 0;
while (i<arr.length) {
exec(i); // or whatever you need to do
if (i % chunksize == (dir==-1 ? 0 : chunksize - 1)) {
dir *= -1;
i += chunksize;
} else
i += dir;
}
or, in one for-statement:
for (var dir=1, i=0; i<arr.length; i+= (i+1)%chunksize == (dir==-1) ? (dir*=-1) && chunksize : dir)
exec(i); // or whatever you need to do
This function accepts an array and a blocksize (in your example, 5)
function forwardAndBack(arr, blocksize){
var i, j, l = arr.length ;
for (i = 0 ; i < l ; i++){
if (i % (2 * blocksize) > (blocksize - 1)){
j = i + (blocksize - (2*(i % blocksize)))-1 ;
}
else {
j = i ;
}
arr[j] && myFunction(arr[j]) ; // In case you've gone too high
}
}
Used like this:
var arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24] ;
var result = [] ;
function myFunction(x){result.push(x)} ;
forwardAndBack(arr, 5);
console.log(result) ; // returns [0, 1, 2, 3, 4, 9, 8, 7, 6, 5, 10, 11, 12, 13, 14, 19, 18, 17, 16, 15, 20, 21, 22, 23, 24]

Categories