How to lower one number when the other rises? - javascript

I take a value from the mouse (mouseX) position in Processing, as this value gets higher, I want the amount of boxes that are rendered to lower in steps of 5 (rotStep).
I did a lot of reading and found out the method I should use is called 'Negative correlation' or at least I think it is. I have never had high-grade math so I'm pretty much in the dark here. Maybe there is a function that already exists to do this. After a lot of googling I came in here to ask.
Tried dividing the mouseX input by itself and some other random sums but it seems this might be more complicated than I anticipated.
I am trying to get into generative art generation and could use a hint to get further with my attempt of rendering more boxes (quads) as the mouseX value lowers.
void setup() {
pixelDensity(displayDensity());
size(500, 500);
background(0);
noFill();
stroke(255);
}
void draw() {
translate(width/2, height/2);
ellipse(0, 0, 50, 50);
background(0);
mouseX= constrain(mouseX, 1, width);
mouseY= constrain(mouseY, 1, height);
float rotationMax = 90;
float rotStep = (mouseX/15)+5;
//I need to add a negative correlation so the number
//of squares lowers as the mouseX position gets higher
//and all this in steps of 5
float quadSize = mouseX;
float qs = quadSize;
for (float i=0; i<rotationMax; i+=rotStep) {
float deg = rotStep;
float rad = radians(deg);
stroke(255);
strokeWeight(1);
rotate(rad);
quad(-qs, -qs, qs, -qs, qs, qs, -qs, qs);
}
}
The rotStep variable should decrease when the mouseX variable increases and vice versa. The variable rotStep should also increase or decrease in steps of 5.

The best advice I can give you is to get out a piece of paper and a pencil, and draw a table of mouse positions and the number of boxes you want. It might look like this:
mouseX boxes
---------------
0 | 50
100 | 40
200 | 30
300 | 20
400 | 10
500 | 0
This is just an example, so your numbers would probably be different. But the idea is to have a general mapping of mouseX to the number of boxes you want to draw.
Once you have that, then you can try to find an equation that gets you from mouseX to your box count. That might be a single equation, or it might involve if statements to bucket values together.
You can get a "negative correlation" by subtracting from the maximum possible value, or by using mouseX as a divisor.
float reverseMouseX = width - mouseX;
float inverseMouseX = 1 / mouseX;
For both of these approaches, as mouseX increases, the value of the variable will decrease. Then you can use these values in your equation or in your if statement logic.
To get to the example table I showed above, I might do something like this:
int boxes = (width - mouseX) / 10;
This is a general approach, but you can apply it to your goal to come up with a specific solution.
Good luck!

Related

Drag Positioning math

I'm trying to implement a feature where you can drag on your screen to change the Position of an Object.
Right now the object is moving similarly to the change of the mouseX.
What I want is the further down on the screen you drag your mouse, the slower the object moves.
I'm pretty bad at maths so i dont really now how to achieve that in a good way.
Right now I'm doing it like that
factor = Math.abs(e.deltaY)/4;
this.newX = this.currentX + (e.deltaX / factor);
currentX is the start X value of the object.
No idea if it is clear what i want to achieve or if i provided all information, but any help is appreciated!
Your arithmetics always makes step equal to 4.
Consider using some function like exponent. For example,
factor = Exp(- k * Abs(deltaX))
If you want to provide factor 1 at close distances and factor halves per every 100 pixels (I've got arbitrary reasonable values), then
0.5 = exp( - k * 100)
ln(0.5) = -k * 100
k = - ln(0.5) / 100 ~= 0.007
note that I mean multiplicative factor:
factor = Exp(- 0.007 * Abs(deltaX))
this.newX = this.currentX + (e.deltaX * factor);
Now speed will be 1 for small distances, 0.5 for 100, 0.25 for 200, 0.125 for 300 and so on.
If you want another dependence, it is possible to find appropriate function.

Subtract opacity instead of multiplying

I'm trying to make it appear as though movement on my <canvas> creates motion trails. In order to do this, instead of clearing the canvas between frames I reduce the opacity of the existing content by replacing a clearRect call with something like this:
// Redraw the canvas's contents at lower opacity. The 'copy' blend
// mode keeps only the new content, discarding what was previously
// there. That way we don't have to use a second canvas when copying
// data
ctx.globalCompositeOperation = 'copy';
ctx.globalAlpha = 0.98;
ctx.drawImage(canvas, 0, 0);
ctx.globalAlpha = 1;
ctx.globalCompositeOperation = 'source-over';
However, since setting globalAlpha multiplies alpha values, the alpha values of the trail can approach zero but will never actually reach it. This means that graphics never quite fade, leaving traces like these on the canvas that do not fade even after thousands of frames have passed over several minutes:
To combat this, I've been subtracting alpha values pixel-by-pixel instead of using globalAlpha. Subtraction guarantees that the pixel opacity will reach zero.
// Reduce opacity of each pixel in canvas
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const data = imageData.data;
// Iterates, hitting only the alpha values of each pixel.
for (let i = 3; i < data.length; i += 4) {
// Use 0 if the result of subtraction would be less than zero.
data[i] = Math.max(data[i] - (0.02 * 255), 0);
}
ctx.putImageData(imageData, 0, 0);
This fixes the problem, but it's extremely slow since I'm manually changing each pixel value and then using the expensive putImageData() method.
Is there a more performant way to subtract, rather than multiplying, the opacity of pixels being drawn on the canvas?
Unfortunately there is nothing we can do about it except from manually iterating over the pixels to clear low-value alpha pixels like you do already.
The problem is related to integer math and rounding (more details at this link, from the answer).
There are blending modes such as "luminosity" (and to a certain degree "multiply") which can be used to subtract luma, the problem is it works on the entire surface contrary to composite modes which only works on alpha - there is no equivalent in composite operations. So this won't help here.
There is also a new luma mask via CSS but the problem is that the image source (which in theory could've been manipulated using for example contrast) has to be updated every frame and basically, the performance would be very bad.
Workaround
One workaround is to use "particles". That is, instead of using a feedback-loop instead log and store the path points, then redraw all logged points every frame. Using a max value and reusing that to set alpha can work fine in many cases.
This simple example is just a proof-of-concept and can be implemented in various ways in regards to perhaps pre-populated arrays, order of drawing, alpha value calculations and so forth. But I think you'll get the idea.
var ctx = c.getContext("2d");
var cx = c.width>>1, cy = c.height>>1, r = c.width>>2, o=c.width>>3;
var particles = [], max = 50;
ctx.fillStyle = "#fff";
(function anim(t) {
var d = t * 0.002, x = cx + r * Math.cos(d), y = cy + r * Math.sin(d);
// store point and trim array when reached max
particles.push({x: x, y: y});
if (particles.length > max) particles.shift();
// clear frame as usual
ctx.clearRect(0,0,c.width,c.height);
// redraw all particles at a log. alpha, except last which is drawn full
for(var i = 0, p, a; p = particles[i++];) {
a = i / max * 0.6;
ctx.globalAlpha = i === max ? 1 : a*a*a;
ctx.fillRect(p.x-o, p.y-o, r, r); // or image etc.
}
requestAnimationFrame(anim);
})();
body {background:#037}
<canvas id=c width=400 height=400></canvas>

Detecting mouse proximity in processing.js

I am coding with processing.js. I want the size variable to get greater as the cursor (mouse) approches the ellipse and to get smaller as the cursor moves away from the ellipse. The size should (if possible) be limited between minimum 50 and maximum 200. Is there any way to accomplish that ?
I've looked online, but there doesn't seem to be lots of documentation (at least for what I was searching for) about this.
Here is my code :
void setup()
{
// Setting up the page
size(screen.width, screen.height);
smooth();
background(0, 0, 0);
// Declaring the variable size ONCE
size = 50;
}
void draw()
{
background(0, 0, 0);
// I want the size variable to be greater as the cursor approches the ellipse and to be smaller as the cursor moves away from the ellipse. The size is limited if possible between 50 and 200
// Here is the variable that needs to be changed
size = 50;
// Drawing the concerned ellipse
ellipse(width/2, height/2, size, size);
}
Thanks.
First, you need to get the distance from the mouse to the ellipse:
float distance = dist(mouseX,mouseY, width/2,height/2);
Then, you need to convert that distance into a more usable range. We'll call the result dia, since size() is also the name of a command in Processing. We also want dia to get larger as the distance gets smaller.
For both those things, we'll use map() which takes an input value, it's range, and an output range:
float dia = map(distance, 0,width/2, 200,50);
When distance is 0, dia = 200 and when distance is the width of the screen divided by 2, dia = 50.

Calculating axis length in proportion to fixed related length

Basics
I am working on a small tool that is supposed help with some geometric calculations for print-related products.
Overview
I have two inputs (w and h) where the user is supposed to enter a width and a height for a box. This box is supposed to be a representation of the users measurements as a small CSS-based box.
The problem is that I cannot just take the measurements and apply them as pixels, or even pixels * 10, or anything, as width/height for the display box, because my space is limited.
The box can have a maximum measurement of 69 x 69.
What I want to achieve is that to apply the longer entered measurement to its according axis, then calculate the other axis in proportion to this.
My approach
I am not a maths person at all. But I did my best and I put together a function that will accomplish the above:
updateRectBox: function(w, h){
// define maximum width and height for box
var max_x=69;
var max_y=69;
var factor,x,y;
factor=w/h;
if(w==h){
// if we have a 1:1 ratio, we want the box to fill `69px` on both axis
x=max_x;
y=max_y;
} else {
if(w>h){
// if width is larger than height, we calculate the box height using the factor
x=max_x;
y=(factor>1 ? max_y/factor : max_y*factor);
} else {
// if height is larger than width, we calculate the box width using the factor
x=(factor>1 ? max_x/factor : max_x*factor);
y=max_y;
}
}
// using this to set the box element's properties
jQuery('#rect').css({
'width': (x)+'px',
'height': (y)+'px'
});
}
This function works well, but:
Question
I know this can be done more beautifully, with less code. But due to my lack of math skills, I just cannot think of anything more compact than what I wrote.
I've created a working fiddle to make it easier for you to test your optimizations.
Your function accomplishes exactly what it needs to. There are ways which are arguably more elegant to write, however.
The basic idea is that you have a box with dimensions (w × h) and you want a box which is a scaled version of this one to fit in a (69 × 69) box.
To fit in a (69 × 69) box, your (w × h) box must be less than 69 wide, and less than 69 tall. Suppose you scale by the quantity s. Then your new box has dimension (s * w × s * h). Using the above constraint, we know that:
s * w <= 69 and that s * h <= 69. Rewrite these, solving for s, and you get:
s <= 69 / w and s <= 69 / h. Both must hold true, so you can rewrite this as:
s <= min( 69 / w, 69 / h). In addition, you want s to be as large as possible (so the box completely fills the region) so s = min( 69 / w, 69 / h).
Your code accomplishes the same, but through if-statements. You can rewrite it considerably terser by doing:
updateRectBox: function(width, height) {
// define maximum width and height for box
var max_width = 69;
var max_height = 69;
var scale = Math.min( max_width / width, max_height / height );
var x = scale * width;
var y = scale * height;
// using this to set the box element's properties
jQuery('#rect').css({
'width': x+'px',
'height': y+'px'
});
}
Changing the variable names helps make it slightly more readable (w and h presumably do mean width and height, but making this explicit is helpful).
All this said, it's unlikely that there will be noticeable performance differences between this and your original. The code is extremely fast, since it does very little. That said, I made a jsperf which shows that using Math.min is about 1.7 times faster on my browser.

Javascript scale check function performance

All right people, I've got a slight performance bottle neck.
Basically I have a graph that consists of a screen div ("screen") and a chart div ("chart"), when this graph finishes rendering it checks to see what scale it needs to set on the chart in order to have the chart fit inside the screen. The problem is that whatever scale I come up with needs to be an exponent of 1.2. In other words you need to be able to get to the number of the new scale by taking 1.2 to the power of some number.
This function is what calculates the scale I need.
fitScale = function (width, height)
{
var scale = 1,
gWidth = graph.element.offsetWidth,
gHeight = graph.element.offsetHeight;
while (gWidth > width * scale && gHeight > height * scale)
scale *= 1.2;
while (gWidth < width * scale || gHeight < height * scale)
scale /= 1.2;
return 900 / scale;
}
The problem is that this sucks...
What it's doing is getting the chart size (width, height) and the screen size (gWidth, gHeight) and looping through a new scale until it hits the right number.
First it makes the scale bigger until at least one dimension of the chart times the scale is bigger than one dimension of the screen.
Than it loops back to make sure that both the dimensions of chart * scale are at least a little bit smaller than the screen.
I'de like to perform this action with just one math calculation. Maybe by calculating a snug fit and then by rounding down, but I can't figure out how to round down to an exponent of 1.2
-fix-
Here's the resulting working version...
fitScale = function (width, height)
{
var wScale = graph.element.offsetWidth / width,
hScale = graph.element.offsetHeight / height,
snugg = wScale < hScale ? wScale : hScale,
exp = Math.log(snugg) / Math.log(1 / 1.2),
scale = Math.pow(1 / 1.2, Math.ceil(exp));
return 900 / scale;
}
My math skills are rusty, so go easy if I wander off the Path of Truth here.
Basically you want to know what power y of 1.2 is equal to some given number x. While the log function would appear not to be helpful since it tells you what power of e will equal your number x, with some mad logarithm skillz you can in fact use that to get to where you want to go:
var y = Math.log(x) / Math.log(1.2);
Odds are pretty good that y won't be a whole number which is I think what you want, so if you just go ahead and Math.floor(y), you should be all set.

Categories

Resources