Correct Wheel Data XY Position - javascript

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.

Related

Is HTML Canvas quadraticCurveTo() not exact?

Edit: I just changed my Control Point to the Intersection. That's why it couldn't fit anymore.
I know it's a very presumptuous. But I am working on a Web-Application and I need to calculate the intersection with a quadratic Beziere-Curve and a Line.
Linear Bezier-Curve: P=s(W-V)+V
Quadratic Bezier-Curve: P=t²(A-2B+C)+t(-2A+2B)+A
Because W, V, A, B, and C are points, I could make two equation. I rearranged the first equation to seperate s to solve the equation.
I'm pretty sure i did it correctly, but my intersection was not on the line. So i was wondering and made my own quadratic-Beziercurve by the correct formular and my intersection hits this curve. Now I am wondering what did I wrong?
That is my function:
intersectsWithLineAtT(lineStartPoint, lineEndPoint)
{
let result = []
let A = this.startPoint, B = this.controlPoint, C = this.endPoint, V = lineStartPoint, W = lineEndPoint
if (!Common.isLineIntersectingLine(A, B, V, W)
&& !Common.isLineIntersectingLine(B, C, V, W)
&& !Common.isLineIntersectingLine(A, C, V, W))
return null
let alpha = Point.add(Point.subtract(A, Point.multiply(B, 2)), C)
let beta = Point.add(Point.multiply(A, -2), Point.multiply(B, 2))
let gamma = A
let delta = V
let epsilon = Point.subtract(W, V)
let a = alpha.x * (epsilon.y / epsilon.x) - alpha.y
let b = beta.x * (epsilon.y / epsilon.x) - beta.y
let c = (gamma.x - delta.x) * (epsilon.y / epsilon.x) - gamma.y + delta.y
let underSquareRoot = b * b - 4 * a * c
if (Common.compareFloats(0, underSquareRoot))
result.push(-b / 2 * a)
else if (underSquareRoot > 0)
{
result.push((-b + Math.sqrt(underSquareRoot)) / (2 * a))
result.push((-b - Math.sqrt(underSquareRoot)) / (2 * a))
}
result = result.filter((t) =>
{
return (t >= 0 && t <= 1)
})
return result.length > 0 ? result : null
}
I hope someone can help me.
Lena
The curve/line intersection problem is the same as the root finding problem, after we rotate all coordinates such that the line ends up on top of the x-axis:
which involves a quick trick involving atan2:
const d = W.minus(V);
const angle = -atan2(d.y, d.x);
const rotated = [A,B,C].map(p => p.minus(V).rotate(angle));
Assuming you're working with point classes that understand vector operations. If not, easy enough to do with standard {x, y} objects:
const rotated = [A,B,C].map(p => {
p.x -= V.x;
p.y -= V.y;
return {
x: p.x * cos(a) - p.y * sin(a),
y: p.x * sin(a) + p.y * cos(a)
};
});
Then all we need to find out is which t values yield y=0, which is (as you also used) just applying the quadratic formula. And we don't need to bother with collapsing dimensions: we've reduced the problem to finding solutions in just the y dimension, so taking
const a = rotated[0].y;
const b = rotated[1].y;
const c = rotated[2].y;
and combining that with the fact that we know that Py = t²(a-b+c)+t(-2a+2b)+a we just work out that t = -b/2a +/- sqrt(b² - 4ac))/2a with the usual checks for negative, zero, and positive discriminant, as well as checking for division by zero.
This gives us with zero or more t value(s) for the y=0 intercept in our rotated case, and for the intersection between our curve and line in the unrotated case. No additional calculations required. Aside from "evaluating B(t) to get the actual (x,y) cooordinates", of course.

Calculate Wheel Data XY Position

I tried to create a lucky draw wheel using reactjs, first, I need to place all the input data to a certain XY position. Below is the expected output XY position example what I need.
var renderData = ["1","2","3","4","5","6","7","8","9","10","11","12"];
React.createElement('div', { className: '_data'},
renderData.map((index,item)=>{
var itemPosition = index / renderData.length * 360;
var itemX = itemPosition * Math.PI/180;
var itemY = itemPosition * Math.PI/180;
return React.createElement('div', { className: '_items',
style:{top:itemX,left:itemY}
},item);
})
)
So I use createElement to create div for each of the data, then using top and left for XY position.
How to calculate the XY position for each div
Update
After tried the #keikai answer
var renderData = ["1","2","3","4","5","6","7","8","9","10","11","12"];
const r = 350;
const len = renderData.length;
const radiusList = renderData.map(x => (360 / len) * (x - 1));
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:`${positionPairList[index].x.toFixed(2)}px`,left:`${positionPairList[index].y.toFixed(2)}px`}
},item);
})
)
all data are rotated 0deg
child div still not place to the right position inside parent div
for clockwise, it starts from 10?
Update: rotate display with clock styles
import React from "react";
import ReactDOM from "react-dom";
import "./styles.css";
function App() {
const n = 12;
const r = 500;
const radiusList = Array.from(Array(n).keys()).map(x => (360 / n) * x);
const positionPairList = radiusList.map(item => ({
x: Math.sin((Math.PI * item) / 180) * r,
y: Math.cos((Math.PI * item) / 180) * r
}));
return (
<div className="App">
{positionPairList.map((item, index) => {
const offset = index === 0 ? n : 0;
return (
<div
className="Parts"
style={{
top: `${r - item.y.toFixed(0)}px`,
right: `${r + 200 - item.x.toFixed(0)}px`,
transform: `rotate(${radiusList[index]}deg)`
}}
>
{index + offset}
</div>
);
})}
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

Find randomly placed element (x,y) inside a grid

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)

How can I simplify my game enemy spawning algorithm? (code snippet included)

I have a hero character in the middle of the screen and I want to spawn zombies all around him in random positions but some distance away from him. heroDistance defines this distance.
It does not matter if they are pushed outside the boundaries of the screen when they are spawned, they all come towards him. It would not matter if this did not happen, but it just seemed easier.
At the moment the random location of the zombie is created for the x axis with random(screenWidth) and y axis random(screenHeight), and those values are fed into the spawnLocation function that depending on where they are in relation to the hero are either increased or decreased to move they away.
My code seems far too verbose, even though I have worked really hard on it. Am I missing some obvious technique to make it simpler?
const state = {
options: {
numberOfZombies: 10,
},
characters: {
hero: {
xPosition: 150,
yPosition: 150,
},
},
};
const screenWidth = 400;
const screenHeight = 400;
const random = range => Math.floor(Math.random() * range);
function createZombies(state) {
const heroDistance = 10;
const spawnLocation = (zomPos, heroPos, axisLength) => {
return zomPos > heroPos
? zomPos + axisLength / heroDistance
: zomPos - axisLength / heroDistance;
};
for (let index = 0; index < state.options.numberOfZombies; index += 1) {
console.log({
xPosition: spawnLocation(
random(screenWidth),
state.characters.hero.xPosition,
screenWidth,
),
yPosition: spawnLocation(
random(screenHeight),
state.characters.hero.yPosition,
screenHeight,
),
});
}
}
createZombies(state);
Generate a random angle and radius, and then transform these values into Cartesian coordinates.
let theta = Math.random() * (2 * Math.PI)
let r = Math.random() * variationInR + minimumR
let zombieX = Math.cos(theta) * r + heroX
let zombieY = Math.sin(theta) * r + heroY
If you want these to be integers, then make them so. This generates zombies uniformly radially from the hero at least minimumR units away (Pythagorean distance). If you want to maintain the Manhattan distance behavior, then generate your dX and dY and add them to the hero's position.

Traverse a Hexagon

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)));

Categories