Given a grid of size 1000, find the x and y coords of a randomly placed element.
I've tried subdividing the grid into four sections, but I also have to make the solution time-complexity efficient.
const GRID_SIZE = 1000
class RandomElement {
constructor() {
const element = {
x: Math.floor(Math.random() * GRID_SIZE),
y: Math.floor(Math.random() * GRID_SIZE)
}
this._element = element
}
findInArea(x1, y1, x2, y2) {
console.log(`Scanning area (${x1}, ${y1}, ${x2}, ${y2})`)
return (
this._element.x >= x1 &&
this._element.y >= y1 &&
this._element.x < x2 &&
this._element.y < y2
)
}
findInCell(x, y) {
console.log(`Scanning cell (${x}, ${y}`)
return this._element.x === x && this._element.y === y
}
}
const RandomElement = new RandomElement()
const iselementHere1 = RandomElement.findInArea(0, GRID_SIZE, 0, GRID_SIZE)
console.log('Is element Here?', iselementHere1)
const iselementHere2 = RandomElement.findInArea(0, GRID_SIZE / 2, GRID_SIZE / 2, GRID_SIZE)
console.log('Is element Here?', iselementHere2)
const iselementHere3 = RandomElement.findInArea(GRID_SIZE / 2, 0, GRID_SIZE, GRID_SIZE / 2)
console.log('Is element Here?', iselementHere3)
const iselementHere4 = RandomElement.findInArea(GRID_SIZE / 2, GRID_SIZE / 2, GRID_SIZE, GRID_SIZE)
console.log('Is element Here?', iselementHere4)
Expressions
xx = Math.floor(this._element.x / (GRID_SIZE / 2))
yy = Math.floor(this._element.y / (GRID_SIZE / 2))
give you cell coordinates in 2x2 grid.
You can combine both in single parameter
cellYX = xx + 2 * yy
to get result 0..3 as cell number
0 | 1
-------
2 | 3
Convert your 2d array into another 2d array where every index holds {value, x, y}. Now sort it row / column wise. Then every element search will take rlogc (sorted column-wise) / clogr (sorted row-wise) and just output the (x,y) of that index (it's not the actual co-ordinates, but the co-ordinates of the given 2d-array)
Related
I have co-ordinates for the points by taking which I draw a polygon. I can add points dynamically on the edges of the polygon and when I drag any point it should drag only the connected lines. As points can be added later on the edges so the point co-ordinates need to be ordered/sorted and the polygon should be redrawn by taking the ordered/sorted points so that on dragging any point the lines connected to the dragged point only should be dragged/updated. So to order/sort the points I am sorting the co-ordinates(2D-points) clockwise using Graham Scan/ sorting by polar angle.
My sorting code is
I find the center of the polygon like
function findCenter(points) {
let x = 0,
y = 0,
i,
len = points.length;
for (i = 0; i < len; i++) {
x += Number(points[i][0]);
y += Number(points[i][1]);
}
return { x: x / len, y: y / len }; // return average position
}
Then I sort the points by finding angles of each point from the center like
function findAngle(points) {
const center = findCenter(points);
// find angle
points.forEach((point) => {
point.angle = Math.atan2(point[1] - center.y, point[0] - center.x);
});
}
//arrVertexes is the array of points
arrVertexes.sort(function (a, b) {
return a.angle >= b.angle ? 1 : -1;
});
But the problem I am facing is if I drag any point more towards opposite side and add a new point on the edges afterward and drag the newly added point the sorting of co-ordinates is not ordered exactly because of which there is a flickering while dragging.
Here is a pictorial view of the problem I am facing for quick understanding.
Initially my svg looks like
After this I add a point and dragged like
Then I added one more point like
once I drag the added point towards down, it redraws the polygon something like (is not it weird ?)
Actually It should be like
NOTE: I really don't know what logic should I apply to get the desire functionality. Seeking help from the community leads.
Demo App
So I am looking for a solution that won't give me weird redrawing of the lines. Only the connected lines to the dragged point should be dragged.
EDIT
I came up with MUCH BETTER solution. The only problem with this approach is, When I try to add a new point on left-vertical line and If I try to move it, that newly added point moves to top-horizontal line
Updated-Demo
I've fixed this bug with left line. Take a look: codepen.
I changed getClosestPointOnLines function (actually refactored a little):
as I understood, the result here is to get i - the index for the new point in array, so I moved the algorithm to new function getI
I changed getI to use not only n (current index), but just 2 any indexes: n1 and n2: const getI = (n1, n2) => {
So all your aXys[n] is now a1 and aXys[n - 1] is now a2.
the result of getI is return i; - this is what we want from this function
I added new function-helper updateI. It calls getI and check if there any positive result.
const updateI = (n1, n2) => {
const newI = getI(n1, n2);
if (newI !== undefined) {
i = newI;
return true;
}
};
So your loop over points is now:
for (let n = 1; n < aXys.length; n++) {
updateI(n, n - 1);
}
But we need to check "left" line separately (because it connects begin and end of the array):
if (updateI(aXys.length - 1, 0)) i = aXys.length;
Sorry, but I disabled part of your code. I did not check where do you use it:
if (i < aXys.length) {
let dx = aXys[i - 1][0] - aXys[i][0];
let dy = aXys[i - 1][1] - aXys[i][1];
x = aXys[i - 1][0] - dx * fTo;
y = aXys[i - 1][1] - dy * fTo;
}
So the final version of getClosestPointOnLines now looks like this:
function getClosestPointOnLines(pXy, aXys) {
var minDist;
var fTo;
var fFrom;
var x;
var y;
var i;
var dist;
if (aXys.length > 1) {
const getI = (n1, n2) => {
let i;
const a1 = aXys[n1];
const a2 = aXys[n2];
if (a1[0] != a2[0]) {
let a = (a1[1] - a2[1]) / (a1[0] - a2[0]);
let b = a1[1] - a * a1[0];
dist = Math.abs(a * pXy[0] + b - pXy[1]) / Math.sqrt(a * a + 1);
} else dist = Math.abs(pXy[0] - a1[0]);
// length^2 of line segment
let rl2 = Math.pow(a1[1] - a2[1], 2) + Math.pow(a1[0] - a2[0], 2);
// distance^2 of pt to end line segment
let ln2 = Math.pow(a1[1] - pXy[1], 2) + Math.pow(a1[0] - pXy[0], 2);
// distance^2 of pt to begin line segment
let lnm12 = Math.pow(a2[1] - pXy[1], 2) + Math.pow(a2[0] - pXy[0], 2);
// minimum distance^2 of pt to infinite line
let dist2 = Math.pow(dist, 2);
// calculated length^2 of line segment
let calcrl2 = ln2 - dist2 + lnm12 - dist2;
// redefine minimum distance to line segment (not infinite line) if necessary
if (calcrl2 > rl2) dist = Math.sqrt(Math.min(ln2, lnm12));
if (minDist == null || minDist > dist) {
if (calcrl2 > rl2) {
if (lnm12 < ln2) {
fTo = 0; //nearer to previous point
fFrom = 1;
} else {
fFrom = 0; //nearer to current point
fTo = 1;
}
} else {
// perpendicular from point intersects line segment
fTo = Math.sqrt(lnm12 - dist2) / Math.sqrt(rl2);
fFrom = Math.sqrt(ln2 - dist2) / Math.sqrt(rl2);
}
minDist = dist;
i = n1;
}
return i;
};
const updateI = (n1, n2) => {
const newI = getI(n1, n2);
if (newI !== undefined) {
i = newI;
return true;
}
};
for (let n = 1; n < aXys.length; n++) {
updateI(n, n - 1);
}
if (updateI(aXys.length - 1, 0)) i = aXys.length;
if (i < aXys.length) {
let dx = aXys[i - 1][0] - aXys[i][0];
let dy = aXys[i - 1][1] - aXys[i][1];
x = aXys[i - 1][0] - dx * fTo;
y = aXys[i - 1][1] - dy * fTo;
}
}
console.log(aXys[i - 1]);
return { x: x, y: y, i: i, fTo: fTo, fFrom: fFrom };
}
Working example on codepen.
You should not allow any point to be added that is not close to a line.
When the user clicks, use the distance from a point to a line algorithm to check each line to see if the click is within an acceptable distance of the line. Perhaps a few pixels. If more than one line is within an acceptable distance, perhaps choose the one that is closest.
You now know where in the array to insert the new point. It will be between the first and second points of the line that just matched.
If you do that, the shape drawing should just work.
From the last question I asked Calculate Wheel Data XY Position , I have get the X,Y and Rotation for the div child.
Let say, the parent div width is 414px , so I divide by 2 is 207 for const r
var renderData = ["1","2","3","4","5","6","7","8","9","10","11","12"];
const r = 207;
const len = renderData.length;
const radiusList = Array.from(Array(len).keys()).map(x => (360 / len) * x);
const positionPairList = radiusList.map(x => ({
x: Math.sin((Math.PI * x) / 180) * r,
y: Math.cos((Math.PI * x) / 180) * r
}));
React.createElement('div', { className: '_data'},
renderData.map((item, index) => {
return React.createElement('div', { className: `_items`,
style:{
top:`${r - positionPairList[index].y.toFixed(0)}px`,
right:`${r - positionPairList[index].x.toFixed(0)}px`,
transform: `rotate(${radiusList[index]}deg)`},
},item);
})
)
Below is the output result
The child X and Y still no position in the right place, note that I cannot manually add the top or right position value, the value must use calculation.
Suppose one attempts to plot the complex valued function $f:\mathhbb{C} \to \mathhbb{C}$ as $f(z) =z$ in jsx graph. It may not be complicated as it appears. What one needs is two connected planes. The point (x, y) in domain planr gets mapped to the point (x, y) in codomain plane. As one drags point in domain plane, corresponding changes takes place in the point in co domain plane. So the only question is how to connect two planes. It is matter of 2 dimensions only. If something similar to the following can be added to jsx graph, it would be great addition to jsx graph. Many properties of complex valued function can then be studied.
Here is the link.
http://www.jimrolf.com/java/complexTool/bookComplexTool.html
Two boards board1, board2 can be connected with board1.addChild(board2). This means, every update in board1 triggers an update in board2.
Here is a basic example, see https://jsfiddle.net/zfbrsdwh/ :
const board1 = JXG.JSXGraph.initBoard('jxgbox1', {
boundingbox: [-5, 5, 5, -5], axis:true
});
var p = board1.create('point', [1,2], {name:'Drag me'});
const board2 = JXG.JSXGraph.initBoard('jxgbox2', {
boundingbox: [-5, 5, 5, -5], axis:true
});
var q = board2.create('point', [function() { return [-p.Y(), p.X()]; }],
{name:'image'});
board1.addChild(board2);
Update in reply to the first comment: Visualizing conformal maps in the complex plane can be done by applying the map to a quadrangle. It is necessary to define the edges of the quadrangle by a curve:
var p0 = board1.create('point', [2, -2]);
var p1 = board1.create('point', [2, 2]);
var p2 = board1.create('point', [-2, 2]);
var p3 = board1.create('point', [-2, -2]);
// Draw the quadrangle through p0, p1, p2, p3 as curve
// defined by [fx, fy]
var fx = function(x) {
if (x < 0 || x > 4) { return NaN; }
if (x < 1) {
return (p1.X() - p0.X()) * x + p0.X();
} else if (x < 2) {
return (p2.X() - p1.X()) * (x - 1) + p1.X();
} else if (x < 3) {
return (p3.X() - p2.X()) * (x - 2) + p2.X();
} else if (x < 4) {
return (p0.X() - p3.X()) * (x - 3) + p3.X();
}
};
var fy = function(x) {
if (x < 0 || x > 4) { return NaN; }
if (x < 1) {
return (p1.Y() - p0.Y()) * x + p0.Y();
} else if (x < 2) {
return (p2.Y() - p1.Y()) * (x - 1) + p1.Y();
} else if (x < 3) {
return (p3.Y() - p2.Y()) * (x - 2) + p2.Y();
} else if (x < 4) {
return (p0.Y() - p3.Y()) * (x - 3) + p3.Y();
}
};
var graph1 = board1.create('curve', [fx, fy, 0, 4]);
Then it should be easy to define a conformal map and plot the composition of the two maps in the second board:
// Conformal complex map z -> 1/z
var map = function(x, y) {
var s = x*x+y*y;
return [x / s, -y/s];
};
// Draw the image of the quadrangle under the map
f2x = function(x) {
return map(fx(x), fy(x))[0];
};
f2y = function(x) {
return map(fx(x), fy(x))[1];
};
var graph2 = board2.create('curve', [f2x, f2y, 0, 4]);
The full mathlet is at https://jsfiddle.net/Lmy60f4g/2/
I can easily traverse the following from left to right, but I'm having a lot of trouble (2 days in, and no progress) getting a formula to traverse it from top right & bottom right.
Basically, I'm looking for a formula which can retrieve the following values:
let topRight = [
[ h[2][4], h[3][3], h[4][2] ],
[ h[1][3], h[2][3], h[3][2], h[4][1] ],
[ h[0][2], h[1][2], h[2][2], h[3][1], h[4][0] ],
[ h[0][1], h[1][1], h[2][1], h[3][0] ],
[ h[0][0], h[1][0], h[2][0] ]
]
let bottomRight = [
[ h[2][4], h[1][3], h[0][2] ],
[ h[3][3], h[2][3], h[1][2], h[0][1] ],
[ h[4][2], h[3][2], h[2][2], h[1][1], h[0][0] ],
[ h[4][1], h[3][1], h[2][1], h[1][0] ],
[ h[4][0], h[3][0], h[2][0] ]
]
The only part that I could get working was the topRight x value:
function hexagonArraySize(row) {
if (row < this.size) {
return (this.size + row)
} else {
return (this.size * 2 - (row % this.size) - 2)
}
}
for (var i = 0, l = this.size * 2 - 1, j = l % size; i < l; ++i, ++j) {
this.h[j] = new Array(this.hexagonArraySize(i)).fill().map((_, b) => {
let x = (i > Math.floor(this.size / 2)) ? b : b + (this.size - i - 1)
let y = 0 // didn't found the formula for this one...
}, []).join("")
}
I made a fiddle available here: https://jsfiddle.net/0qwf8m1p/12/
There still is topRight y, bottomRight x & y to be found.
Ok, so you worked out topRight x coordinate.
Both topRight and bottomRight y coordinates have the same equation:
You start with (size - 1) * 2
First value always equals (size - 1) * 2 - i) (where i is the sequence index)
Then you repeat first value i times, but at most size times
This wraps up to the following formula:
let y = (size - 1) * 2 - i - Math.max(0, b - Math.min(i, size - 1))
| | |
| first value | repeat at most size times |
Next, you have to calculate x value for bottomRight. You have two cases here:
If i is less than size / 2 then value equals sequence length - b (item index)
Otherwise value equals (size - 1) * 2 - b
This wraps up to the following formula:
let x = (i > Math.floor(size / 2)) ? (size - 1) * 2 - b : hexagonArraySize(i) - b - 1
| | |
| check index | second half | first half
Working fiddle here
Here's a possible implementation for the two methods. They work by initializing the x,y coordinates of the start for each row in the transformed hexagon, then conditionally iterate the x value based on whether or not the y value passed the size of the hexagon, where the indices "bend":
function hexagon (size) {
const height = size * 2 - 1;
return Array.from({ length: height }, (_, y) => {
const width = size + Math.min(y, height - y - 1);
return Array.from({ length: width }, (_, x) => [y, x]);
})
}
const h = hexagon(3);
function topRight (h) {
const height = h.length;
const size = (height + 1) / 2;
const t = [];
for (let i = height; i > 0; i--) {
const width = size + Math.min(i - 1, height - i);
const row = Array.from({ length: width });
let y = Math.max(i - size, 0);
let x = i - 1;
for (let j = 0; j < width; j++) {
row[j] = h[y++][y >= size ? x-- : x];
}
t.push(row);
}
return t;
}
function bottomRight (h) {
const height = h.length;
const size = (height + 1) / 2;
const t = [];
for (let i = 0; i < height; i++) {
const width = size + Math.min(i, height - i - 1);
const row = Array.from({ length: width });
let y = height - Math.max(size - i, 1);
let x = height - i - 1;
for (let j = 0; j < width; j++) {
row[j] = h[y][y-- < size ? x-- : x];
}
t.push(row);
}
return t;
}
console.log('topRight');
topRight(h).forEach(row => console.log(JSON.stringify(row)));
console.log('bottomRight');
bottomRight(h).forEach(row => console.log(JSON.stringify(row)));
If you'd like a more object-oriented approach, here's a possible alternative:
class Hexagon extends Array {
constructor (size, map = (row, column) => [row, column]) {
const length = size * 2 - 1;
super(length);
this.fill();
this.size = size;
this.forEach((_, row, hexagon) => {
const width = size + Math.min(row, length - row - 1);
hexagon[row] = Array.from({ length: width }, (_, column) => map(row, column));
});
}
topRight () {
const { length, size } = this;
return new Hexagon(size, (row, column) => {
const upper = Math.max(row * 2 - length, 0) + 1;
const y = Math.max(size - 1 - row, 0) + column;
const x = Math.max(Math.min(length - 1 - row, length - upper - column), 0);
return this[y][x];
});
}
bottomRight () {
const { length, size } = this;
return new Hexagon(size, (row, column) => {
const upper = Math.max(row * 2 - length, 0) + 1;
const y = Math.min(size + row, length) - 1 - column;
const x = Math.max(Math.min(length - 1 - row, length - upper - column), 0);
return this[y][x];
});
}
}
let h = new Hexagon(3);
console.log('topRight');
h.topRight().forEach(row => console.log(JSON.stringify(row)));
console.log('bottomRight');
h.bottomRight().forEach(row => console.log(JSON.stringify(row)));
I have a 300px * 300px image. I also have some specific pixels every 100px in every direction (to 16 specific pixels in total).
0 1 2 <-- IntervalX
______ ______ ______
0 | | | |
|______|______|______|
1 | | | |
|______|______|______|
2 | | | |
|______|______|______|
^
IntervalY
I want to put every pixels (except the specific ones) in blocks bounded by the specific pixels, but defined by the pixels value in a 1D array, not 2D array.
const gridX = width / (trueX - 1);
const gridY = width / (trueY - 1);
//Loop for every pixel:
intervalX = Math.floor((pixel[inc].x) / gridX);
intervalY = Math.floor((pixel[inc].y) / gridY);
//Implementing formula for transforming from 1D array to 2D array : (y * width + x) = item number
//All leftmost known pixels start from a "zero" value, so we do not need to substract 1
let isNotFirstArray;
if (intervalX == 0)
isNotFirstArray = 0;
else
isNotFirstArray = 1;
p1 = pixel[intervalY * gridY * 299 + intervalX * gridX - isNotFirstArray];
p2 = pixel[intervalY * gridY * 299 + (intervalX + 1) * gridX - 1];
p3 = pixel[(intervalY + 1) * gridY * 299 + intervalX * gridX - isNotFirstArray];
p4 = pixel[(intervalY + 1) * gridY * 299 + (intervalX + 1) * gridX - 1];
pixel[inc].value = Math.round(bilinearInterpolate(p1, p2, p3, p4, j, i));
inc++;
The problem is the values for the specific pixels are not calculated correctly. The Y values are correct, but the X are not.
Later edit:
The error is like this: the coordinates of the specific pixels are correct on the Y axis, but not on the X axis. They are shifted for the inner intervals with 100px*(max intervals - current interval).
If I get that right, you would like to »filter-out« some of the pixels, which appear at some interval in x and y direction?
const img = <image array>,
imgWidth = 600,
gridX = 150,
gridY = 200;
let result = [];
for (let i = 0; i < input.length; i++) {
let x = i % imgWidth,
y = Math.floor(i / imgWidth);
if (!(x % gridX == 0 || y % gridY == 0)) {
result.push(input[i])
}
}
That should filter out all the indexes which are as special as you said. It remains an array of pixels of an image with the a width.