I implemented the algorithm from the answer in here Maze (recursive division) algorithm design.
but I ran into a problem that there isn't always a path from start(+) to finish(-)
like this image.
also there is another problem that the start(+) or finish(-) node will be surrounded by wall see start node (+)
but I think it's easy to fix.
remove the walls around that node [x-1][y], [x][y+1], [x+1][y] and [x][y-1].
I don't know if it's the right fix or not.
that's my maze class
class Maze {
private G: INode[][];
private rows: number;
private cols: number;
constructor(G: INode[][]) {
this.G = G.map((row) =>
row.map((node) => ({
...node,
isWall: false,
}))
);
this.rows = this.G.length;
this.cols = this.G[0].length;
// border walls
for (let i = 0; i < this.rows; i++) {
this.G[i][0].isWall = true;
this.G[i][this.cols - 1].isWall = true;
}
for (let i = 0; i < this.cols; i++) {
this.G[0][i].isWall = true;
this.G[this.rows - 1][i].isWall = true;
}
}
private rand(min: number, max: number) {
return Math.floor(Math.random() * (max - min + 1) + min);
}
private isNodeStartOrFinish(row: number, col: number) {
return this.G[row][col].isStart || this.G[row][col].isFinish;
}
// call perfectMaze to get the generate the maze
public perfectMaze() {
this.divide(true, 1, this.cols - 2, 1, this.rows - 2);
}
private divide(
h: boolean,
minX: number,
maxX: number,
minY: number,
maxY: number
) {
if (h) {
if (maxX - minX < 2) return;
const y = Math.floor(this.rand(minY, maxY) / 2) * 2;
this.addHWall(minX, maxX, y);
this.divide(!h, minX, maxX, minY, y - 1);
this.divide(!h, minX, maxX, y + 1, maxY);
} else {
if (maxY - minY < 2) return;
const x = Math.floor(this.rand(minX, maxX) / 2) * 2;
this.addVWall(minY, maxY, x);
this.divide(!h, minX, x - 1, minY, maxY);
this.divide(!h, x + 1, maxX, minY, maxY);
}
}
private addHWall(minX: number, maxX: number, y: number) {
const hole = Math.floor(this.rand(minX, maxX) / 2) * 2 + 1;
for (let i = minX; i <= maxX; i++) {
if (i !== hole && !this.isNodeStartOrFinish(y, i)) {
this.G[y][i].isWall = true;
}
}
}
private addVWall(minY: number, maxY: number, x: number) {
const hole = Math.floor(this.rand(minY, maxY) / 2) * 2 + 1;
for (let i = minY; i <= maxY; i++) {
if (i !== hole && !this.isNodeStartOrFinish(i, x)) {
this.G[i][x].isWall = true;
}
}
}
public getMaze() {
return this.G;
}
}
okay i was stupid
i didn't remove the wall when i === hole
in the addHWall and addVWall methods
you must delete the wall if i === hole otherwise the wall will be added again
private addHWall(minX: number, maxX: number, y: number) {
const hole = Math.floor(this.rand(minX, maxX) / 2) * 2 + 1;
for (let i = minX; i <= maxX; i++) {
if (i === hole || this.isNodeStartOrFinish(y, i)) {
this.G[y][i].isWall = false;
} else {
this.G[y][i].isWall = true;
}
}
}
the same with addVWall
if (i === hole || this.isNodeStartOrFinish(i, x)) {
this.G[y][i].isWall = false;
} else {
this.G[i][x].isWall = true;
}
and i ended up deleting the walls around the start and finish node still don't know if it's the right solution or not
Related
I am trying to create a shape with both straight lines and curved parts. I want all points to connect and then to fill the interior with a color. I cannot seem to locate this information or a way to do it in a single shape. Is there a way to do this? For example a rectangle with an inverted curve on each end? I can do it with arcs and lines like the below code but I have to think there is an easier way using beginShape somehow or something similar? Also even using the lines and arcs, I am not sure how to fill it.
arc(200, 200, 150, 150, radians(0), radians(90), OPEN);
arc(200, 200, 50, 50, radians(0), radians(90), OPEN);
let x1 = 200 + Math.cos(radians(0)) * 25;
let y1 = 200 + Math.sin(radians(0)) * 25;
let x2 = 200 + Math.cos(radians(90)) * 25;
let y2 = 200 + Math.sin(radians(90)) * 25;
let x3 = 200 + Math.cos(radians(0)) * 75;
let y3 = 200 + Math.sin(radians(0)) * 75;
let x4 = 200 + Math.cos(radians(90)) * 75;
let y4 = 200 + Math.sin(radians(90)) * 75;
line(x1, y1, x3, y3)
line(x2, y2, x4, y4)
This should answer part of your question experimentally. As you can see by playing with the sketch below, if there is a single curveVertex() in the shape all of the vertices created with vertex() will also be treated as curveVertex() type vertices.
I think you should be able to achieve your goal with bezierVertex().
const InteractionThreshold = 5;
const SqInteractionThreshold = InteractionThreshold * InteractionThreshold;
function lineEditor(p) {
let controlPoints = [{
x: 50,
y: 50,
type: 'vertex'
},
{
x: 250,
y: 250,
type: 'vertex'
},
];
p.setup = () => {
p.createCanvas(300, 300);
p.rectMode(p.RADIUS);
p.ellipseMode(p.RADIUS);
p.noFill();
p.noLoop();
};
p.draw = () => {
p.background(200);
p.beginShape();
for (let i = 0; i < controlPoints.length; i++) {
let pt = controlPoints[i];
switch (pt.type) {
case 'vertex':
p.vertex(pt.x, pt.y);
break;
case 'curve':
p.curveVertex(pt.x, pt.y);
break;
}
}
p.endShape();
p.push();
p.stroke('green');
p.drawingContext.setLineDash([5, 15]);
p.beginShape();
for (let i = 0; i < controlPoints.length; i++) {
let pt = controlPoints[i];
p.vertex(pt.x, pt.y);
}
p.endShape();
p.drawingContext.setLineDash([]);
p.pop();
p.push();
p.fill("red");
p.noStroke();
for (let i = 0; i < controlPoints.length; i++) {
let pt = controlPoints[i];
switch (pt.type) {
case 'curve':
p.circle(pt.x, pt.y, 4);
break;
case 'vertex':
p.push();
p.translate(pt.x, pt.y);
p.rotate(p.PI / 4);
p.square(0, 0, 4);
p.pop();
break;
}
}
p.pop();
};
let dragging;
p.mousePressed = function() {
// is the mouse over a point
let closest = getClosest(p.mouseX, p.mouseY);
if (closest && closest.sqdist < SqInteractionThreshold) {
dragging = closest.ix;
}
};
p.mouseReleased = function() {
dragging = undefined;
};
p.mouseDragged = function() {
if (dragging !== undefined) {
controlPoints[dragging].x = p.constrain(p.mouseX, 0, p.width);
controlPoints[dragging].y = p.constrain(p.mouseY, 0, p.height);
p.redraw();
}
};
p.mouseClicked = function() {
if (p.keyIsDown(p.SHIFT)) {
// is the mouse over a point
let closest = getClosest(p.mouseX, p.mouseY);
if (closest && closest.sqdist < SqInteractionThreshold) {
let pt = controlPoints[closest.ix];
switch (pt.type) {
case 'vertex':
pt.type = 'curve';
break;
case 'curve':
pt.type = 'vertex';
break;
}
p.redraw();
}
}
}
p.doubleClicked = function() {
if (p.keyIsDown(p.SHIFT)) {
if (controlPoints.length > 2) {
// delete
let closest = getClosest(p.mouseX, p.mouseY);
if (closest && closest.sqdist < SqInteractionThreshold) {
controlPoints.splice(closest.ix, 1);
p.redraw();
}
}
} else {
// insert
let closest = getClosestSegment(p.mouseX, p.mouseY);
if (closest.error < InteractionThreshold) {
controlPoints.splice(closest.end, 0, {
x: p.mouseX,
y: p.mouseY,
type: 'vertex'
});
p.redraw();
}
}
};
function getClosest(x, y) {
let closest;
for (let i = 0; i < controlPoints.length; i++) {
let dx = x - controlPoints[i].x;
let dy = y - controlPoints[i].y;
let sqdist = dx * dx + dy * dy;
if (!closest || sqdist < closest.sqdist) {
closest = {
sqdist,
ix: i
};
}
}
return closest;
}
function getClosestSegment(x, y) {
let closest;
for (let i = 0; i < controlPoints.length - 1; i++) {
let start = controlPoints[i];
let end = controlPoints[i + 1];
let angle = p.atan2(end.y - start.y, end.x - start.x);
let len = p.dist(start.x, start.y, end.x, end.y);
// convert [mouseX, mouseY] to a position relative to start
let vec = p.createVector(x - start.x, y - start.y);
vec = p.createVector(
p.cos(angle) * vec.x + p.sin(angle) * vec.y, -p.sin(angle) * vec.x + p.cos(angle) * vec.y,
);
let error;
if (vec.x < 0) {
error = p.dist(0, 0, vec.x, vec.y);
} else if (vec.x > len) {
error = p.dist(len, 0, vec.x, vec.y);
} else {
// There is no X error, only consider the Y
error = p.abs(vec.y);
}
if (!closest || error < closest.error) {
closest = {
error,
start: i,
end: i + 1
};
}
}
return closest;
}
}
let lineEditorSketch = new p5(lineEditor);
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.js"></script>
I need to access the wall array inside the C variable. I can find what c is, but not what any specific value of c. I probably made a mistake of how to access the value from this array, but I tried to do c[3], and it always says undefined. This is the full program link https://editor.p5js.org/Meowmeow/sketches/b4AhGA4xH.
function index(i, j) {
if (i < 0 || j < 0 || i > cols - 1 || j > cols - 1) {
return -1;
}
return i + j * cols
}
function Cell(i, j) {
this.visited = false;
this.i = i;
this.j = j;
this.walls = [true, true, true, true];
this.checkNeighbors = function() {
var neighbors = []
var top = grid[index(i, j - 1)]
var right = grid[index(i + 1, j)]
var left = grid[index(i - 1, j)]
var bottom = grid[index(i, j + 1)]
var Wall = this.walls;
var wall=[]
wall.push(i,j,Wall)
if (top && !top.visited) {
neighbors.push(top);
}
if (right && !right.visited) {
neighbors.push(right);
}
if (bottom && !bottom.visited) {
neighbors.push(bottom);
}
if (left && !left.visited) {
neighbors.push(left);
}
if (neighbors.length > 0) {
var r = floor(random(0, neighbors.length))
return neighbors[r];
} else {
return undefined;
}
}
this.highlight = function() {
var x = this.i * w;
var y = this.j * w;
noStroke();
fill(0, 0, 255, 100)
rect(x, y, w, w);
}
this.show = function() {
var x = this.i * w;
var y = this.j * w;
stroke(255);
noFill();
if (this.walls[0]) {
line(x, y, x + w, y);
}
if (this.walls[1]) {
line(x + w, y, x + w, y + w);
}
if (this.walls[2]) {
line(x + w, y + w, x, y + w);
}
if (this.walls[3]) {
line(x, y + w, x, y);
}
if (this.visited) {
fill(255, 0, 255, 100);
noStroke();
rect(x, y, w, w);
}
}
};
function removeWalls(a, b) {
var x = a.i - b.i;
if (x === 1) {
a.walls[3] = false;
b.walls[1] = false;
} else if (x === -1) {
a.walls[1] = false;
b.walls[3] = false;
}
var y = a.j - b.j;
if (y === 1) {
a.walls[0] = false;
b.walls[2] = false;
} else if (y === -1) {
a.walls[2] = false;
b.walls[0] = false;
}
}
function draw() {
background(51);
for (var i = 0; i < grid.length; i++) {
grid[i].show()
}
//Step One
current.visited = true;
current.highlight();
var next = current.checkNeighbors()
if (next) {
next.visited = true;
//Step 2
stack.push(current);
check.push(current)
//Step 3
removeWalls(current, next)
{}
//Step 4
current = next;
} else if (stack.length > 0) {
current = stack.pop();
}
if (stack[0] === undefined) {
fill(0, 105, 100)
rect(0, 0, w, w)
rect(width - w, height - w, w, w)
frameRate(8)
var c = check.find(cell => cell.i == ~~(k/w) && cell.j == ~~(m/w));
console.log(c)
if(c===undefined)
I'm not sure if I needed to put in this much of the code, but I didn't want to leave anything important out.
You're definitely on the right track.
I would just finish that check to see if a cell has been found before attempting to access the walls property. For example:
if(c===undefined){
console.log('no cell found');
}else{
console.log('i',c.i,'j', c.j,'walls', c.walls);
}
as you press the arrow keys you should see different cell row/col indices and walls.
I'm trying to get the x, y coordinates of branch endpoints on a simple L-Systems tree. The idea is to create a p5.Vector(x, y) and push it to an array.
Right now, I'm able to draw ellipses marking the desired points by setting their origin to (0, -len), but I have a problem. When I try to push (0, -len) as a new p5.Vector(x, y) to an array, every single point has an x coordinate of 0, albeit with the correct y coordinate.
I know that it has something to do with translating the coordinate back to (width/2, height), but I'm just not able to figure out the correct calculation. I've even tried tan(angle) * (y1 - y2) but it's not quite right. TIA!
var axiom = 'F';
var sentence = axiom;
var len = 300;
var count = 0;
var flowerArr = [];
var rules = [];
rules[0] = {
a: 'F',
b: 'G[+F][-F]GF'
};
rules[1] = {
a: 'G',
b: 'GG'
};
function setup() {
createCanvas(window.innerWidth, window.innerHeight);
stroke(10);
smooth();
turtle();
}
function turtle() {
background(255);
strokeWeight(1);
angle = radians(Math.random() * (25 - 15) + 15);
resetMatrix();
translate(width / 2, height);
for (var i = 0; i < sentence.length; i++) {
var current = sentence.charAt(i);
var randomSeed = 2;
if (current == 'F' || current == 'G') {
ellipse(0, -len, 5);
line(0, 0, 0, -len);
translate(0, -len);
} else if (current == '+') {
let positiveRotation = angle * Math.random() * randomSeed;
rotate(positiveRotation);
} else if (current == '-') {
let negativeRotation = -angle * Math.random() * randomSeed;
rotate(negativeRotation);
} else if (current == '[') {
push();
} else if (current == ']') {
pop();
count++;
}
}
if (i >= sentence.length) {
finished = true;
console.log("done", count);
}
}
function generateStems(iterations) {
for (i = iterations - 1; i > 0 ; i--) {
branch();
}
}
function branch() {
len *= Math.random() * (.52 - .45) + .45;
var nextSentence = '';
for (var i = 0; i < sentence.length; i++) {
var current = sentence.charAt(i);
var found = false;
for (var j = 0; j < rules.length; j++) {
if (current == rules[j].a) {
found = true;
nextSentence += rules[j].b;
break;
}
}
if (!found) {
nextSentence += current;
}
}
sentence = nextSentence;
turtle();
}
function draw() {
generateStems(4);
noLoop();
}
As far as I know, at the moment, p5.js support for vector/matrix operations and coordinate space conversion isn't quite there yet.
In theory you could manually keep track of every single transformation (translate/rotate) and manually compute it to get the transformed positions, howeve, in practice this may be error prone and cumbersome.
In Processing you could rely on PMatrix's mult(PVector) method to transform a point from one coordinate system to another, but not in p5.js at the moment.
Functions like screenX()/screenY() simplify this even further.
Here's a basic example (note the usage of P3D):
PVector v1 = new PVector();
float len = 100;
void setup(){
size(300,300,P3D);
noFill();
strokeWeight(3);
}
void draw(){
background(255);
// isolate coordinate system
pushMatrix();
// apply a set of transformations
translate(width / 2, height);
translate(0,-len);
rotate(radians(45));
// draw a blue rectangle from the corner to illustrate this transformed position
stroke(0,0,192);
rect(0,0,30,30);
// further transform
translate(90,0);
// draw a rect rectangle
stroke(192,0,0);
rect(0,0,30,30);
// use screenX/screenY to calculate the transformed coordinates
v1.set(screenX(0,0,0),screenY(0,0,0));
popMatrix();
// simply draw a (green) circle on top at the same transformed coordinates, without being in that local coordinate system
stroke(0,192,0);
ellipse(v1.x, v1.y, 30, 30);
}
At the moment, for practical reasons, if computing the transformed locations is a must, I would recommend porting your code to Processing.
Update Based on your comment it is easier to use the L-System to introduce a new rule for the flower.
Let's say * represents a flower, you could modify your rule to include it for example as the last instruction: b: 'G[+F][-F]GF' becomes b: 'G[+F][-F]GF*'
then it's just a matter of handling that symbol as you traverse the current sentence:
var axiom = 'F';
var sentence = axiom;
var len = 300;
var count = 0;
var flowerArr = [];
var rules = [];
rules[0] = {
a: 'F',
b: 'G[+F][-F]GF*'
};
rules[1] = {
a: 'G',
b: 'GG'
};
function setup() {
createCanvas(630, 630);
stroke(10);
noFill();
smooth();
turtle();
}
function turtle() {
background(255);
strokeWeight(1);
angle = radians(Math.random() * (25 - 15) + 15);
resetMatrix();
translate(width / 2, height);
for (var i = 0; i < sentence.length; i++) {
var current = sentence.charAt(i);
var randomSeed = 2;
if (current == 'F' || current == 'G') {
ellipse(0, -len, 5);
line(0, 0, 0, -len);
translate(0, -len);
// flower rule
} else if (current == '*') {
flower(6,len * 0.618033);
} else if (current == '+') {
let positiveRotation = angle * Math.random() * randomSeed;
rotate(positiveRotation);
} else if (current == '-') {
let negativeRotation = -angle * Math.random() * randomSeed;
rotate(negativeRotation);
} else if (current == '[') {
push();
} else if (current == ']') {
pop();
count++;
}
}
if (i >= sentence.length) {
finished = true;
// console.log("done", count);
}
}
function flower(sides, sideLength){
beginShape();
let angleIncrement = TWO_PI / sides;
for(let i = 0 ; i <= sides; i++){
vertex(cos(angleIncrement * i) * sideLength,
sin(angleIncrement * i) * sideLength);
}
endShape();
}
function generateStems(iterations) {
for (i = iterations - 1; i > 0 ; i--) {
branch();
}
}
function branch() {
len *= Math.random() * (.52 - .45) + .45;
var nextSentence = '';
for (var i = 0; i < sentence.length; i++) {
var current = sentence.charAt(i);
var found = false;
for (var j = 0; j < rules.length; j++) {
if (current == rules[j].a) {
found = true;
nextSentence += rules[j].b;
break;
}
}
if (!found) {
nextSentence += current;
}
}
sentence = nextSentence;
turtle();
}
function draw() {
generateStems(5);
noLoop();
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.0.0/p5.min.js"></script>
As further explorations here are a couple of fun JS implementations of L-system to play with:
https://jsxgraph.uni-bayreuth.de/wiki/index.php/L-systems
http://www.plastaq.com/elsy/
Can someone please help me with what I'm doing wrong here. I'm completely new to programming, and am currently learning p5js at university. I'm trying to create 'cells' floating around in space, and when they hit each other they combine together. It works for a minute but then I get an error
"Uncaught TypeError: Cannot read property 'eats' of undefined (sketch: line 76)"
This is my code:
function Cell(x, y, r) {
this.x = x;
this.y = y;
this.r = r;
this.speedx = random(-1, 1);
this.speedy = random(-1, 1);
this.width2 = 0.5;
this.col = color(255);
this.show = function() {
fill(this.col);
ellipse(this.x, this.y, this.r * 2);
}
this.move = function() {
this.x = this.x + this.speedx;
this.y = this.y + this.speedy;
}
this.bounce = function() {
if (this.x > width || this.x < 0) {
this.speedx = -this.speedx;
}
if (this.y > height || this.y < 0) {
this.speedy = -this.speedy;
}
}
// this.changeColor = function (){
//this.col = color(random(255), random (255), random(255));
//}
this.eats = function(other) {
var d = dist(this.x, this.y, other.x, other.y);
if (d < this.r + other.r) {
var sum = PI * this.r * this.r + PI * other.r * other.r;
this.r = sqrt(sum / PI);
//this.r += other.r;
return true;
} else {
return false;
}
}
// this.intersects = function(other){
// var d= dist(this.x, this.y, other.x, other.y);
//if (d < this.r + other.r) {
//var sum = PI * this.r * this.r + PI * other.r * other.r;
//this.r = sqrt(sum / PI);
//this.r += other.r;
//return true;
//} else {
//return false;
//}
//}
}
var cells = [];
function setup() {
createCanvas(600, 600);
for (var i = 0; i < 50; i++) {
cells[i] = new Cell(random(width), random(height), 10);
}
}
function draw() {
background(0);
for (var i = cells.length - 1; i >= 0; i--) {
cells[i].show();
cells[i].move();
cells[i].bounce();
for (var j = cells.length - 1; j >= 0; j--) {
if (j != i && cells[i].eats(cells[j])) {
// cells[i].changeColor();
// cells[j].changeColor();
cells.splice(j, 1);
}
}
}
}
You are removing cells from cells array using cells.splice(j, 1);. So when it checks for that on next loop, its throwing error.
How can I generate a number between 1 and 9 using a function which generate a number between 1 and 3? The probability to obtain any number between 1 and 9 must be the same, so rand3()+rand3()+rand3() is not a good solution.
Try cartesian-like product:
Rand9() = 3 * (Rand3() - 1) + Rand3()
With 3 * (Rand3() - 1) you make sub-intervals 1-3, 4-6 and 7-9 equally likely. With + Rand3() you will then choose equally on that sub-interval.
Written as a product:
3 * (Rand3() - 1) + Rand3() -> {1, 4, 7} X {+1,+2,+3} -> {1,2,3,4,5,6,7,8,9}
The main idea of the solution is to build 3*3 matrix.
[
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
]
function init() {
const rand3 = generateMathRandom(3);
let randFunc = rand3;
const start = 9;
const end = 10;
let i = start;
console.time('---start');
while (i < end) {
randFunc = generateRandom(i, 3, rand3);
healthCheck(i, randFunc);
i++;
}
console.timeEnd('---start');
i = start;
console.time('---start-math');
while (i < end) {
randFunc = generateMathRandom(i);
healthCheck(i, randFunc);
i++;
}
console.timeEnd('---start-math');
}
function generateMathRandom(range) {
return function () {
return Math.floor(Math.random() * range);
}
}
// range - target random range number : e.g 9
// lowerRange - base random range number : e.g 3
// func - function which generate random with the range of lowerRange : e.g rand3
function generateRandom(range, lowerRange, func) {
return function () {
const results = [];
let times = 1;
let matRange = lowerRange;
let mat = Math.sqrt(range);
while(mat > lowerRange) {
mat = Math.sqrt(mat);
times ++;
matRange *= lowerRange;
}
const noneStart = Math.floor(matRange * matRange / range) * range;
for (let i = 0; i < matRange; i++) {
results.push([]);
for (let j = 0; j < matRange; j++) {
const num = i * matRange + j;
if (num < noneStart)
results[i].push(num % range);
else
results[i].push(-1);
}
}
while (true) {
const row = new Array(times).fill(1).map(n => func()).reduce((a, c) => a * lowerRange + c, 0);
const col = new Array(times).fill(1).map(n => func()).reduce((a, c) => a * lowerRange + c, 0);
if (!results[row]) {
debugger;
}
if (results[row][col] == undefined) {
debugger;
}
if (results[row][col] === -1) continue;
return results[row][col];
}
}
}
function healthCheck(range, func = null, count = 100000) {
const funcToCheck = func || rand[range] || null;
if (!funcToCheck) {
console.log('Function is not provided');
return;
}
const health = new Array(range).fill(0);
const checkCount = count - count % range; // To do correct check.
for (let i = 0; i < checkCount; i++) {
health[funcToCheck()]++;
}
const result = health.map((cnt, index) => ({
_value: index,
rate: (cnt / checkCount * 100).toFixed(5)
}));
// console.log('Random result:', result);
let minRate = 100, maxRate = 0;
for (let i = 0; i < range; i++) {
minRate = Math.min(Number(result[i].rate), minRate);
maxRate = Math.max(Number(result[i].rate), maxRate);
}
console.log('Random health<' + range + '>: ' + healthOutput(range, maxRate - minRate));
}
function healthOutput(range, rangeOffset) {
const healthThreshold = 2; // It should be lower for the best Alghorithms
const threshold = healthThreshold;
if (rangeOffset < threshold * 0.2) {
return 'The Super! -> ' + rangeOffset.toFixed(5);
} else if (rangeOffset < threshold * 0.5) {
return 'The Best! --> ' + rangeOffset.toFixed(5);
} else if (rangeOffset < threshold * 1) {
return 'Good work! -> ' + rangeOffset.toFixed(5);
} else if (rangeOffset < threshold * 2) {
return 'Not bad! ---> ' + rangeOffset.toFixed(5);
}
return 'Worst! -----> ' + rangeOffset.toFixed(5);
}
init();