Plotly surface data is arranged as a 2d Array (a matrix) whose indices correspond to x and y values and whose elements indicate the z values. E.g. if element [0][0] equals 10, that indicates an (x,y,z) coordinate of (0,0,10).
The problem: Because Array indices start at zero, it seems impossible to graph surfaces that have negative x or y values.
Here is a CodePen with three surfaces plotted. The surfaces look fine because only two octants are shown in the graph - the (+x,+y) quadrant. If all octants are displayed in the graph (CodePen), then it ends up looking incomplete because the plotted surfaces won't extend into the remaining 3 quadrants.
The general form of surface data is:
{ z : dataArray,
type : 'surface',
opacity : 0.9 }
Is there a way to give the surface data an xstart or ystart, or the like, so that full 3d surfaces can be drawn?
You can add your own x and y coordinates, Plotly just assumes them if you do not provide them.
From the documentation:
x (data array)
Sets the x coordinates.
y (data array)
Sets the y coordinates.
Code is based on the example here.
z = [[8.83,8.89,8.81,8.87,8.9,8.87],
[8.89,8.94,8.85,8.94,8.96,8.92],
[8.84,8.9,8.82,8.92,8.93,8.91],
[8.79,8.85,8.79,8.9,8.94,8.92],
[8.79,8.88,8.81,8.9,8.95,8.92],
[8.8,8.82,8.78,8.91,8.94,8.92],
[8.75,8.78,8.77,8.91,8.95,8.92],
[8.8,8.8,8.77,8.91,8.95,8.94],
[8.74,8.81,8.76,8.93,8.98,8.99],
[8.89,8.99,8.92,9.1,9.13,9.11],
[8.97,8.97,8.91,9.09,9.11,9.11],
[9.04,9.08,9.05,9.25,9.28,9.27],
[9,9.01,9,9.2,9.23,9.2],
[8.99,8.99,8.98,9.18,9.2,9.19],
[8.93,8.97,8.97,9.18,9.2,9.18]];
var x = [];
var y = [];
for (var i = 0; i < z.length; i += 1) {
x[i] = [];
y[i] = [];
for (var j = 0; j < z[i].length; j += 1) {
x[i].push(j + i - 10);
y[i].push(j - 3);
}
}
Plotly.newPlot('myDiv', [{z: z,
x: x,
y: y,
type: 'surface'}]);
<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
<div id="myDiv"></div>
Related
I am currently developing a game, which requires a map consisting of various tile images. I managed to make them display correctly (see second image) but I am now unsure of how to calculate the clicked tile from the mouse position.
Are there any existing libraries for this purpose?
Please also note, that the tile images aren't drawn perfectly "corner-facing-camera", they are slightly rotated clockwise.
Isometric Transformations
Define a projection
Isometric display is the same as standard display, the only thing that has changed is the direction of the x and y axis. Normally the x axis is defined as (1,0) one unit across and zero down and the y axis is (0,1) zero units across and one down. For isometric (strictly speaking your image is a dimetric projection) you will have something like x axis (0.5,1) and y axis (-1,0.5)
The Matrix
From this you can create a rendering matrix with 6 values Two each for both axes and two for the origin, which I will ignore for now (the origin) and just use the 4 for the axis and assume that the origin is always at 0,0
var dimetricMatrix = [0.5,1.0,-1,0.5]; // x and y axis
Matrix transformation
From that you can get a point on the display that matches a given isometric coordinate. Lets say the blocks are 200 by 200 pixels and that you address each block by the block x and y. Thus the block in the bottom of your image is at x = 2 and y = 1 (the first top block is x = 0, y = 0)
Using the matrix we can get the pixel location of the block
var blockW = 200;
var blockH = 200;
var locX = 2;
var locY = 1;
function getLoc(x,y){
var xx,yy; // intermediate results
var m = dimetricMatrix; // short cut to make code readable
x *= blockW; // scale up
y *= blockH;
// now move along the projection x axis
xx = x * m[0];
yy = x * m[1];
// then add the distance along the y axis
xx += y * m[2];
yy += y * m[3];
return {x : xx, y : yy};
}
Befoer I move on you can see that I have scaled the x and y by the block size. We can simplify the above code and include the scale 200,200 in the matrix
var xAxis = [0.5, 1.0];
var yAxis = [-1, 0.5];
var blockW = 200;
var blockH = 200;
// now create the matrix and scale the x and y axis
var dimetricMatrix = [
xAxis[0] * blockW,
xAxis[1] * blockW,
yAxis[0] * blockH,
yAxis[1] * blockH,
]; // x and y axis
The matrix holds the scale in the x and y axis so that the two numbers for x axis tell us the direction and length of a transformed unit.
Simplify function
And redo the getLoc function for speed and efficiency
function transformPoint(point,matrix,result){
if(result === undefined){
result = {};
}
// now move along the projection x axis
result.x = point.x * matrix[0] + point.y * matrix[2];
result.y = point.x * matrix[1] + point.y * matrix[3];
return result;
}
So pass a point and get a transformed point back. The result argument allows you to pass an existing point and that saves having to allocate a new point if you are doing it often.
var point = {x : 2, y : 1};
var screen = transformPoint(point,dimetricMatrix);
// result is the screen location of the block
// next time
screen = transformPoint(point,dimetricMatrix,screen); // pass the screen obj
// to avoid those too
// GC hits that kill
// game frame rates
Inverting the Matrix
All that is handy but you need the reverse of what we just did. Luckily the way matrices work allows us to reverse the process by inverting the matrix.
function invertMatrix(matrix){
var m = matrix; // shortcut to make code readable
var rm = [0,0,0,0]; // resulting matrix
// get the cross product of the x and y axis. It is the area of the rectangle made by the
// two axis
var cross = m[0] * m[3] - m[1] * m[2]; // I call it the cross but most will call
// it the determinate (I think that cross
// product is more suited to geometry while
// determinate is for maths geeks)
rm[0] = m[3] / cross; // invert both axis and unscale (if cross is 1 then nothing)
rm[1] = -m[1] / cross;
rm[2] = -m[2] / cross;
rm[3] = m[0] / cross;
return rm;
}
Now we can invert our matrix
var dimetricMatrixInv = invertMatrix(dimetricMatrix); // get the invers
And now that we have the inverse matrix we can use the transform function to convert from a screen location to a block location
var screen = {x : 100, y : 200};
var blockLoc = transformPoint(screen, dimetricMatrixInv );
// result is the location of the block
The Matrix for rendering
For a bit of magic the transformation matrix dimetricMatrix can also be used by the 2D canvas, but you need to add the origin.
var m = dimetricMatrix;
ctx.setTransform(m[0], m[1], m[2], m[3], 0, 0); // assume origin at 0,0
Now you can draw a box around the block with
ctx.strokeRect(2,1,1,1); // 3rd by 2nd block 1 by 1 block wide.
The origin
I have left out the origin in all the above, I will leave that up to you to find as there is a trillion pages online about matrices as all 2D and 3D rendering use them and getting a good deep knowledge of them is important if you wish to get into computer visualization.
I want to draw vertical lines at points on the x axis in dygraphs.
They should:
Span the entire y axis automatically, but not prevent other series from being automatically zoomed in on the y axis when manually zoomed to a range of the x axis
Be completely vertical
have no horizontal lines joining them
In that sense, a lot like having multiple extra axis lines drawn at random points.
But it would be helpful if they have the same features of regular series plots in that when it is hovered over, a label displays its value along the x axis.
In case it matters, they will be plotted on the same graph as other series lines, and may or may not have common x axis values with those series.
How might it be achieved?
A way to draw vertical lines, is to use the underlayCallback. It allows you to draw the background of the graph.
Here is a sample that draw 3 vertical lines at X=100, X=200 and X=500 :
<script src="https://cdnjs.cloudflare.com/ajax/libs/dygraph/1.1.1/dygraph-combined.js"></script>
<div id="div_g" style="width:400px; height:200px;"></div>
<script>
// A basic sinusoidal data series.
var data = [];
for (var i = 0; i < 1000; i++) {
var base = 10 * Math.sin(i / 90.0);
data.push([i, base]);
}
new Dygraph(
document.getElementById("div_g"),
data, {
labels: ['X', 'Y'],
underlayCallback: function(canvas, area, g) {
canvas.strokeStyle = 'red';
var lines = [100, 200, 500];
for (var idx = 0; idx < lines.length; idx++) {
var canvasx = g.toDomXCoord(lines[idx]);
var range = g.yAxisRange();
canvas.beginPath();
canvas.moveTo(canvasx, g.toDomYCoord(range[0]));
canvas.lineTo(canvasx, g.toDomYCoord(range[1]));
canvas.stroke();
canvas.closePath();
}
}
}
);
</script>
I am working on an optimization problem in javascript.
I have two arrays:
dataset1 = [[x1,y1],[x2,y2],...,[xn,yn]]
dataset2 = [[z1,w1],[z2,w2],...,[zm,wm]]
dataset1 and dataset2 represent monotonic functions
x1 < x2 < ... < xn (x coordinate)
z1 < z2 < ... < zm (x coordinate)
and
y1 < y2 < ... < yn (y coordinate)
w1 > w2 > ... > wm (y coordinate)
Therefore, the first coordinate of both datasets always increases
The second coordinate of dataset1 always increases and the second coordinate of dataset2 always decreases.
I am looking for a fast binary iterative algorithm to find the two closest pair of coordinates (intersection of the two monotonic functions).
Does anyone know how to do it?
as I understand, you have two monotonic functions, one that increases to the right, and one that decreases to the right.
Your arrays are sorted points, thus beginning at the most inferior x coordinate for each one.
What you could do is compare each point of the first array to each point of the second array, and store an object like so:
//point
{
pointIncreasing: [xj, yj],
pointDecreasing: [zi, wi],
distance : //distance between the points
}
And then to calculate the distance you check:
Whether the two share the same x or y coordinate, in which case the distance is Math.abs(xj-zi) or Math.abs(yj-wi).
If they don't share an x or y coordinate you can use the theorem of Pythagoras to find the distance as d = Math.sqrt(Math.pow(Math.abs(xj-zi),2) + Math.pow(Math.abs(yj-wi),2))
So at the end you would get a function like so:
var results = [];
function populateResults(fun1, fun2){
for (var j = 0; j < fun1.length; j++){
for (var i = 0; i< fun2.length; i++){
var temp = {
pointI : fun1[j],
pointD : fun2[i],
d : null
}
// do as said before, add some ifs, calculate distance
// and do a temp.d = calculatedDistance
// and add an if calculatedDistance == 0, to stop the
// loop, because you got your intersection
results.push(temp);
}
}
results.sort(function(a, b){return (a.d - b.d);});
// to sort your results array, not sure of order though...
}
And then you just need to take results[0] and you've got your two closest points, or intersection if distance == 0.
Hope this helps!
I have a Bezier curve: (0,0), (.25,.1), (.25,1), and (1,1).
This is graphically seen here: http://cubic-bezier.com/#.25,.1,.25,1
We see on the x axis is time.
This is my unknown. This is a unit cell. So I was wondering how can I get x when y is 0.5?
Thanks
I saw this topic: y coordinate for a given x cubic bezier
But it loops, I need to avoid something loops
So I found this topic: Cubic bezier curves - get Y for given X
But I can't figure out how to solve a cubic polynomial in js :(
This is mathematically impossible unless you can guarantee that there will only be one y value per x value, which even on a unit rectangle you can't (for instance, {0,0},{1,0.6},{0,0.4},{1,1} will be rather interesting at the mid point!). The fastest is to simply build a LUT, like for instance:
var LUT_x = [], LUT_y = [], t, a, b, c, d;
for(let i=0; i<100; i++) {
t = i/100;
a = (1-t)*(1-t)*(1-t);
b = (1-t)*(1-t)*t;
c = (1-t)*t*t;
d = t*t*t;
LUT_x.push( a*x1 + 3*b*x2 + 3*c*x3 + d*x4 );
LUT_y.push( a*y1 + 3*b*y2 + 3*c*y3 + d*y4 );
}
Done, now if you want to look up an x value for some y value, just run through LUT_y until you find your y value, or more realistically until you find two values at index i and i+1 such that your y value lies somewhere in between them, and you will immediately know the corresponding x value because it'll be at the same index in LUT_x.
For nonexact matches with 2 indices i and i+1 you simply do a linear interpolation (i.e. y is at distance ... between i and i+1, and this at the same distance between i and i+1 for the x coordinates)
All the solutions that use a look up table can only give you an approximate result. If that is good enough for you, you are set. If you want a more accurate result, then you need to use some sort of numeric method.
For a general Bezier curve of degree N, you do need to loop. Meaning, you need to use bi-section method or Newton Raphson method or something similar to find the x value corresponding to a given y value and such methods (almost) always involve iterations starting with an initial guess. If there are mutiple solutions, then what x value you get will depend on your initial guess.
However, if you only care about cubic Bezier curves, then analytic solution is possible as roots of cubic polynomials can be found using the Cardano formula. In this link (y coordinate for a given x cubic bezier), which was referenced in the OP, there is an answer by Dave Bakker that shows how to solve cubic polynomial using Cardano formula. Source codes in Javascript is provided. I think this will be your good source to start your investigation on.
Thanks again to Mike's help we found the fastest way to do this. I put this function togather, takes 0.28msg on average:
function getValOnCubicBezier_givenXorY(options) {
/*
options = {
cubicBezier: {xs:[x1, x2, x3, x4], ys:[y1, y2, y3, y4]};
x: NUMBER //this is the known x, if provide this must not provide y, a number for x will be returned
y: NUMBER //this is the known y, if provide this must not provide x, a number for y will be returned
}
*/
if ('x' in options && 'y' in options) {
throw new Error('cannot provide known x and known y');
}
if (!('x' in options) && !('y' in options)) {
throw new Error('must provide EITHER a known x OR a known y');
}
var x1 = options.cubicBezier.xs[0];
var x2 = options.cubicBezier.xs[1];
var x3 = options.cubicBezier.xs[2];
var x4 = options.cubicBezier.xs[3];
var y1 = options.cubicBezier.ys[0];
var y2 = options.cubicBezier.ys[1];
var y3 = options.cubicBezier.ys[2];
var y4 = options.cubicBezier.ys[3];
var LUT = {
x: [],
y: []
}
for(var i=0; i<100; i++) {
var t = i/100;
LUT.x.push( (1-t)*(1-t)*(1-t)*x1 + 3*(1-t)*(1-t)*t*x2 + 3*(1-t)*t*t*x3 + t*t*t*x4 );
LUT.y.push( (1-t)*(1-t)*(1-t)*y1 + 3*(1-t)*(1-t)*t*y2 + 3*(1-t)*t*t*y3 + t*t*t*y4 );
}
if ('x' in options) {
var knw = 'x'; //known
var unk = 'y'; //unknown
} else {
var knw = 'y'; //known
var unk = 'x'; //unknown
}
for (var i=1; i<100; i++) {
if (options[knw] >= LUT[knw][i] && options[knw] <= LUT[knw][i+1]) {
var linearInterpolationValue = options[knw] - LUT[knw][i];
return LUT[unk][i] + linearInterpolationValue;
}
}
}
var ease = { //cubic-bezier(0.25, 0.1, 0.25, 1.0)
xs: [0, .25, .25, 1],
ys: [0, .1, 1, 1]
};
var linear = {
xs: [0, 0, 1, 1],
ys: [0, 0, 1, 1]
};
//console.time('calc');
var x = getValOnCubicBezier_givenXorY({y:.5, cubicBezier:linear});
//console.timeEnd('calc');
//console.log('x:', x);
i want to make a little photoshop javascript. Technically, i just need to know how to compare the color values of pixels af if they were an array with three integer values in each, for example: (pseudocode)
for all pixels x
for all pixels y
if left pixel's green channel is bigger than red channel:
set the blue channel to 25
else
if the blue channel is greater than 50
set the green channel to 0
in the documentation, there's a ton of things like filters, text and layers you can do, but how do you do something as simple as this?
Reading and writing pixel values in Photoshop scripts is indeed not as simple as it could be ... Check out the following script which inverts the blue channel of an image:
var doc = app.open(new File("~/Desktop/test1.bmp"));
var sampler = doc.colorSamplers.add([0, 0]);
for (var x = 0; x < doc.width; ++x) {
for (var y = 0; y < doc.height; ++y) {
sampler.move([x, y]);
var color = sampler.color;
var region = [
[x, y],
[x + 1, y],
[x + 1, y + 1],
[x, y + 1],
[x, y]
];
var newColor = new SolidColor();
newColor.rgb.red = color.rgb.red;
newColor.rgb.green = 255 - color.rgb.green;
newColor.rgb.blue = color.rgb.blue;
doc.selection.select(region);
doc.selection.fill(newColor);
}
}
I'm not sure if there's a prettier way of setting a pixel color than the select + fill trick.
This script runs super slow, so maybe Photoshop scripts are not the best tool for pixel manipulation ...