Changing Props in React - javascript

Im trying to make a Hexagon Grid and each of the Hexes to have data props with coordinates x y z and with value. I've managed to make the logic to create the grid and coordinates, but I can't figure out how to connect them. When I try to assign x,y,z props the TypeError pops out, saying I cannot change props in Element. The code:
import { StyledHexagonGrid } from './StyledHexagonGrid.jsx';
import Hexagon from '../Hexagon/Hexagon.jsx';
export const coordinatesCalc = (radius) => {
let coordinateRadius = radius - 1;
let coordinates = [];
for (let x = -coordinateRadius; x < coordinateRadius + 1; x++) {
for (let z = -coordinateRadius; z < coordinateRadius + 1; z++) {
for (let y = -coordinateRadius; y < coordinateRadius + 1; y++) {
let sum = x + y + z;
let coordinate = { x, y, z };
if (sum === 0) {
coordinates.push(coordinate);
}
}
}
}
return coordinates;
};
function HexagonGrid({ dataparentToChild }) {
let passedRadius = Object.values({ dataparentToChild });
let radius = passedRadius[0];
let columns = 2 * radius - 1;
let height = 600 / columns;
let width = height * 1.1547;
let dom_content = [];
for (let i = 0, j = radius - 1; i < columns; i++, j--) {
for (let k = 0; k < columns - Math.abs(j); k++) {
let left = width * 0.75 * i;
let top = k * height + Math.abs(j * (height / 2));
dom_content.push(
<StyledHexagon
style={{
top: `${top}px`,
left: `${left}px`,
width: `${width}px`,
height: `${height}px`,
}}
data-x=''
data-y=''
data-z=''
value=''
/>
);
}
}
console.log(dom_content);
let coordinates = coordinatesCalc(radius);
let hexElements = [];
for(let i=0;i<coordinates.length;i++){
let el = dom_content[i];
el.props.x = coordinates[i].x;
el.props.y = coordinates[i].y;
el.props.z = coordinates[i].z;
hexElements.push(el);
}
console.log(hexElements);
return (
<StyledHexagonGrid>
<Hexagon
dataparentToChild={passedRadius[0]}
/>
</StyledHexagonGrid>
);
}
export default HexagonGrid;

Related

Rendering Indexed Buffer Geometry within React-Three-Fiber

I am currently trying to get the example from https://threejs.org/examples/?q=buffer#webgl_buffergeometry_indexed working within a React-Three-Fiber environment.
There are several issues that I'm currently seeing within my code.
First, specifically in Chrome, I am repeatedly getting the following warning:
[.WebGL-0x26d30384f700] GL_INVALID_ENUM: Enum is not currently supported.
In addition, I cannot currently see any color being applied to the mesh.
Here is the code that I have working so far:
import React, { useMemo } from "react";
import { Canvas } from "#react-three/fiber";
import { DoubleSide } from "three";
const App = () => {
const size = 20;
const segments = 10;
const [colors, normals, positions] = useMemo(() => {
const colorsArr = [];
const normalsArr = [];
const positionsArr = [];
const halfSize = size / 2;
const segmentSize = size / segments;
// generate vertices, normals and color data for a simple grid geometry
for (let i = 0; i <= segments; i++) {
const y = i * segmentSize - halfSize;
for (let j = 0; j <= segments; j++) {
const x = j * segmentSize - halfSize;
positionsArr.push(x, -y, 0);
normalsArr.push(0, 0, 1);
const r = x / size + 0.5;
const g = y / size + 0.5;
colorsArr.push(r, g, 1);
}
}
return [colorsArr, normalsArr, positionsArr];
}, []);
const indices = useMemo(() => {
const indicesArr = [];
// generate indices (data for element array buffer)
for (let i = 0; i < segments; i++) {
for (let j = 0; j < segments; j++) {
const a = i * (segments + 1) + (j + 1);
const b = i * (segments + 1) + j;
const c = (i + 1) * (segments + 1) + j;
const d = (i + 1) * (segments + 1) + (j + 1);
// generate two faces (triangles) per iteration
indicesArr.push(a, b, d); // face one
indicesArr.push(b, c, d); // face two
}
}
return indicesArr;
}, []);
return (
<Canvas
camera={{
fov: 27,
near: 1,
far: 3500
}}
position-z={64}
>
<color attach="background" args={["#050505"]} />
<mesh>
<bufferGeometry attach="geometry">
<bufferAttribute
array={indices}
attach="index"
count={indices.length}
itemSize={1}
/>
<bufferAttribute
attachObject={["attributes", "position"]}
count={positions.length / 3}
array={positions}
itemSize={3}
/>
<bufferAttribute
attachObject={["attributes", "color"]}
count={colors.length / 3}
array={colors}
itemSize={3}
/>
<bufferAttribute
attachObject={["attributes", "normal"]}
count={normals.length / 3}
array={normals}
itemSize={3}
/>
</bufferGeometry>
<meshPhongMaterial attach="material" side={DoubleSide} vertexColors />
</mesh>
<hemisphereLight />
</Canvas>
);
};
export default App;
Sample Code from Three.js: https://github.com/mrdoob/three.js/blob/master/examples/webgl_buffergeometry_indexed.html
Got this issue fixed finally. The main issue was that I needed to convert the position, color, and normals array to a Float32Array() and the index to a Uint32Array().
So for example, in the case of the index array, the following worked for me:
const indices = useMemo(() => {
const indicesArr = [];
// generate indices (data for element array buffer)
for (let i = 0; i < segments; i++) {
for (let j = 0; j < segments; j++) {
const a = i * (segments + 1) + (j + 1);
const b = i * (segments + 1) + j;
const c = (i + 1) * (segments + 1) + j;
const d = (i + 1) * (segments + 1) + (j + 1);
// generate two faces (triangles) per iteration
indicesArr.push(a, b, d); // face one
indicesArr.push(b, c, d); // face two
}
}
return new Uint32Array(indicesArr);
}, []);

Many bouncing balls in javascript

I've been trying to get this to work for a while and I've hit a block and can't get these balls to bounce off of each other and the walls. I'm trying to make essentially a virus simulator, with different balls having different properties determining infection chances whenever they contact each other. Here's the code I'm working with:
Molecule.js:
class Molecule {
constructor(_i, _max_rad){
this.moleculeIndex = _i;
this.rad = random(min_rad, _max_rad);
this.position = createVector(random(this.rad,width-this.rad),random(this.rad, height-this.rad));
this.velocity = createVector(random(-2,5),random(-2,5));
this.bounce = false;
}
render() {
noStroke();
if (this.bounce) {
let dx = this.position.x - molecules[moleculeIndex].position.x;
let dy = this.position.y - molecules[moleculeIndex].position.y;
let dist = Math.sqrt(dx * dx + dy * dy);
let normalX = dx / dist;
let normalY = dy / dist;
let midpointX = (this.position.x.x + molecules[moleculeIndexmoleculeIndex].position.x) / 2;
let midpointY = (this.position.x.y + molecules[moleculeIndex].position.y) / 2;
let dVector = (this.velocity.x - molecules[moleculeIndex].velocity.x) * normalX;
dVector += (this.velocity.y - molecules[moleculeIndex].velocity.y) * normalY;
let dvx = dVector * normalX;
let dvy = dVector * normalY;
this.velocity.x -= dvx;
this.velocity.y -= dvy;
molecules[moleculeIndex].velocity.x += dvx;
molecules[moleculeIndex].velocity.y += dvy;
}
push();
translate(this.position.x,this.position.y)
ellipse(0,0,this.rad*2,this.rad*2);
pop();
}
step() {
this.position.add(this.velocity);
}
checkEdges(){
if(this.position.x < this.rad || this.position.x > width-this.rad){
this.velocity.x = this.velocity.x * -1;
}
if(this.position.y < this.rad || this.position.y > height-this.rad){
this.velocity.y = this.velocity.y * -1;
}
}
}
Sketch.js:
let molecules = [];
let numOfMolecules = 100;
let min_rad = 10;
let max_rad = 50;
let row = 5;
let col = 5;
let gridHeight;
let gridWidth;
let moleculeKey;
let tempArray;
let intersectCount;
let numchecks;
let displayMolecules = true;
let draw_grid = true;
let display_info = true;
function setup() {
createCanvas(window.innerWidth, window.innerHeight); //create canvas size of screen
background(150, 178, 164);
createMolecules();
}
function createMolecules() {
molecules = [];
for (let i = 0; i < numOfMolecules; i++) {
molecules.push(new Molecule(i, max_rad));
}
}
function draw() {
background(150, 178, 164);
gridWidth = window.innerWidth / col;
gridHeight = window.innerHeight / row;
splitIntoGrids();
checkIntersections();
drawGrid();
renderGrid();
resetBalls();
}
function splitIntoGrids() {
moleculeKey = [];
for (let i = 0; i < row; i++) {
moleculeKey.push([]);
for (let j = 0; j < col; j++) {
moleculeKey[i].push([]);
molecules.forEach(molecule => {
if ((molecule.position.x + molecule.rad > j * gridWidth) &&
(molecule.position.x - molecule.rad < j * gridWidth + gridWidth) &&
(molecule.position.y + molecule.rad > i * gridHeight) &&
(molecule.position.y - molecule.rad < i * gridHeight + gridHeight)) {
moleculeKey[i][j].push(molecule.moleculeIndex);
}
});
}
}
}
/* Splits into grids and counts the molecules in each grid.
* Also checks molecules when overlapping between two cells
* Stores molecules in an array, to track the location of each molecule
*/
function checkIntersections() {
intersectCount = 0;
numchecks = 0;
for (let i = 0; i < moleculeKey.length; i++) {
for (let j = 0; j < moleculeKey[i].length; j++) {
// if a cell contains more than one molecule, store the molecules into temporary array
if (moleculeKey[i][j].length > 1) {
tempArray = moleculeKey[i][j];
// loops through each molecule in the temporary array
for (let k = 0; k < tempArray.length; k++) {
for (let l = k + 1; l < tempArray.length; l++) {
// calculate distance of the molecules between each other
let distanceMolecules = p5.Vector.sub(molecules[tempArray[k]].position, molecules[tempArray[l]].position);
let vectorLength = distanceMolecules.mag();
numchecks++;
//checks if molecules are intersecting
if (vectorLength < molecules[tempArray[k]].rad + molecules[tempArray[l]].rad) {
molecules[tempArray[k]].bounce = true;
molecules[tempArray[l]].bounce = true;
intersectCount++;
}
}
}
}
}
}
}
function drawGrid() {
if (draw_grid) {
for (let i = 0; i < row; i++) {
for (let j = 0; j < col; j++) {
stroke(255);
noFill();
rect(j * gridWidth, i * gridHeight, gridWidth, gridHeight);
fill(255);
textSize(12);
text(moleculeKey[i][j].length, j * gridWidth + 10, i * gridHeight + gridHeight - 10);
}
}
}
}
function resetBalls() {
for (let i = 0; i < moleculeKey.length; i++) {
for (let j = 0; j < moleculeKey[i].length; j++) {
if (moleculeKey[i][j].length > 1) {
tempArray = moleculeKey[i][j];
//console.log(tempArray);
for (let k = 0; k < tempArray.length; k++) {
for (let l = k + 1; l < tempArray.length; l++) {
let distanceMolecules = p5.Vector.sub(molecules[tempArray[k]].position, molecules[tempArray[l]].position);
let vectorLength = distanceMolecules.mag(); //get the length of vector
//checks if molecules are not intersecting
if (!vectorLength < molecules[tempArray[k]].rad + molecules[tempArray[l]].rad) {
//change back color
molecules[tempArray[k]].bounce = false;
molecules[tempArray[l]].bounce = false;
}
}
}
}
}
}
}
function renderGrid() {
molecules.forEach(molecule => {
if (displayMolecules) {
molecule.render();
}
molecule.checkEdges();
molecule.step();
});
}
With this code I get an error 'Uncaught ReferenceError: moleculeIndex is not defined' on the first line of the if(bounce), I've tried replacing moleculeIndex with some other things but really it was just hoping. Does anyone know why I'm having this problem?
Thanks.
Try replacing the: moleculeIndex with this.moleculeIndex

Game of life bug

I'm coding Conways game of life in P5JS, but I got a wierd bug. It seems to "work" but it looks all wrong. I'm not sure if it has t do with finding the neighbors, because when I call the function manually, it works. I even copied a second neighbor-counting function of the internet in there, and it works, too.
Maybe it's a visual glitch, but I'm not sure of that either, because the code looks fine.
/// <reference path="../TSDef/p5.global-mode.d.ts" />
let gridSize = 10;
let arrCurrent = create2dArray(gridSize);
let arrNext = create2dArray(gridSize);
function setup() {
createCanvas(800, 800, WEBGL);
background(0);
stroke(0, 255, 0);
noFill();
initGame();
}
function draw() {
displayCells();
calcNextGen();
}
//Returns a 2D Array
function create2dArray(size) {
let newArray = new Array(size);
for (let i = 0; i < newArray.length; i++) {
newArray[i] = new Array(1);
}
return newArray;
}
//Fills initial array with random values
function initGame() {
for (let x = 0; x < arrCurrent.length; x++) {
for (let y = 0; y < arrCurrent.length; y++) {
arrCurrent[x][y] = Math.round((Math.random()));
}
}
}
//Calculates next generation
function calcNextGen() {
for (let x = 0; x < gridSize; x++) {
for (let y = 0; y < gridSize; y++) {
let neighbors = countNeighbors1(arrCurrent, x, y);
let state = arrCurrent[x][y];
//If cell is dead and has exactly 3 neighbors, it starts living
if (state === 0 && neighbors === 3) {
arrNext[x][y] = 1;
}
//If cell lives and has too few or too many neighbors, it dies
else if (state === 1 && (neighbors < 2 || neighbors > 3)) {
arrNext[x][y] = 0;
}
else {
arrNext[x][y] = state;
}
}
}
arrCurrent = arrNext.slice();
}
//Count neighbors
function countNeighbors(x, y) {
return arrCurrent[(x + 1) % gridSize][y] +
arrCurrent[x][(y + 1) % gridSize] +
arrCurrent[(x + gridSize - 1) % gridSize][y] +
arrCurrent[x][(y + gridSize - 1) % gridSize] +
arrCurrent[(x + 1) % gridSize][(y + 1) % gridSize] +
arrCurrent[(x + gridSize - 1) % gridSize][(y + 1) % gridSize] +
arrCurrent[(x + gridSize - 1) % gridSize][(y + gridSize - 1) % gridSize] +
arrCurrent[(x + 1) % gridSize][(y + gridSize - 1) % gridSize];
}
function countNeighbors1(grid, x, y) {
let sum = 0;
for (let i = -1; i < 2; i++) {
for (let j = -1; j < 2; j++) {
let col = (x + i + gridSize) % gridSize;
let row = (y + j + gridSize) % gridSize;
sum += grid[col][row];
}
}
sum -= grid[x][y];
return sum;
}
function displayCells() {
background(0);
translate(-300, -300, 0);
for (let x = 0; x < arrCurrent.length; x++) {
for (let y = 0; y < arrCurrent.length; y++) {
push();
translate(x * 50, y * 50, 0);
if (arrCurrent[x][y] === 1) box(50);
pop();
}
}
}
function logGrid() {
console.log(arrCurrent[0]);
console.log(arrCurrent[1]);
console.log(arrCurrent[2]);
console.log(arrCurrent[3]);
console.log(arrCurrent[4]);
console.log(arrCurrent[5]);
console.log(arrCurrent[6]);
console.log(arrCurrent[7]);
console.log(arrCurrent[8]);
console.log(arrCurrent[9]);
}
I know I'm very close, but I'm banging my head against this one since 2 hours.
Here's a little P5JS Web Editor, you can copy the code over and visually see the problem.
Any help is appreciated - thank you!
arrCurrent = arrNext.slice(); doesn't create a deep copy of the grid, it just creates a shallow copy of the first dimension.
It creates a grid, where columns of arrCurrent refers to the rows of arrNext.
You've to create a completely new grid:
arrCurrent = []
for (let x = 0; x < gridSize; x++)
arrCurrent.push(arrNext[x].slice());
let gridSize = 10;
let arrCurrent = create2dArray(gridSize);
let arrNext = create2dArray(gridSize);
function setup() {
createCanvas(800, 800, WEBGL);
background(0);
stroke(0, 255, 0);
noFill();
initGame();
frameRate(10)
}
function draw() {
displayCells();
calcNextGen();
}
//Returns a 2D Array
function create2dArray(size) {
let newArray = new Array(size);
for (let i = 0; i < newArray.length; i++) {
newArray[i] = new Array(1);
}
return newArray;
}
//Fills initial array with random values
function initGame() {
for (let x = 0; x < arrCurrent.length; x++) {
for (let y = 0; y < arrCurrent.length; y++) {
arrCurrent[x][y] = Math.round((Math.random()));
}
}
}
//Calculates next generation
// - A live cell dies if it has fewer than two live neighbors.
// - A live cell with two or three live neighbors lives on to the next generation.
// - A live cell with more than three live neighbors dies.
// - A dead cell will be brought back to live if it has exactly three live neighbors.
function calcNextGen() {
for (let x = 0; x < gridSize; x++) {
for (let y = 0; y < gridSize; y++) {
let neighbors = countNeighbors1(arrCurrent, x, y);
let state = arrCurrent[x][y];
//If cell is dead and has exactly 3 neighbors, it starts living
if (state === 0 && neighbors === 3) {
arrNext[x][y] = 1;
}
//If cell lives and has too few or too many neighbors, it dies
else if (state === 1 && (neighbors < 2 || neighbors > 3)) {
arrNext[x][y] = 0;
}
else {
arrNext[x][y] = state;
}
}
}
arrCurrent = []
for (let x = 0; x < gridSize; x++)
arrCurrent.push(arrNext[x].slice());
}
function countNeighbors1(grid, x, y) {
let sum = 0;
for (let i = -1; i < 2; i++) {
for (let j = -1; j < 2; j++) {
let col = (x + i + gridSize) % gridSize;
let row = (y + j + gridSize) % gridSize;
sum += grid[col][row];
}
}
sum -= grid[x][y];
return sum;
}
function displayCells() {
background(0);
translate(-75, -75, 0);
stroke(128);
box(50*gridSize, 50*gridSize, 50);
translate(-225, -225, 0);
stroke(0, 255, 0);
for (let x = 0; x < arrCurrent.length; x++) {
for (let y = 0; y < arrCurrent.length; y++) {
push();
translate(x * 50, y * 50, 0);
if (arrCurrent[x][y] === 1) box(50);
pop();
}
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.9.0/p5.js"></script>

Is there a way to reduce the number of coordinates in a complex enclosed SVG path?

What I'd like to do is take an SVG shape drawn by an enclosed path (in this case, a region of a map) and reduce the number of points to create a simpler shape.
I've tried implementing the Ramer-Douglas-Peucker algorithm to reduce the number of points. For example, here's a fiddle using the simplify.js library:
https://jsfiddle.net/0t3n8762/
After reading about the issue, if I understood it correctly, it seems the algorithm isn't really designed to work on enclosed shapes but open ended paths. I tried splitting each path into two (so there are two lines that together make the entire shape) and running the algorithm on each before recombining them, though the results seem essentially identical:
https://jsfiddle.net/caqwL3t7/
It may be (and indeed is quite likely) that I'm just not grasping how the algorithm is supposed to work and am implementing it incorrectly. Or perhaps that I should be trying a completely different method altogether.
coords = pathToCoords($("#JP-01").attr("d"));
for (var i = 0; i < coords.length; i++) {
newCoords[i] = simplify(coords[i], 2);
}
newPath = coordsToPath(newCoords);
$("#JP-01").attr("d", newPath);
What I would want to produce is a simpler path that still retains the overall shape of the original, drawn with fewer points. The actual result is a distorted shape that shares little in common with the original.
As Paul pointed out in the comments, you haven't considered about l and v command in a path.
I made a snippet to show how to achieve your goal but it won't work in all the cases (I guess) and it still needs to be improved.
Kindly find the snippet and comments below.
$("#simplify-1").on("click", function(){
var coords;
var newCoords = [];
var newPath;
var coordsObject;
// get the coordinates from a given path
function pathToCoords(path) {
// save each path individually (.i.e islands are stored separately)
var data = path.match(/(?<=m).*?(?=z)/igs);
var coordsArray = [];
var objectArray = [];
var objectArrayA = [];
var objectArrayB = [];
var objectContainer = [];
// split each pair of coordinates into their own arrays
for (var i = 0; i < data.length; i++) {
// should remove anything between h or v and l instead?
data[i] = data[i].split(/[LlHhVv]/);
coordsArray[i] = [];
for (var j = 0; j < data[i].length; j++) {
coordsArray[i].push(data[i][j].split(",").map(Number));
}
}
// convert each pair of coordinates into an object of x and y
for (var i = 0; i < coordsArray.length; i++) {
objectArray[i] = [];
for (var j = 0; j < coordsArray[i].length; j++) {
objectArray[i].push({
x: coordsArray[i][j][0],
y: coordsArray[i][j][1]
});
}
// split each array of coordinates in half
var halfway = Math.floor(objectArray[i].length / 2);
objectArrayB[i] = JSON.parse(JSON.stringify(objectArray[i]));;
objectArrayA[i] = objectArrayB[i].splice(0, halfway);
}
objectContainer = [objectArrayA, objectArrayB];
return objectContainer;
}
// convert the coordinates back into a string for the path
function coordsToPath(objectContainer) {
var objectArray = [];
var coordsArray = [];
var data;
// recombine the two objectArrays
for (var i = 0; i < objectContainer[0].length; i++) {
objectArray[i] = objectContainer[0][i].concat(objectContainer[1][i])
}
for (var i = 0; i < objectArray.length; i++) {
coordsArray[i] = [];
// take the X and Y values from the objectArray and strip the unwanted information
for (var j = 0; j < objectArray[i].length; j++) {
if (j == 0) {
// add 'M' in front of the first entry
coordsArray[i].push("M" + Object.values(objectArray[i][j]));
} else if (j == objectArray[i].length - 1) {
// add 'z' to the end of the last entry
coordsArray[i].push("l" + Object.values(objectArray[i][j]) + "z");
} else {
// add 'l' in front of each coordinate pair
coordsArray[i].push("l" + Object.values(objectArray[i][j]));
}
}
coordsArray[i] = coordsArray[i].toString();
}
// put everything back into a single valid SVG path string
data = coordsArray.join("");
return data;
}
// -- simplify.js -- //
/*
(c) 2017, Vladimir Agafonkin
Simplify.js, a high-performance JS polyline simplification library
mourner.github.io/simplify-js
*/
(function() {
'use strict';
// to suit your point format, run search/replace for '.x' and '.y';
// for 3D version, see 3d branch (configurability would draw significant performance overhead)
// square distance between 2 points
function getSqDist(p1, p2) {
var dx = p1.x - p2.x,
dy = p1.y - p2.y;
return dx * dx + dy * dy;
}
// square distance from a point to a segment
function getSqSegDist(p, p1, p2) {
var x = p1.x,
y = p1.y,
dx = p2.x - x,
dy = p2.y - y;
if (dx !== 0 || dy !== 0) {
var t = ((p.x - x) * dx + (p.y - y) * dy) / (dx * dx + dy * dy);
if (t > 1) {
x = p2.x;
y = p2.y;
} else if (t > 0) {
x += dx * t;
y += dy * t;
}
}
dx = p.x - x;
dy = p.y - y;
return dx * dx + dy * dy;
}
// rest of the code doesn't care about point format
// basic distance-based simplification
function simplifyRadialDist(points, sqTolerance) {
var prevPoint = points[0],
newPoints = [prevPoint],
point;
for (var i = 1, len = points.length; i < len; i++) {
point = points[i];
if (getSqDist(point, prevPoint) > sqTolerance) {
newPoints.push(point);
prevPoint = point;
}
}
if (prevPoint !== point) newPoints.push(point);
return newPoints;
}
function simplifyDPStep(points, first, last, sqTolerance, simplified) {
var maxSqDist = sqTolerance,
index;
for (var i = first + 1; i < last; i++) {
var sqDist = getSqSegDist(points[i], points[first], points[last]);
if (sqDist > maxSqDist) {
index = i;
maxSqDist = sqDist;
}
}
if (maxSqDist > sqTolerance) {
if (index - first > 1) simplifyDPStep(points, first, index, sqTolerance, simplified);
simplified.push(points[index]);
if (last - index > 1) simplifyDPStep(points, index, last, sqTolerance, simplified);
}
}
// simplification using Ramer-Douglas-Peucker algorithm
function simplifyDouglasPeucker(points, sqTolerance) {
var last = points.length - 1;
var simplified = [points[0]];
simplifyDPStep(points, 0, last, sqTolerance, simplified);
simplified.push(points[last]);
return simplified;
}
// both algorithms combined for awesome performance
function simplify(points, tolerance, highestQuality) {
if (points.length <= 2) return points;
var sqTolerance = tolerance !== undefined ? tolerance * tolerance : 1;
points = highestQuality ? points : simplifyRadialDist(points, sqTolerance);
points = simplifyDouglasPeucker(points, sqTolerance);
return points;
}
// export as AMD module / Node module / browser or worker variable
if (typeof define === 'function' && define.amd) define(function() {
return simplify;
});
else if (typeof module !== 'undefined') {
module.exports = simplify;
module.exports.default = simplify;
} else if (typeof self !== 'undefined') self.simplify = simplify;
else window.simplify = simplify;
})();
// -- end simplify.js -- //
coords = pathToCoords($("#OriginalJP-01").attr("d"));
for (var i = 0; i < coords.length; i++) {
newCoords[i] = [];
for (var j = 0; j < coords[i].length; j++) {
newCoords[i][j] = simplify(coords[i][j], 1);
}
}
newPath = coordsToPath(newCoords);
$("#JP-01").attr("d", newPath);
});
$("#simplify-2").on("click", function(){
let d = $("#OriginalJP-01").attr("d");
let coordsArray = [];
let data = d.match(/(?<=m).*?(?=z)/igs);
// split each pair of coordinates into the array as an object {x, y}
for (var i = 0; i < data.length; i++) {
let ca = coordsArray[i] = [];
// split data[i] into each coordinate text
let matches = data[i].match(/((\w?-?\d+(\.\d+)?)(,-?\d+(\.\d+)?)?)/g);
for(let j=0;j<matches.length;j++){
let x, y,
text = matches[j],
// split with comma and convert it to a number
temp = text.split(",").map(v=>+v.replace(/^[^\-\d]/g,""));
switch(text[0]){
default:
case "L": // absolute
x = temp[0];
y = temp[1];
break;
case "l": // relative
x = ca[j-1].x + temp[0];
y = ca[j-1].y + temp[1];
break;
case "V": // absolute
x = ca[j-1].x;
y = temp[0];
break;
case "v": // relative
x = ca[j-1].x;
y = ca[j-1].y + temp[0];
break;
case "H": // absolute
x = temp[0];
y = ca[j-1].y;
break;
case "h": // relative
x = ca[j-1].x + temp[0];
y = ca[j-1].y;
break;
}
x = +x.toFixed(2);
y = +y.toFixed(2);
ca.push({x, y});
}
}
let mArray = [];
// calculate the slopes
for(let i=0;i<coordsArray.length;i++){
mArray[i] = [];
for(let j=0;j<coordsArray[i].length-1;j++){
let {x, y} = coordsArray[i][j], // current point's x and y
{x: nx, y: ny} = coordsArray[i][j+1], // next point's x and y
dy = (ny - y);
if(dy === 0) // to check if the denominator is legal or not
// in your case, it would not enter here
mArray[i].push(Infinity);
else
mArray[i].push((nx - x) / dy);
}
}
let abandonFactor = +$("#abandonFactor").val();
let newCoordsArray = [];
for(let i=0;i<mArray.length;i++){
let na = newCoordsArray[i] = [];
// calculate the abandonRate base on the amount of the original points
let abandonRate = coordsArray[i].length * abandonFactor;
for(let j=0;j<mArray[i].length-1;j++){
let m = mArray[i][j], // this slope
nm = mArray[i][j+1]; // next slope
let diffRate = Math.abs((m - nm) / m); // calculate the changes of the slope
// check if the diffRate is greater than abandonRate
// or the sign of m not equals the sign of nm
// you can try out removing the "sign check part" and see what would happen ;)
if(diffRate >= abandonRate || (Math.sign(m) !== Math.sign(nm))){
na.push(coordsArray[i][j]);
}
}
}
let newPath = [];
// create new path
for(let i=0;i<newCoordsArray.length;i++){
let temp = [];
for(let j=0;j<newCoordsArray[i].length;j++){
let {x, y} = newCoordsArray[i][j];
let p = `${x},${y}`;
temp.push(p);
}
newPath.push("M" + temp.join("L") + "z");
}
$("#JP-01").attr("d", newPath.join(""));
}).click();
$("#abandonFactor").on("change", function(){
$("#abandonFactor_text").text(`Abandon factor: ${this.value}`);
$("#simplify-2").click();
});
div {
width: 50%;
}
.original {
float: left;
}
.simplified {
float: right;
}
.map {
box-sizing: border-box;
width: 100%;
padding: 0.5rem;
text-shadow: 1px 1px white;
cursor: grab;
}
.land {
fill: lightgreen;
fill-opacity: 1;
stroke: white;
stroke-opacity: 1;
stroke-width: 0.5;
cursor: pointer;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="original">
<svg id="OriginalMap" viewBox="324.55999755859375 0 126.44989013671875 111.65999603271484" class="map" xmlns="http://www.w3.org/2000/svg" xmlns:amcharts="http://amcharts.com/ammap">
<g>
<path id="OriginalJP-01" title="Hokkaido" class="land" d="
M344.04,71.29l2.71,-2.13l0.04,-0.59l-1.96,-3.17l-1.38,-0.99l-0.7,-1.16l0.68,-2.54l-0.27,-0.48l1.64,0.11l0.6,-0.42l0.21,-0.54l0.53,-0.21l2.75,2.66l1.93,0.87l1.13,1.28l2.48,-0.47l0.17,-0.42l1.02,-0.32l0.72,0.02l-0.14,1.17l3.04,1.39l2.2,-1.22l1.97,-2.15l0.99,-1.6l0.1,-2.35l-1.38,-2.76l0.54,-1.94L363,51.89l-0.45,-2.27l1.02,-2.1l1.47,-0.89l0.76,-0.05l2.03,-1.32l0.93,-1.89l0.41,-1.95l-0.35,-7.86l0.5,-0.32l1.64,-3.44l0.21,-2.86l0.38,-0.79l0.13,-1.6l-0.3,-3.27l-0.77,-3.84l-3.04,-7.32l-0.31,-1.16l0.07,-0.99l0.86,-1.27l0.79,-1.95l-0.41,-1.4l0.15,-1.28l0.75,0.77l0.13,0.52l0.39,0.16l1.76,-0.28l1.35,-0.94l0.55,-1.99L374.52,0l0.47,0.33l0.56,1.29l0.59,0.34l0.63,1.47l2.32,1.7l2.21,3.12l4.95,5.56l3.91,7.23l2.58,2.55l2.44,3.1l5.58,4.4l1.57,0.89l0.33,0.95l1.26,0.96l3.7,1.94l3.24,1.28l5.88,1.69l2.79,0.53l0.44,-0.31l0.29,0.77l-0.06,1.28l0.87,1.55l0.58,0.48l3.21,1.01l3.41,0.21l2.57,-0.32l2.28,-1.99l1.54,-1.92l2.18,-2.04l1.83,-1.26l0.66,-1.45l1.1,-1.16l0.78,-1.38l0.46,0.03l0.45,2.23l-1.21,2.49l-0.91,0.92l-0.6,2.4l-1.73,2.13l-0.94,2.12l0.1,0.83L436.06,48l0.22,1.06l1.35,2.58l1.49,1.97l1.52,5.23l1.49,1.96l-1.16,-0.53l-0.19,-0.53l-1.14,-0.22l-0.24,0.37l0.21,1.26l0.86,-0.67l0.47,0.4l-0.29,0.55l0.37,0.39l0.4,-0.28l2.55,0.62l2.09,-1.7l0.32,-0.6l1.11,-0.71l0.24,-0.5l3.28,0.2l-0.19,0.5l-1.17,0.91l-1.73,0.31l-1.87,1.03l-0.8,2.93l-0.77,-0.36l-1.24,-0.05l-2.71,0.51l-1.51,0.9l-1.26,-0.21l-0.62,0.6l-0.14,1.15l-1.6,2.15l-1.02,0.49l-1.9,-0.1l-0.9,-0.79l0.03,-0.53l0.33,-0.31l-0.55,-0.37l-0.73,0.1l-1.19,1.73l-0.02,0.41l1.15,1.13l-2.01,-0.12l-1.15,-0.35l-3.58,-0.02l-0.69,-0.15l-1.58,-1.27l-3.06,0.54l-2.43,1.18l-3.4,2.53l-5.27,5.27l-4.08,5.8l-1.53,3.31l-0.28,1.67l0.55,1.44l-0.82,3.88l-0.91,1.53l0,1.61l-3.7,-3.72l-4.85,-2.02l-8.09,-4.45l-2.38,-1.67l-1.84,-2.05l-3.21,-0.94l-2.21,-2.05l-1.82,-1.2l-1.83,-0.53l-2.38,-0.04l-3.17,1.04l-2.08,1.29l-2.68,2.28l-1.59,0.89l-2.12,2.45l-0.34,0.75l-1.07,-0.4l-0.33,-0.55l0.38,-0.29l0.59,0.64l0.12,-0.81l-1.35,-0.41l-0.87,-2.37l-0.79,-0.4l-1.07,-1.12l-0.12,-0.58l-1.27,-1.23l-1.24,-0.09l-1.25,0.53l-0.97,-0.59l-1.21,0.03l-1.68,1.81l-1.44,2.75l-0.71,2.55l0.28,1.94l2.32,1.2l2.74,2.35l0.75,0.14l1.37,-0.53l2.04,0.31l1.23,2.21l1.63,1.22l1.08,1.88l3.15,1.36l0.55,0.89l0.79,0.59l-0.61,0.61l-0.95,0.01l-1.1,1.3l-1.66,0.66l-0.86,-0.79l-2.78,-0.91l-0.93,0.16l-0.28,0.62l-0.41,0.1l-0.18,-0.61l0.68,-0.45l-0.2,-0.67l-0.59,-0.37l-0.86,0.08l-0.41,0.33l-0.43,1.68l-1.44,1.06l-1.29,0.1l-0.35,0.33l-0.31,3.94l-0.4,0.48l-0.94,0.03l-1.96,0.87l-0.9,1.89l-0.39,0.32l-1.02,-0.8l-1.14,0.22l-0.9,-0.71l-1.08,-2.72l-0.09,-0.86l0.41,-1.77l1.35,-3.03v-1.05l0.77,0.08l0.29,-0.59l0.31,-2.7l-0.4,-1.98l-1.9,-2.94l-0.67,-0.36l-1.3,-0.12L334.29,91l-0.85,-0.88l-1.14,-0.41l-0.39,-0.73l-0.19,-1.39l0.31,-1.12l0.94,-1.03l0.33,-0.95l0.03,-2.65l-0.49,-2.23l0.92,-1.45l1.12,-0.62l2.28,-0.06l0.61,-0.91l1.39,-0.73l0.85,-1.92l0.82,0.57l0.56,0.97l0.89,-0.23l0.09,-1.39l0.82,-0.84L344.04,71.29z
M358.76,9.86l-0.09,-1.23l1.28,-1.25l2.07,1.21l0.51,1.81l-1.81,1.49l-1.37,-1.07L358.76,9.86z
M326.88,91.03l-0.29,2.22l-0.39,0.63l-0.65,0.23l-0.02,0.36l-0.61,-0.54l0.06,-1.71l-0.42,-1.11l0.74,-1.09l1.91,-0.38l0.46,-0.52l0.03,0.64L326.88,91.03z
M357.23,4.25l-0.26,2.59l-0.39,0.13L355.7,4l0.22,-1.31l-0.61,-0.34l0.09,-0.73l0.65,0.85l0.66,-0.15l-0.02,-0.59L357.03,2l0.35,0.78L357.23,4.25z"/>
</g>
</svg> Original
</div>
<div class="simplified">
<svg id="Map" viewBox="324.55999755859375 0 126.44989013671875 111.65999603271484" class="map" xmlns="http://www.w3.org/2000/svg" xmlns:amcharts="http://amcharts.com/ammap">
<g>
<path id="JP-01" title="Hokkaido" class="land" d=""/>
</g>
</svg> Simplified
<button id="simplify-1">Your method</button>
<button id="simplify-2">New method</button>
<input type="range" min="0.0001" max="0.1" value="0.01" step="0.0001" class="slider" id="abandonFactor">
<div id="abandonFactor_text">Abandon factor: 0.01</div>
</div>

Fast way scaling arrays (2d matrix of ones and zeros) with a specific factor

I have an array (2d matrix of ones and zeros) like below and like to change it's size and so I've developed a function you can also find below.
'011111' '001111111111'
'000000' '001111111111'
'000011' ---> '000000000000'
'000001' '000000001111'
'000000001111'
'000000000011'
'011111' '001111111111'
'000000' '001111111111'
'000011' <--- '000000000000'
'000001' '000000001111'
'000000001111'
'000000000011'
So the example task above is working fine with the code below. But scaling down a big matrix (2000*1000) to (40*20) takes a LONG time. Also in the other direction (40*20) to (2000*1000)
So does anybody has an idea how to edit my code to make it working way faster?
So I would be very thankful if somebody shares some working code matching my problem, however.. Thanks a million in advance,
Nina.
var matrix=[
'011111000000000000000000001111000000000000000000000000000000000000000011111110000000000000001011',
'001111000000000000000000011110000000000000000011111000000000000000000001111111000000000000011101',
'001111100000000000000000111110000000000000111111111111100000000000000000011111100000000000010100',
'011111000000000000000000111100000000000111111111111111111000000000000000000111110000000000111000',
'000000000000000000000001111100000000001111111111111111111110000000000000000001100000000000111000',
'000000000000000000000001111000000000111111111111111111111111000000000000000000000000000001110000',
'000000000000000000000011110000000001111111111111111111111111100000000000000000000000000001110000',
'000000000000000000000111110000000011111111111111111111111111111000000000000000000000000011100000',
'000000000000000000000111100000000111111111111111111111111111111000000000000000000000000111000000',
'000000000000000000001111100000001111111111111111111111111111111100000000000000000000000111000001',
'000000000000000000011111000000011111111111111111111111111111111110000000000000000000001110000001',
'000000000000000000011110000000011111111111111111111111111111111111000000000000000000001110000000',
'000000000000000000111110000000111111111111111111111111111111111111110000000000000000000111000000',
'000000000000000000111100000001111111111111111111111111111111111111100000000000000000011000000001',
'000000000000000001111100000001111111111111111111111111111111111111111000000000000000001110000000',
'000000000000000011111000000001111111111111111111111111111111111111111000000000000000011100000000',
'000000000000000011111000000011111111111111111111111111111111111111111110000000000000001111000001',
'000000000000000111110000000011111111111111111111111111111111111111111110000000000000011100000001',
'000000000000001111100000000011111111111111111111111111111111111111111100000000000001110000000001',
'000000000000001111100000000011111111111111111111111111111111111111111110000000000000011100000001',
'000000000000011111000000000111111111111111111111111111111111111111111110000000000001110000000001',
'000000000000011111000000000111111111111111111111111111111111111111111110000000000111000000000001',
'000000000000111110000000000111111111111111111111111111111111111111111110000000000011000000000001',
'000000000000111100000000000111111111111111111111111111111111111111111110000000000011000000000001',
'000000000000111100000000000111111111111111111111111111111111111111111110000000111000000000000001',
'000000000001111000000000000111111111111111111111111111111111111111111110000000010100000000000001',
'000000000001111000000000000111111111111111111111111111111111111111111100011100000000000000000001',
'000000000111110000000000000111111111111111111111111111111111111111111000001111000000000000000001',
'000000000111100000000000000011111111111111111111111111111111111111111000000011100000000000000001',
'000000000111100000000000000011111111111111111111111111111111111111110000001111000000000000000001',
'000000000111100000000000000011111111111111111111111111111111111111100000000111000000000000000001',
'000000101111110000000000000001111111111111111111111111111111111111100000000110000000000000000001',
'000000001111111000000000000001111111111111111111111111111111111111000000111000000000000000000001',
'000000000111111110000000000000111111111111111111111111111111111110000000011110000000000000000001',
'000000000011111111100000000000111111111111111111111111111111111100000000011100000000000000000001',
'000000000001111111110000000000011111111111111111111111111111111100000000111000000000000000000001',
'000000000000011111111000000000001111111111111111111111111111111000000001111000000000000000000001',
'000000000000001111111100000000001111111111111111111111111111110000000001110000000000000000000001',
'000000000000000011111111000000000011111111111111111111111111100000000001110000000000000000000001',
'000000000000000001111111110000000001111111111111111111111111000000000010000000000000000000000001',
'000000000000000000111111111000000000111111111111111111111100000000000111100000000000000000000001',
'000000000000000000001111111110000000001111111111111111111000000000000111000000000000000000000001',
'000000000000000000000011111111100000000011111111111111000000000000001111000000000000000000000001',
'000000000000000000000001111111111000000000001111111000000000000000001110000000000000000000000001',
'000000000000000000000000011110111100000000000000000000000000000000011100000000000000000000000001'
]
Array.prototype.scale_matrix = function(scale_factor) {
var matrix = this;
var scale = 1 / scale_factor;
var array = duplicate_matrix(Math.round(this.length * scale_factor), Math.round(this[0].length * scale_factor), '0').map(str => Array.from(str, String))
if (scale_factor < 1) {
for (var y = 0; y < array.length; y++) {
for (var x = 0; x < array[0].length; x++) {
_y = Math.round(y / scale_factor);
_x = Math.round(x / scale_factor);
var old_values = []
for (var oldx = _x; oldx < _x + scale; oldx++) {
for (var oldy = _y; oldy < _y + scale; oldy++) {
try {
old_values.push(matrix[oldy][oldx]);
}
catch (e) {}
}
}
var new_value = old_values.sort((a, b) => old_values.filter(v => v === a).length - old_values.filter(v => v === b).length).pop();
array[y][x] = matrix[_y][_x];
}
}
}
else {
for (var y = 0; y < matrix.length; y++) {
for (var x = 0; x < matrix[0].length; x++) {
_y = Math.round(y * scale_factor);
_x = Math.round(x * scale_factor);
for (var newx = _x; newx < _x + scale_factor; newx++) {
for (var newy = _y; newy < _y + scale_factor; newy++) {
try {
array[newy][newx] = matrix[y][x];
}
catch (e) {}
}
}
}
}
}
return array.map(a => [a.join('')]).concat.apply([], array.map(a => [a.join('')]));
}
function duplicate_matrix(rows, cols, value) {
var arr = [];
for (var i = 0; i < rows; i++) {
arr.push([]);
arr[i].push(new Array(cols));
for (var j = 0; j < cols; j++) arr[i][j] = value;
};
return arr;
}
console.log(matrix.scale_matrix(0.3).join('\n'));
console.log('\n\n>------<\n\n');
console.log(matrix.scale_matrix(1.3).join('\n'));
.as-console-wrapper { max-height: 100% !important; top: 0; }

Categories

Resources