how to calculate the number to increment the variable - javascript

How can I calculate the missing number, so that when I add it to a certain variable the result will be equal to or greater than the other variable.
Promoter: 10
Detractor: 2
Total: 12
Average: 66.67
Target: 75
You need ??? Promoters to reach your Target.
I want to find how many Promoters I need if the Average is less than the Target. How can I calculate the missing number so that when I add it to the Promoter the results of the Average is equal to or greater than the Target.
Thank You!
function missingNum() {
var xPromoter = '';
var x = 10;
var y = 2;
var target = 75;
var z = x + y;
var v = ((x - y) / z) * 100;
average = Math.round(v * 100) / 100;
if (average<target) //how to increment x so that average => target
document.write("Promoter:" + "\n" + x + "<br>");
document.write("Detractor:" + "\n" + y + "<br>" );
document.write("Total:" + "\n" + z + "<br>" );
document.write("Average:" + "\n" + average + "<br>" );
document.write("Target:" + "\n" + target + "<br>" );
document.write("You need " + "\n" + xPromoter + "Promoters to reach your Target." );
}

This is a simple problem of algebra.
Average = ((Promoters - Detractors) / Total) * 100
Let fp be the final number of promotors you're looking for
Let d be the current number of Detractors
Let t be your target Average
The formula using these variable to find your target starts as such:
((fp - d) / (fp + d)) * 100 = t
Divide by 100
(fp - d) / (fp + d) = t / 100
Multiply both sides by (fp + d)
fp - d = (t / 100) * (fp + d)
Distribute t / 100
fp - d = (t / 100) * fp + (t / 100) * d
Add d to both sides
fp = (t / 100) * fp + (t / 100) * d + d
Subtract (t / 100) * x from both sides
fp - (t / 100) * fp = (t / 100) * d + d
Simplify the lefthand side
(1 - t / 100) * fp = (t / 100) * d + d
You want to have only fp on the lefthand side, so divide both sides by (1 - t / 100)
fp = ((t / 100) * d + d) / (1 - t / 100)
This is the formula you will use to find the total number of promotors you need
Now just plug in the numbers for t and d and solve for x
fp = ((75 / 100) * 2 + 2) / (1 - 75 / 100)
Writing this all out so you can see the final result:
fp = (.75 * 2 + 2) / (1 - .75)
fp = (1.5 + 2) / .25
fp = 3.5 / .25
fp = 14
Using the variables in your program
var fp = ((target / 100) * y + y) / (1 - target / 100);
xPromoter = fp - x;

Forget looping and do some math! Doing some algebra, the formula for needed promoters, given the target and current number of detractors and promoters, is:
promotersNeeded = (detractors * (target + 100) / (100 - target)) - promoters
So in your example,
promotersNeeded = 2 * (75 + 100) / (100 - 75) - 10
which comes out to 4.

You could always make a simple function.. however a calculation might be better..
function calculateNeededPromoters (promo, detrac) {
var percentage = 0.00;
var currentPromo = promo;
while (percentage < 75) {
currentPromo++;
var total = currentPromo + detrac;
var v = ((currentPromo - detrac) / total) * 100;
percentage = Math.round(v * 100) / 100;
}
return currentPromo;
}

Related

How to plot an ellipse on canvas from 2 points on the ellipse, where slope of major axis (rx), and minor axis (ry) length are unknown

This may be more of a mathematics problem, but maybe there is a simple javascript solution that I am missing.
I want to plot an ellipse on html canvas from user input of a center point, radius of the major (longest) axis, and 2 points will fall on the ellipse.
This should potentially create 2 possible ellipse paths, both of which will center around the center point, and cross through the 2 points.
So for example, if the center = [2, 1] major axis radius a = 10, point 1 u = [4, 2] and point 2 v = [5, 6], what is the minor axis radius b and angle of rotation theta?
So far I have tried to implement an equation that I found from https://math.stackexchange.com/questions/3210414/find-the-angle-of-rotation-and-minor-axis-length-of-ellipse-from-major-axis-leng,
but it does not return valid values. My javascript code looks like this:
function getEllipseFrom2Points(center, u, v, a) {
function getSlope(plusOrMinus) {
return Math.sqrt(((uy * vx - ux * vy) ** 2) / (-ux * uy * (a * (v2x + v2y) - 1) + vx * vy * (a * (u2x + u2y) - 1) - plusOrMinus * (uy * vx - ux * vy) * q) / (u2x * (1 - a * v2y) + v2x * (a * u2y - 1)));
}
function getMinorAxis(plusOrMinus) {
return (u2x + u2y + v2x + v2y - a * (2 * u2x * v2x + 2 * u2y * v2y + 2 * ux * uy * vx * vy + u2y * v2x + u2x * v2y) + plusOrMinus * 2 * (ux * vx + uy * vy) * q);
}
var vx = v[0],
vy = v[1],
ux = u[0],
uy = u[1],
v2x = vx ** 2,
v2y = vy ** 2,
u2x = ux ** 2,
u2y = uy ** 2,
q = Math.sqrt((1 - a * (u2x + u2y)) * (1 - a * (v2x + v2y))),
ellipse1 = { rx: a, ry: getMinorAxis(1), origin: center, rotation: getSlope(1) },
ellipse2 = { rx: a, ry: getMinorAxis(-1), origin: center, rotation: getSlope(-1) };
}
Either the equation that I am following is wrong, or I have implemented it wrong
In case anyone is interested, here is my solution to the problem, which isn't really "the" solution. If anyone can solve this I would still be happy to know.
Since I can't solve for both slope of the major axis and length of the minor axis, I just take a guess at slope and then test how close it is, and then refine the result by trying in a smaller and smaller region. Since the final ellipse that gets drawn is actually an estimation constructed from bezier curves, I can get close enough in a reasonable amount of time.
function getEllipseFrom2Points (center, u, v, a) {
function getSemiMinorAxis([x, y], a, t) {
// equation for rotated ellipse
// b = a(ycos(t) - xsin(t)) / sqrt(a^2 - x^2cos^2(t) - 2xysin(t)cos(t) - y^2sin^2(t)) and
// b = a(xsin(t) - ycos(t)) / sqrt(a^2 - x^2cos^2(t) - 2xysin(t)cos(t) - y^2sin^2(t))
// where a^2 !== (xcos(t) + ysin(t))^2
// and aycos(t) !== axsin(t)
if (a ** 2 !== (x * Math.cos(t) + y * Math.sin(t)) ** 2 &&
a * y * Math.cos(t) !== a * x * Math.sin(t)) {
var b = [],
q = (Math.sqrt(a ** 2 - x ** 2 * (Math.cos(t)) ** 2 - 2 * x * y * Math.sin(t) * Math.cos(t) - y ** 2 * (Math.sin(t)) ** 2));
b[0] = (a * (y * Math.cos(t) - x * Math.sin(t))) / q;
b[1] = (a * (x * Math.sin(t) - y * Math.cos(t))) / q;
return b;
}
}
function getAngle_radians(point1, point2){
return Math.atan2(point2[1] - point1[1], point2[0] - point1[0]);
}
function getDistance(point1, point2) {
return Math.sqrt((point2[0] - point1[0]) ** 2 + (point2[1] - point1[1]) ** 2);
}
function rotatePoint(point, center, radians) {
var x = (point[0] - center[0]) * Math.cos(radians) - (point[1] - center[1]) * Math.sin(radians) + center[0];
var y = (point[1] - center[1]) * Math.cos(radians) + (point[0] - center[0]) * Math.sin(radians) + center[1];
return [x, y];
}
function measure(ellipseRotation, pointOnEllipse, minorAxisLength) {
var d = getDistance(point, pointOnEllipse);
if (d < bestDistanceBetweenPointAndEllipse) {
bestDistanceBetweenPointAndEllipse = d;
bestEstimationOfB = minorAxisLength;
bestEstimationOfR = ellipseRotation;
}
}
function getBestEstimate(min, max) {
var testIncrement = (max - min) / 10;
for (let r = min; r < max; r = r + testIncrement) {
if (radPoint1 < r && radPoint2 < r || radPoint1 > r && radPoint2 > r) {//points both on same side of ellipse
semiMinorAxis = getSemiMinorAxis(v, a, r);
if (semiMinorAxis) {
for (let t = 0; t < circle; t = t + degree) {
ellipsePoint1 = [a * Math.cos(t), semiMinorAxis[0] * Math.sin(t)];
ellipsePoint2 = [a * Math.cos(t), semiMinorAxis[1] * Math.sin(t)];
point = rotatePoint(u, [0, 0], -r);
measure(r, ellipsePoint1, semiMinorAxis[0]);
measure(r, ellipsePoint2, semiMinorAxis[1]);
}
}
}
}
count++;
if (new Date().getTime() - startTime < 200 && count < 10) //refine estimate
getBestEstimate(bestEstimationOfR - testIncrement, bestEstimationOfR + testIncrement);
}
if (center instanceof Array &&
typeof center[0] === "number" &&
typeof center[1] === "number" &&
u instanceof Array &&
typeof u[0] === "number" &&
typeof u[1] === "number" &&
v instanceof Array &&
typeof v[0] === "number" &&
typeof v[1] === "number" &&
typeof a === "number") {
// translate points
u = [u[0] - center[0], u[1] - center[1]];
v = [v[0] - center[0], v[1] - center[1]];
var bestDistanceBetweenPointAndEllipse = a,
point,
semiMinorAxis,
ellipsePoint1,
ellipsePoint2,
bestEstimationOfB,
bestEstimationOfR,
radPoint1 = getAngle_radians([0, 0], v),
radPoint2 = getAngle_radians([0, 0], u),
circle = 2 * Math.PI,
degree = circle / 360,
startTime = new Date().getTime(),
count = 0;
getBestEstimate(0, circle);
var ellipseModel = MakerJs.$(new MakerJs.models.Ellipse(a, bestEstimationOfB))
.rotate(MakerJs.angle.toDegrees(bestEstimationOfR), [0, 0])
.move(center)
.originate([0, 0])
.$result;
return ellipseModel;
}

Calculate the x and y based on bearing

I have a screen where I would like to calculate the next x and y based on angle.
The first footstep is this example starts at Step 1.
How can I calculate the next footstep where I want to increase going forward by 120 and the side step needs to weave in and out of about 60.
Please bear in mind that the starting point could be x = 100, y = 100 with the angle being 180 so the footsteps would have to then be going up the y axis.
I've attempted the following Javascript but the footsteps seem to get confused:
this.startingFootPrintX = Math.floor(Math.random() * 1000) + 20; //Random number between 20 and 1000
this.startingFootPrintY = Math.floor(Math.random() * 560) + 20; //Random number between 20 and 560
this.startingAngle = Math.floor(Math.random() * 340) + 20; //Random number between 20 and 340
startFootSteps();
startFootSteps(){
console.log(this.startingFootPrintX);
console.log(this.startingFootPrintY);
this.startingFootPrintX = Math.round(Math.cos(this.startingAngle * Math.PI / 180) * 120 + this.startingFootPrintX);
this.startingFootPrintY = Math.round(Math.sin(this.startingAngle * Math.PI / 180) * 60 + this.startingFootPrintY);
setInterval(function () {
startFootSteps();
}, 3000);
}
Diagram:
The step direction (black line) is given by (cos θ, sin θ). The step offset direction (small blue lines) is given by (sin θ, -cos θ)
The position recurrence is given by:
s determines which side of the black line the next footprint is on, i.e. -1 for the left foot and +1 for the right.
If you know the starting position c0 and the starting foot s0, a closed-form solution is given by:
This alternates between both feet for every step.
In your diagram example the parameters are w = 60, d = 120, θ = 40°, c0 = (96, 438), s0 = -1 (starting with left foot).
UPDATE: JavaScript code snippet
this.startingPosX = Math.floor(Math.random() * 1000) + 20;
this.startingPosY = Math.floor(Math.random() * 560) + 20;
this.startingAngle = Math.floor(Math.random() * 340) + 20;
this.startingFoot = 1 - 2 * Math.round(Math.random()); // select which foot to start with
this.stepSize = 120;
this.stepWidth = 60;
footsteps(0);
footsteps(n) {
// should actually pre-calculate these outside but oh well
let a = this.startingAngle * Math.PI / 180;
let c = Math.cos(a), s = Math.sin(a);
// current foot
let d = this.startingFoot * (1 - 2 * (n % 2));
// apply equations
this.footprintX = Math.round(
this.startingPosX + // initial
this.stepSize * n * c + // step
this.stepWidth * 0.5 * d * s // foot offset
);
this.footprintY = Math.round(
this.startingPosY + // initial
this.stepSize * n * s - // step
this.stepWidth * 0.5 * d * c // foot offset
);
// draw the foot here
console.log(this.footprintX);
console.log(this.footprintY);
// increment the step counter for the next call
setInterval(function() { footsteps(n+1); }, 3000);
}

Centering grid of objects

As in the title, im trying to create a grid of objects in P5 Spot(x, y, size), with a 4 pixel space between them and center it on the canvas without using translate, heres what i've got:
gridSize = 7;
spotSize = 60;
spots = [];
for (var y = height / 2 - ((gridSize * spotSize + gridSize * 4) / 2); y < (height / 2 - ((gridSize * spotSize + gridSize * 4) / 2)) + (gridSize * spotSize + gridSize * 4); y += spotSize + 4) {
for (var x = width / 2 - ((gridSize * spotSize + gridSize * 4) / 2); x < (width / 2 - ((gridSize * spotSize + gridSize * 4) / 2)) + (gridSize * spotSize + gridSize * 4); x += spotSize + 4) {
spots.push(new Spot(x, y, spotSize));
}
}
Problem is that my grid looks off, why is it not centered? Probably a really simple and stupid mistake but i cant find it. Any help appreciated.
My Spot object just draws an ellipse at the given x and y. Entire code at http://codepen.io/felipe_mare/pen/GWyMOL
-SOLVED-
spots.push(new Spot(x + spotSize/2, y + spotSize/2, spotSize));
Wasn't taking into account the fact that the ellipse is drawn from the center, so i have to add the radius of the circle spotSize/2

Iteration vs Recursion: Calculate point position in sequence for known iteration

I have a recursive function that takes a point {x,y} and calculates the next point in the sequence, recursively.
It looks a little like this:
var DECAY = 0.75;
var LENGTH = 150;
var ANGLE = 0.52;
getNextPoint(0, 0, ANGLE, LENGTH);
function getNextPoint (x, y, a, l) {
l *= DECAY;
a += ANGLE;
var x1 = x - Math.cos(a) * l;
var y1 = y - Math.sin(a) * l;
//We now have 2 points, draw lines etc.
getNextPoint(x1, y1, a, l);
}
How can I calculate a point (or 2 consecutive points) given a known iteration?
I know that the angle and length values for a given iteration could be calculated fairly easily with something like the following:
var a = ANGLE * iteration;
var l = LENGTH * Math.pow(DECAY, iteration);
But I would still need to know the position of the point at iteration - 1 to apply these values to?
Think of this as complex numbers. z = x + i*y is your point. b = cos(a)*l + i*sin(a)*l is some parameter, and c = cos(ANGLE)*DECAY + i*sin(ANGLE)*DECAY is a constant.
Initially you have z0 = 0 and b0 = c*LENGTH/DECAY. In each recursion you do
b(k+1) = b(k)*c
z(k+1) = z(k) - b
So you have
b1 = b0*c = c^2*LENGTH/DECAY
z1 = z0-b1 = -b1 = -c^2*LENGTH/DECAY
b2 = b1*c = c^3*LENGTH/DECAY
z2 = z1-b2 = -(c^2+c^3)*LENGTH/DECAY
⋮
zn = -(c^2+c^3+⋯+c^(n+1))*LENGTH/DECAY
If you ask Wolfram Alpha it will tell you that
c^2+c^3+⋯+c^(n+1) = c^2*(c^n - 1)/(c - 1)
You can make the denominator real if you multiply by the complex conjugate. Then you can turn the whole thing back into a formula for real numbers. So let's write
c = cr + i*ci cr = cos(ANGLE)*DECAY ci = sin(ANGLE)*DECAY
d = c^n = dr + i*di dr = cos(n*ANGLE)*pow(DECAY, n) di = …
Then we have
c^2*(d - 1)*(cr - i*ci - 1)/((cr + i*ci - 1)*(cr - i*ci - 1))
= ((cr + i*ci)*(cr + i*ci)*(dr + i*di - 1)*(cr - i*ci - 1)) /
((cr - 1)*(cr - 1)*ci*ci)
= ((cr^3*dr + cr*ci^2*dr - cr^2*ci*di - ci^3*di - cr^3 - cr*ci^2
- cr^2*dr + ci^2*dr + 2*cr*ci*di + cr^2 - ci^2) +
(cr^2*ci*dr + ci^3*dr + cr^3*di + cr*ci^2*di - cr^2*ci - ci^3
- 2*cr*ci*dr - cr^2*di + ci^2*di + 2*cr*ci))/((cr - 1)*(cr - 1)*ci*ci)
xn = -(cr^3*dr + cr*ci^2*dr - cr^2*ci*di - ci^3*di - cr^3 - cr*ci^2
- cr^2*dr + ci^2*dr + 2*cr*ci*di + cr^2 - ci^2) /
((cr - 1)*(cr - 1)*ci*ci) * LENGTH / DECAY
yn = -(cr^2*ci*dr + ci^3*dr + cr^3*di + cr*ci^2*di - cr^2*ci - ci^3
- 2*cr*ci*dr - cr^2*di + ci^2*di + 2*cr*ci) /
((cr - 1)*(cr - 1)*ci*ci) * LENGTH / DECAY
The expansions of the numerator came out of my CAS; it might well be that you can write this somewhat shorter, but I don't feel like multiplying those four terms manually just to try that.
Here is a working example to demonstrate all of this:
var ctxt = document.getElementById("MvG1").getContext("2d");
var sin = Math.sin, cos = Math.cos, pow = Math.pow;
var DECAY = 0.75;
var LENGTH = 150;
var ANGLE = 0.52;
var cr = cos(ANGLE)*DECAY, ci = sin(ANGLE)*DECAY;
var cr2 = cr*cr, ci2 = ci*ci, cr3 = cr2*cr, ci3 = ci2*ci;
var f = - LENGTH / DECAY / ((cr - 1)*(cr - 1)*ci*ci)
ctxt.beginPath();
ctxt.moveTo(100,450);
for (var n = 0; n < 20; ++n) {
var da = pow(DECAY, n), dr = cos(n*ANGLE)*da, di = sin(n*ANGLE)*da;
var xn, yn;
xn = (cr3*dr + cr*ci2*dr - cr2*ci*di - ci3*di - cr3 - cr*ci2
- cr2*dr + ci2*dr + 2*cr*ci*di + cr2 - ci2)*f;
yn = (cr2*ci*dr + ci3*dr + cr3*di + cr*ci2*di - cr2*ci - ci3
- 2*cr*ci*dr - cr2*di + ci2*di + 2*cr*ci)*f;
console.log([xn,yn]);
ctxt.lineTo(0.1*xn + 100, 0.1*yn + 450);
}
ctxt.stroke();
<canvas id="MvG1" width="300" height="500"></canvas>

Javascript Calculate darker colour

how can i change this calculation to a darker and not brighter color?
function increase_brightness(hex, percent){
// strip the leading # if it's there
hex = hex.replace(/^\s*#|\s*$/g, '');
// convert 3 char codes --> 6, e.g. `E0F` --> `EE00FF`
if(hex.length == 3){
hex = hex.replace(/(.)/g, '$1$1');
}
var r = parseInt(hex.substr(0, 2), 16),
g = parseInt(hex.substr(2, 2), 16),
b = parseInt(hex.substr(4, 2), 16);
return '#' +
((0|(1<<8) + r + (256 - r) * percent / 100).toString(16)).substr(1) +
((0|(1<<8) + g + (256 - g) * percent / 100).toString(16)).substr(1) +
((0|(1<<8) + b + (256 - b) * percent / 100).toString(16)).substr(1);
}
src = JavaScript Calculate brighter colour
demo http://jsbin.com/utavax/3/edit
You can change
return '#' +
((0|(1<<8) + r + (256 - r) * percent / 100).toString(16)).substr(1) +
((0|(1<<8) + g + (256 - g) * percent / 100).toString(16)).substr(1) +
((0|(1<<8) + b + (256 - b) * percent / 100).toString(16)).substr(1);
to
return '#' +
((0|(1<<8) + r * (1 - percent / 100)).toString(16)).substr(1) +
((0|(1<<8) + g * (1 - percent / 100)).toString(16)).substr(1) +
((0|(1<<8) + b * (1 - percent / 100).toString(16)).substr(1);
will fix your problem. demo.
But darker color is not a good definition. Darker can be interpreted as less brightness or less saturation. So the better approach is to convert the RGB color space to HSB color space, and tweak the S/B channels, then convert them back.
A little explanation on why negative value to original code is not OK.
Give -100 as percent, and some channel, say r, less than 128. Then
r + (256 - r) * percent / 100
is less than 0, after plus 1 << 8 = 256
((0|(1<<8) + r + (256 - r) * percent / 100)
is less than 256.
Take a number less than 256 will generate at most two hex digits by calling toString(16), so .substr(1) will contain 0 or 1 digit only. By combining all these wrong digits together will not generate a proper color in hex representation.
I updated your original function to do a cheap version of lightening/darkening by basically multiplying a percentage (up or down) off the original RGB values.
function adjust(hexInput: string, percent: number) {
let hex = hexInput;
// strip the leading # if it's there
hex = hex.replace(/^\s*#|\s*$/g, "");
// convert 3 char codes --> 6, e.g. `E0F` --> `EE00FF`
if (hex.length === 3) {
hex = hex.replace(/(.)/g, "$1$1");
}
let r = parseInt(hex.substr(0, 2), 16);
let g = parseInt(hex.substr(2, 2), 16);
let b = parseInt(hex.substr(4, 2), 16);
const calculatedPercent = (100 + percent) / 100;
r = Math.round(Math.min(255, Math.max(0, r * calculatedPercent)));
g = Math.round(Math.min(255, Math.max(0, g * calculatedPercent)));
b = Math.round(Math.min(255, Math.max(0, b * calculatedPercent)));
return `#${r.toString(16).toUpperCase()}${g.toString(16).toUpperCase()}${b
.toString(16)
.toUpperCase()}`;
}
console.log(adjust("#49D174", -14)) // Darken by 14% = #3FB464
console.log(adjust("#49D174", -27)) // Darken by 27% = #359955
The function takes the percent as an integer, but could easily be modified for a decimal. Negative to darken, positive to lighten.
I've taken the main idea from Stylus (1, 2) and a few code parts from Stack Overflow answers and produced a library, darken_color.js.
Here are a few usage examples:
darken('lightgreen', '0.1'); // -> #63e763
darken('lightgreen', '10'); // -> #63e763
darken('#90EE90', '10'); // -> #63e763
darken('#9e9', '10%'); // -> #98ed98
return '#' +
((0|(1<<8) + r + (256 - r) * percent / 100).toString(16)).substr(1) +
((0|(1<<8) + g + (256 - g) * percent / 100).toString(16)).substr(1) +
((0|(1<<8) + b + (256 - b) * percent / 100).toString(16)).substr(1);
Here you are adding some percentage of r, g and b to current values which makes the color lighter. If you use a negative value for percentage, it will decrease from current values and make the color darker.

Categories