File
Constructor
constructor(x: number, y: number)
|
export class Point {
constructor(public x: number, public y: number) {}
}
export class Position {
public left: string;
public top: string;
public point: Point;
constructor(point: Point) {
this.point = point;
this.left = point.x + "%";
this.top = point.y + "%";
}
}
// Builder of array of positions in percentages
// With a n-size collection it can generate n-size array of {top, left} positions
// You can then use the array to distribute the elements of the collection with the 'position: absolute' css attribute
export class BuilderDistributionCollection {
public static generatePositionsOverGrid(collectionLength: number) {
let point: Point;
let pos: Position;
const positions: Position[] = [];
// We want a never full grid
const gridWidth = Math.floor(Math.sqrt(collectionLength) + 1);
const m = gridWidth ** 2;
const paddingBetweenElements = 100 / gridWidth;
// Get range of the possible positions in the flatten grid
const positionsArray = Array.from(Array(m).keys());
// For each place drawn, generate the pos
const i = 0;
let place, placeIndex: number;
let topPosPercentage, leftPosPercentage: number;
for (let i = 0; i < collectionLength; i++) {
placeIndex = Math.floor(Math.random() * (m - i));
place = positionsArray[placeIndex];
positionsArray.splice(placeIndex, 1);
// Ex : With a 4x4 grid & place_drawn=11 we have pos = 2,3 (2*4+3)
// And transform the place into top/left percentage
topPosPercentage = Math.floor(place / gridWidth) * paddingBetweenElements;
leftPosPercentage = (place % gridWidth) * paddingBetweenElements;
point = new Point(leftPosPercentage, topPosPercentage);
pos = new Position(point);
positions.push(pos);
}
return positions;
}
// Generate an array of positions (ie percentages {top:50%, left:25%})
// Positions are uniform over a circle
public static generatePositionsOverCircle(collectionLength: number, randomize = true) {
let point: Point;
let pos: Position;
const positions: Position[] = [];
const radius = 50;
const padding = (2 * Math.PI) / collectionLength;
let angle = Math.random() * 2 * Math.PI; // Radians
let x, y: number; // Euclidean coordinates
for (let i = 0; i < collectionLength; i++) {
// With init=30 and padding=25 we have angles=30,55,80... (here in 'deg' but below it is 'rad')
angle = (angle + padding) % (2 * Math.PI);
// Transforms with polar coordinates
point = new Point(Math.floor(radius * Math.sin(angle)), Math.floor(radius * Math.cos(angle)));
point.x += radius;
point.y += radius;
pos = new Position(point);
positions.push(pos);
}
if (randomize) {
this.shuffle(positions);
}
return positions;
}
// Shuffle/randomize a collection (inplace)
public static shuffle(collection: Array<any>) {
let m: number = collection.length,
t: any,
i: number;
while (m) {
i = Math.floor(Math.random() * m--);
t = collection[m];
collection[m] = collection[i];
collection[i] = t;
}
}
// Generate an array of positions, distributed over a universe
// Src : yahiko from https://www.developpez.net/forums/d1470523/applications/developpement-2d-3d-jeux/generation-aleatoire-d-univers/
public static generatePositionsOverUniverse(collectionLength: number, epsilon: number = 0.02) {
let universe: Position[] = [];
let iteratorCount = 0;
let i = 0;
let point: Point;
while (i < collectionLength) {
point = new Point(0.05 + 0.95 * Math.random() * 100, 0.05 + 0.95 * Math.random() * 100);
if (isValidLocation(point, universe)) {
universe.push(new Position(point));
i++;
}
iteratorCount++;
if(iteratorCount % 100 === 0){
epsilon *= 0.95;
}
}
function isValidLocation(point: Point, universe: Position[]): boolean {
for (let i = 0; i < universe.length; i++) {
let pos = universe[i];
if (distance(point.x, point.y, pos.point.x, pos.point.y) < epsilon) {
return false;
} else if (distance(point.x, point.y, pos.point.x, pos.point.y) < 0) {
return true;
}
}
return true;
}
function distance(x1: number, y1: number, x2: number, y2: number): number {
return Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
}
return universe;
}
}