Need explanation of basic JavaScript code - javascript

I am completely new to JavaScript and I try to understand some basic code.
I need explanation of what's going on in this code:
circles = d3.range(numDisks).map(function(i) {
return {
index: i,
x: Math.round(Math.random() * (width - radius * 2) + radius),
y: Math.round(Math.random() * (height - radius * 2) + radius)
};
});
I don't need explanation of math round/random etc.
I need to understand what data structure is built and how to delete elements from this data structure? Not null-them out but complete remove with a shift or whatever.
Thanks you

the data structure created is just an array of objects. Starting with the d3.range function which returns an array of objects which you then map into a new array of objects with the array map operator
you can't remove items in an array map operator as map transforms all objects in the original array, you need to add a filter operator before or after the map with the removal logic.
circles = d3.range(numDisks).map(function(i) {
return {
index: i,
x: Math.round(Math.random() * (width - radius * 2) + radius),
y: Math.round(Math.random() * (height - radius * 2) + radius)
};
}).filter(i => i.x !== 0); // this filter removes items where their x value = 0

Related

Accessing a random variable from an array for use in another varaible

I have one of four possibilities for this particular variable -->
var lowVelocity = Math.random() * (45 - 30) + 30;
var medVelocity = Math.random() * (60 - 45) + 45;
var highVelocity = Math.random() * (80 - 60) + 45;
var hwyVelcoity = Math.random() * (100 - 80) +80;
In one scenario I might have either lowVelocity and medVelocity so I want to choose randomly between those two only. So I did this by:
const velocities = ["lowVelocity", "medVelocity"];
const randomUrban = Math.floor(Math.random() * velocities.length);
And this works - it now chooses one of the two velocities that I am interested in. Low velocity is between 30 and 45 and med is between 45 and 60.
In my innerHTML I want to print the number returned from that variable chosen in the array. When the user clicks this starts a function that has this in it.
document.getElementById("scenario").innerHTML = randomUrban;
But when it prints in the HTML, it doesn't print the number that is associated with the variable, it prints the array number it chose (like 0 or 1).
How do I get the variable (eg if it chose lowVelocity then the number it prints will be the random number lowVelocity found, eg 39) to print instead of the array number?
You could just use a single array to do all of this for you!
If you add the velocities to an array, then the random number can be used as an index into this array. In the code below, we use the length of this array to set the limits for the random number, then you can simply access the value from the velocities array at the position indicated by the random number:
velocities[randomUrban][1]
This gets the sub-array at the position indicated by randomUrban, and [1] gets the second element in the row, i.e. the calculated velocity.
/* Add the velocities to an array or easier management */
var velocities= [
["lowVelocity", Math.random() * (45 - 30) + 30],
["medVelocity", Math.random() * (60 - 45) + 45],
["highVelocity",Math.random() * (80 - 60) + 45],
["hwyVelcoity" ,Math.random() * (100 - 80) +80]
]
/* Pick a random position from the velocities */
const randomUrban = Math.floor(Math.random() * velocities.length);
/* Print the velocity at that position. (Also includes the velocity name for illustration) */
document.getElementById("scenario").innerHTML =
velocities[randomUrban][0] + " = " + velocities[randomUrban][1];
<div id="scenario"></div>
Note: In your example you only use 2 of the 4 velocities, so you can just add the velocities you want to choose from into the randomVelocities array.
In your case just use
document.getElementById("scenario").innerHTML = window[velocities[randomUrban]];
var lowVelocity = Math.random() * (45 - 30) + 30;
var medVelocity = Math.random() * (60 - 45) + 45;
var highVelocity = Math.random() * (80 - 60) + 45;
var hwyVelcoity = Math.random() * (100 - 80) + 80;
const velocities = ["lowVelocity", "medVelocity"];
const randomUrban = Math.floor(Math.random() * velocities.length);
console.log(randomUrban);// random from velocities
console.log(velocities[randomUrban]); //get variable name
console.log(window[velocities[randomUrban]]); // search in scope where the variables are declared in this case window
How about document.getElementById("scenario").innerHTML = velocities[randomUrban]; ?
Well, you are trying to print the wrong variable; randomUrban is the variable containing the velocities array index selected.
A quick (but pretty dirty) fix would be printing eval(velocities[randomUrban]) but eval() has been deprecated because of security issues.
A bit longer but better solution would be grouping your velocities into a single variable of type object and access its keys to retrieve the numeric value:
const velObj = {
lowVelocity: Math.random() * (45 - 30) + 30,
medVelocity: Math.random() * (60 - 45) + 45,
highVelocity: Math.random() * (80 - 60) + 45,
hwyVelocity: Math.random() * (100 - 80) + 80
};
const velocities = ["lowVelocity", "medVelocity"];
const randomUrban = Math.floor(Math.random() * velocities.length);
const velKey = velocities[randomUrban];
document.getElementById("scenario").innerHTML = velObj[velKey];
<div id="scenario"></div>

get X and Y of a tile on a tileset

I'm trying to write a function that returns the position of a tile on a tileset.
I wrote some code for that but that seems unnecessarily complicated.
how each tile has a number:
// My old code:
function getXPosByNumber(width, height, tilesize, tilenumber) {
if ((tilenumber % (width / tilesize) - 1) < 0) {
return width - tilesize;
} else {
return (tilenumber % (width / tilesize) - 1) * tilesize;
}
}
function getYPosByNumber(width, height, tilesize, tilenumber) {
return (Math.ceil(tilenumber / (width / tilesize)) - 1) * tilesize;
}
How can I access any tile coordonates with a cleaner/simpler code ?
Thanks.
Here's what I figured out to simplify. I found that height isn't so relevant to calculate either X or Y. The important factor here is how much it spreads horizontally.
Then, since a specific tileset will more likely always have the same width and tilesize, I suggest a more "Object"-ified approach so you don't have to constantly remember its configuration. Personally, I like to wrap constants and configurations so I can write lightweight code afterwards:
var tileset = function(width, tilesize) {
this.width = width;
this.tilesize = tilesize;
};
// calculate X by tilenumber
tileset.prototype.getX = function(tilenumber) {
return (tilenumber % this.width - 1) * this.tilesize;
};
// calculate Y by tilenumber
tileset.prototype.getY = function(tilenumber) {
return Math.floor(tilenumber / this.width) * this.tilesize;
};
That allows you to use it like this:
var mySet = new tileset(6, 10); // width=6, tilesize=10
var x = mySet.getX(16); // tilenumber=16
var y = mySet.getY(16); // tilenumber=16
To see it in action, check this FIDDLE.
Please note that it calculates the position of top left corner. It can be adjusted otherwise if needed.
Hope this helps!
If this tileset is always the same I would suggest to create a kind of lookup table. An object that will store precalculated positions for each tile number. It will be much simpler and faster since you only need to access property of an object.

D3.js: Getting my own data for the layout

I have experimenting around with the D3 cluster force layout.
However I am not sure as to how to bind the data and hence, I am not able to have my own data within.
This is what I have tried, without any binding, it's bound not to work.
d3.tsv("data.tsv", function(data){
nodes = d3.range(n).map(function(data) {
var i = Math.floor(Math.random() * m),
r = Math.sqrt((i + 1) / m * -Math.log(data.diskSpace)) * maxRadius,
d = {
cluster: i,
radius: data.radius,
x: Math.cos(i / m * 2 * Math.PI) * 200 + width / 2 + Math.random(),
y: Math.sin(i / m * 2 * Math.PI) * 200 + height / 2 + Math.random()
};
if (!clusters[i] || (r > clusters[i].radius)) clusters[i] = d;
return d;
});
This does not seem to work as expected.
What exactly is happening and how do I fix it?
EDIT:
The TSV file
model diskSpace radius
DE431 8 8
DD342 16 18
...
First, make sure the file is actually separated with tabs and not spaces. Loading a tsv in d3 looks like this:
d3.tsv('data.tsv', function (rows) {
console.log(rows);
});
an array of rows will be logged to the console. Note that this call is made asynchronously, the d3.tsv()-function does not return the rows but instead calls a function with the rows as first argument.
Now we need to transform this data into something the d3 force layout understands:
d3.tsv('data.tsv', function (rows) {
var nodes = rows.map(function (item) {
var i = item['diskSpace'],
r = +item['radius'], // note the + to convert to number here.
d = {
cluster: i,
radius: r,
x: Math.cos(i / m * 2 * Math.PI) * 200 + width / 2 + Math.random(),
y: Math.sin(i / m * 2 * Math.PI) * 200 + height / 2 + Math.random()
};
if (!clusters[i] || (r > clusters[i].radius)) {
clusters[i] = d;
}
return d;
});
// insert code generating the force layout here (including the cluster and collide functions)
});
This will cluster the rows by diskSpace. Please note the comments I added.
Working Demo here

Generating a Regular Polygon with Three.js

I'm using Three.js to procedurally generate a regular N-gon based on a user-provided number of sides. The long-term goal is to use this as the first step in rendering a polyhedral prism.
I'm using the solution discussed here to calculate the vertices of the N-gon.
I'm then using the technique discussed here to generate faces on the N-gon.
My first attempt to produce the necessary Geometry object resulted in the following, which doesn't seem to render anything after being added to a Mesh:
function createGeometry (n, circumradius) {
var geometry = new THREE.Geometry(),
vertices = [],
faces = [],
x;
// Generate the vertices of the n-gon.
for (x = 1; x <= n; x++) {
geometry.vertices.push(new THREE.Vector3(
circumradius * Math.sin((Math.PI / n) + (x * ((2 * Math.PI)/ n))),
circumradius * Math.cos((Math.PI / n) + (x * ((2 * Math.PI)/ n))),
0
));
}
// Generate the faces of the n-gon.
for (x = 0; x < n-2; x++) {
geometry.faces.push(new THREE.Face3(0, x + 1, x + 2));
}
geometry.computeBoundingSphere();
return geometry;
}
After toying with that for too long, I discovered the ShapeGeometry class. This uses the same vertex algorithm as the above example, but this one renders properly after being added to a Mesh:
function createShapeGeometry (n, circumradius) {
var shape = new THREE.Shape(),
vertices = [],
x;
// Calculate the vertices of the n-gon.
for (x = 1; x <= sides; x++) {
vertices.push([
circumradius * Math.sin((Math.PI / n) + (x * ((2 * Math.PI)/ n))),
circumradius * Math.cos((Math.PI / n) + (x * ((2 * Math.PI)/ n)))
]);
}
// Start at the last vertex.
shape.moveTo.apply(shape, vertices[sides - 1]);
// Connect each vertex to the next in sequential order.
for (x = 0; x < n; x++) {
shape.lineTo.apply(shape, vertices[x]);
}
// It's shape and bake... and I helped!
return new THREE.ShapeGeometry(shape);
}
What's wrong with the Geometry example that's resolved with the ShapeGeometry example?
I don't think it's an issue with camera or positioning because replacing the complex vertex calculations with simpler whole numbers produces a polygon without an issue, provided the values make sense.
The reason I'm asking is because, as I mentioned initially, I'd like to eventually use this as the first step in rendering a polyhedron. ShapeGeometry objects can be extruded to give them depth, but even with the options that Three.js makes available, this may not be enough for my needs in the long run as the required polyhedra become more irregular.
Any thoughts?
You can create prisms using THREE.CylinderGeometry; for an n-sided prism, you could use
// radiusAtTop, radiusAtBottom, height, segmentsAroundRadius, segmentsAlongHeight
var nPrism = new THREE.CylinderGeometry( 30, 30, 80, n, 4 );
You can also use CylinderGeometry to create pyramids and frustums; for more examples of built-in shapes, you can check out:
http://stemkoski.github.io/Three.js/Shapes.html
Since you also sound like you may be interested in more general polyhedra, you might also want to check out:
http://stemkoski.github.io/Three.js/Polyhedra.html
which includes models of the Platonic Solids, Archimedean Solids, Prisms, Antiprisms, and Johnson Solids; however, in that program the polyhedra are "thick" from using spheres for vertices and cylinders for edges.
Hope this helps!
Your function works as expected.
Look at this fiddle http://jsfiddle.net/Elephanter/mUah5/
there is a modified threejs fiddle with your createGeometry function
So you have problem in another place, not at createGeometry function

How to do smooth transition for map reprojection in d3 js

I'd like to do a smooth transition between alber/orthographic in a mini app I am building, much like this example:
http://mbostock.github.io/d3/talk/20111018/#27
It seems like this smooth transition is broken in v3 however, with a rather choppy transition of the map paths:
https://www.evernote.com/shard/s236/sh/46b002bd-9c5b-4e9b-87ef-270c303eb677/2eaeebb267a3fc59df5a8447bbbcc58b/res/37917835-5aad-4509-b534-31a3e3034762/Worst_Tornado_Outbreaks_of_All_Time-20130611-074050.jpg.jpg?resizeSmall&width=832
Code is pretty straight forward, I initialize the map as albers, then run ortho() to update it.
function ortho() {
var self = this,
h = 1000,
w = document.width;
this.projection = d3.geo.orthographic()
.scale(500)
.translate([ (w - 300) / 2, h / 2])
.clipAngle(90)
.rotate([90, 0, 0])
.precision(.1);
this.path = d3.geo.path()
.projection(this.projection);
//update path WITH transition
d3.selectAll('path')
.transition()
.duration(900)
.attr('d', app.path);
}
The map changes from albers to orthographic, but the transition is not smooth. Any thoughts would be great.
If you interpolate the path using D3’s naïve string interpolator (d3.interpolateString), then the number of coordinates in the starting path and the number of the coordinates in the ending path must match exactly, including in the same order. But this is almost never the case due to clipping, cutting and resampling. Shape interpolation is possible (using multiple strategies), but it’s a hard problem to solve in the general case. See this explanation (part of the Path Transitions) for why naïve interpolation is insufficient.
Instead of interpolating the path, you want to interpolate the projection. Interpolating the projection does not require an exact correspondence between coordinates and therefore avoids interpolation artifacts. See these examples for a demonstration:
http://bl.ocks.org/mbostock/5731632
http://bl.ocks.org/mbostock/3711652
http://www.jasondavies.com/maps/transition/
As shown in the first example, here is an implementation you can use:
function interpolatedProjection(a, b) {
var projection = d3.geo.projection(raw).scale(1),
center = projection.center,
translate = projection.translate,
α;
function raw(λ, φ) {
var pa = a([λ *= 180 / Math.PI, φ *= 180 / Math.PI]), pb = b([λ, φ]);
return [(1 - α) * pa[0] + α * pb[0], (α - 1) * pa[1] - α * pb[1]];
}
projection.alpha = function(_) {
if (!arguments.length) return α;
α = +_;
var ca = a.center(), cb = b.center(),
ta = a.translate(), tb = b.translate();
center([(1 - α) * ca[0] + α * cb[0], (1 - α) * ca[1] + α * cb[1]]);
translate([(1 - α) * ta[0] + α * tb[0], (1 - α) * ta[1] + α * tb[1]]);
return projection;
};
delete projection.scale;
delete projection.translate;
delete projection.center;
return projection.alpha(0);
}
Create the interpolated projection using two projections a and b, and then set the interpolated alpha to a value between 0 (for a) and 1 (for b).

Categories

Resources