I'm hoping someone can explain how the the below function expression works. The parameter 'p' has not been assigned a value, however it is used in the body of the function for calculations (defining the "angle" and "tail" variables) and as an argument object when draw() is called. When you step through the code, you can see that parameter 'p' does, indeed, have a value (see attached image)- but I don't understand where that value comes from. Please note that 'p' is not noted anywhere else upon review of the full code.
When you log 'p' to the console you see the following values, which continue to increase as the application runs:
Image of console.log(p);
Here is the full function expression and the function call:
var draw = function(p) {
$.fillStyle = "hsla(38,5%,12%,.90)";
$.fillRect(0, 0, w, h);
$.fillStyle = "hsla(38, 25%, 90%, 1)";
$.strokeStyle = "hsla(38, 25%, 90%, 1)";
for (var i = 0; i < numh; i++)
for (var j = 0; j < numw; j++) {
var diagnalW = j * spacing +
(i % 2 ? 0 : spacing / 2);
var diagnalH = i * spacing;
var arr = [position[0] - diagnalW,
position[1] - diagnalH
],
wave = Math.sqrt(arr[0] * arr[0] +
arr[1] * arr[1]),
arr = [arr[0] / wave, arr[1] / wave],
angle = 50 * (Math.cos(p / 360 - wave / 105) - 1);
$.beginPath();
$.arc(diagnalW + arr[0] * angle, diagnalH +
arr[1] * angle, 2.8, 0, 2 * Math.PI, false);
$.closePath();
$.fill();
for (var n = 0; n < 5; n++) {
var tail = 50 * (Math.cos((p - 50 * n) /
360 - wave / 105) - 1);
$.beginPath();
$.moveTo(diagnalW + arr[0] * angle, diagnalH +
arr[1] * angle);
$.lineWidth = 5 - n;
$.lineTo(diagnalW + arr[0] * tail, diagnalH + arr[1] * tail);
$.stroke()
}
}
};
var anim = function(p) {
window.requestAnimationFrame(anim);
draw(p);
};
anim();
I understand everything about this code with the exception of how 'p' obtains the values shown in the console. Also - in case it wasn't clear, this is an html5 canvas application.
(Side note: No, $ in the above isn't jQuery. It's just what the original author uses for her canvas context object.)
In the first call to draw, p will have the value undefined because anim() doesn't pass a value for p, and anim then uses p when calling draw.
After that, though, draw is called by the browser, not by that code, because it's being used as the callback for requestAnimationFrame. The browser will call it with a high-resolution timer value, which is what you're seeing in p.
p is a parameter of a callback for window.requestAnimationFrame
A parameter specifying a function to call when it's time to update your animation for the next repaint. The callback has one single argument, a DOMHighResTimeStamp, which indicates the current time (the time returned from performance.now()) for when requestAnimationFrame starts to fire callbacks.
Related
The code is from here:
t=0
draw=_=>{t||createCanvas(W = 720,W)
t+=.01
B=blendMode
colorMode(HSB)
B(BLEND)
background(0,.1)
B(ADD)
for(y = 0; y < W; y += 7)
for(x = 0; x < W; x+=7)
dist(x, y, H = 360, H) +
!fill(x * y % 360, W, W,
T=tan(noise(x, y) * 9 + t))
< sin(atan2(y - H, x - H) * 2.5 + 84)**8 * 200 + 130?
circle(x, y + 30, 4/T) : 0}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.5.0/p5.js"></script>
I see that t is increased by 0.01 each iteration, but I am unsure whether for each t value the entire canvas is refreshed, or whether there is an endpoint somewhat set by :0}. Is this correct?
It also seems like there is a Boolean call in the middle basically comparing two distances to determine which circles are filled and how. If the < was instead > the circles would form a complementary pattern on the rendition.
The ! is explained here, as a way of saving space and calling a function as soon as it is declared. I presume it determines how points are filled with a certain variable color scheme depending on the Boolean operation result. The +!fill() is unfamiliar, as well as the ? at the end, and I guess that they amount to: if the x, y location is within the boundary of the star the circles are colored (filled) as such, but the negation in '!' is confusing.
Can I get an English explanation of the main structural points on this code (the loop and the Boolean) to match the syntax?
I have so far gathered that the basic loop is
for(y from 0 to the width of the canvas at increments of 7)
for(x from... )
check if the distance from (x , y) to 360 is less than sin()^8 * 200 + 130
If so fill (or not fill with the ! ????) with these colors
otherwise do nothing :0
This is what it might look like if it were written normally
let t = 0;
const W = 720;
// https://p5js.org/reference/#/p5/draw
// `draw` needs to be in the global scope so p5 can use it
draw = () => {
// create a canvas if this is the first frame
if (!t) createCanvas(W, W);
t += 0.01;
// Use HSB and blending to do the fancy effects
// The black circles are because we're ignoring stroke and so using its defaults
// The blending will eventually hide it anyway
colorMode(HSB);
blendMode(BLEND);
background(0, 0.1);
blendMode(ADD);
// iterate over 7px grid
for(y = 0; y < W; y += 7) {
for(x = 0; x < W; x += 7) {
// center
const H = 360;
// distance from center
const D = dist(x, y, H, H);
// pick an alpha based on location and time
const T = tan(noise(x, y) * 9 + t);
// set fill color
fill(x * y % 360, W, W, T);
// magic to calculate the star's boundary
// sine wave in polar coords, I think
const S = sin(atan2(y - H, x - H) * 2.5 + 84)**8 * 200 + 130;
// draw a circle if we're within the star's area
// circle's color, alpha, and radius depend on location and time
if (D < S) circle(x, y + 30, 4/T);
}
}
};
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.5.0/p5.js"></script>
Note that there are some hacks in the original code that are solely to help make it a one-liner or to reduce the number of characters. They don't have any meaningful effect on the results.
The main issue is the +! with ? and :0, which is terribly confusing, but clearly it is equivalent to
t=0
draw=_=>{t||createCanvas(W = 720,W)
t+=.01
B=blendMode
colorMode(HSB)
B(BLEND)
background(0,.1)
B(ADD)
for(y = 0; y < W; y += 7)
for(x = 0; x < W; x+=7)
if(dist(x, y, H = 360, H) < sin(atan2(y - H, x - H) * 2.5 + 84)**8 * 200 + 130){
fill(x * y % 360, W, W,
T=tan(noise(x, y) * 9 + t))
circle(x, y + 30, 4/T)}else{0}}
The Boolean in the ? applies to the dist() part (in absolute values the angle has to be less than sin()... + 130.
The + part I still don't understand, and hopefully will be addressed by someone who knows Processing or JS (not me). However, it probably forces the execution to identify and throw out with regards to filling (hence !) values that are too low for the sin(atan2()), which will happen at around 0 and pi.
Because of arctan2() being (here)
the number of spikes in the star will be a multiple of 5 going from 0 to 2 pi. The fact that changing the code to < sin(2 * atan2(...)... doubles the spikes implies that the fill() part is also in Boolean operation of its own.
The result of the Boolean determines whether the colorful fill is applied or not (applied if less than).
The :0 at the end is the else do nothing.
slowly learning JavaScript on the side and wanted to try and animate this with Three.JS:
https://www.reddit.com/r/gifs/comments/ag6or3/send_this_to_your_loved_ones_for_valentines/
I was trying to re-create that equation but ran into a wall in that the code below is not producing the right result. I had read that JS has some big issues with floating point numbers and in particular cubed roots don't really work all that well.
for (var x = -100; x < 100; x++)
{
y = Math.pow(x, 2/3) + 0.9 * (Math.pow(3.0 - (x*x), 0.5)) * Math.sin(10 *
Math.PI * x)
}
Does that look right to you JS masters?
Here is my code implementation in trying to get this to work including the fix mentioned below.
https://codesandbox.io/s/vjm4xox185
Look at the range of the graph you linked to. The heart is being drawn in the range of x: [-2, 2] , but your loop is from x: [-100, 100]. This means you'll probably get undefined results for all x values except -1, 0, 1. Try narrowing down the range of your for() loop, and you should get the desired result.
The problem is that the result of the calculation of (Math.pow(3.0 - (x*x), 0.5)) return NAN as if not realistic number
Read here for more information about Math.pow(negativeNumber, 0.5)
so i added validPow that will validate the x is positive or negative and return the right result.
for (var x = -100; x < 100; x++)
{
y = (Math.pow(x, 2/3) + 0.9) * (validPow(3.0 - (x*x), 0.5)) *
Math.sin(10 * Math.PI * x)
console.log(y)
}
function validPow(x, y)
{
var result = Math.pow(x, y);
if (x > 0)
{
return result;
}
else
{
return -1 * Math.pow(-x, y);
}
}
Finally solved this.
It came down to this line with the key being to use Math.abs(x) inside the first Math.pow statement:
var y = Math.pow(Math.abs(x), 0.66) + (0.9 * Math.sqrt(3.3 - x * x)) * Math.sin(10 * Math.PI * x);
Thanks for everyone who provided input and help!
You can view the final result here:
https://codesandbox.io/s/vjm4xox185
I'm modeling the resonance effect with html5 canvas to animate spring when its reach the resonance.
Also got jquery ui slider (max ranged) that changes frequency (w) of the oscillations dynamically during the animation. The problem is when its changed, for some reason sine wave brokes at some points and the animation is not smooth. This is only happens when changing frequency, with amplitude its much better.
my main function to render each frame is this:
function doSways() {
var spring = springs[0],
a = 0,
A = params.standParams.A,
w = params.standParams.w;
animIntervalId = setInterval(function() {
ctx.clearRect(0, 0, cnvW, cnvH);
A = params.standParams.A;
w = params.standParams.w;
/*if (w < params.standParams.w) { // with this expression and commented <w = params.standParams.w> it works a bit smoother but still some issues can be noticed, for example sharp increases just after the new change of the frequency (w) on the slider
w += 0.01;
}*/
stand.y = A*Math.sin(a*degToRad*w) + offsetY;
stand.draw();
spring.draw(stand.y, A, w);
if (a++ >= 360) { // avoid overflow
a = 0;
}
},
25);
}
here's how I change frequency(w) on the slider and assign it to params.standParams.w
$( "#standfreq_slider" ).slider({
range: "max",
min: 1,
max: 25,
step: 1,
value: 5,
slide: function( event, ui ) {
params.standParams.w = parseInt(ui.value);
}
});
);
That if expression in doSways function kinda work but it casues another problem, I need to know the direction of sliding to determine wether I need to += or -= 0.01..
How to make everything work ideal ?
problem illustration live
jsfiddle
based on the basic formula for sinusoids:
V = V0 + A * sin(2 * pi * f * t + phi)
Where:
V: current value
V0: center value
A: amplitude
f: frequency (Hz)
t: time (seconds)
phi: phase
We want the current value (V) to be the same before and after a frequency change. In order to do that we will need a frequency (f) and phase (phi) before and after the change.
First we can set the first equation equal the the second one:
V0 + A * sin(2 * pi * f1 * t + phi1) = V0 + A * sin(2 * pi * f2 * t + phi2)
do some cancelling:
2 * pi * f1 * t + phi1 = 2 * pi * f2 * t + phi2
solve for the new phase for the final formula:
phi2 = 2 * pi * t * (f1 - f2) + phi1
More mathy version:
edited:
check out modified fiddle: https://jsfiddle.net/potterjm/qy1s8395/1/
Well the pattern i explain below is one i use quite often.
( It might very well have a name - please S.O. people let me know if so-. )
Here's the idea : whenever you need to want a property evolving in a smooth manner, you must distinguish the target value, and the current value. If, for some reason, the target value changes, it will only affect the current value in the way you decided.
To get from the current value to the target value, you have many ways :
• most simple : just get it closer from a given ratio on every tick :
current += ratio * (target-current);
where ratio is in [0, 1], 0.1 might be ok. You see that, with ratio == 1, current == target on first tick.
Beware that this solution is frame-rate dependent, and that you might want to threshold the value to get the very same value at some point, expl :
var threshold = 0.01 ;
if (Math.abs(target-current)<threshold) current = target;
• You can also reach target at a given speed :
var sign = (target - current) > 0 ? 1 : -1;
current += sign * speedToReachTarget * dt;
Now we are not frame-rate dependent (you must handle frame time properly), but you will have 'bouncing' if you don't apply a min/max and a threshold also :
if (Math.abs(target-current)<0.01) {
current = target;
return;
}
var sign = (target - current) > 0 ? 1 : -1;
current += sign * speedToReachTarget * dt;
current = (( sign > 0) ? Math.min : Math.max )( target, current);
• and you might use many other type of interpolation/easing.
I was working on a fun project that implicates creating "imperfect" circles by drawing them with lines and animate their points to generate a pleasing effect.
The points should alternate between moving away and closer to the center of the circle, to illustrate:
I think I was able to accomplish that, the problem is when I try to render it in a canvas half the render jitters like crazy, you can see it in this demo.
You can see how it renders for me in this video. If you pay close attention the bottom right half of the render runs smoothly while the top left just..doesn't.
This is how I create the points:
for (var i = 0; i < q; i++) {
var a = toRad(aDiv * i);
var e = rand(this.e, 1);
var x = Math.cos(a) * (this.r * e) + this.x;
var y = Math.sin(a) * (this.r * e) + this.y;
this.points.push({
x: x,
y: y,
initX: x,
initY: y,
reverseX: false,
reverseY: false,
finalX: x + 5 * Math.cos(a),
finalY: y + 5 * Math.sin(a)
});
}
Each point in the imperfect circle is calculated using an angle and a random distance that it's not particularly relevant (it relies on a few parameters).
I think it's starts to mess up when I assign the final values (finalX,finalY), the animation is supposed to alternate between those and their initial values, but only half of the render accomplishes it.
Is the math wrong? Is the code wrong? Or is it just that my computer can't handle the rendering?
I can't figure it out, thanks in advance!
Is the math wrong? Is the code wrong? Or is it just that my computer can't handle the rendering?
I Think that your animation function has not care about the elapsed time. Simply the animation occurs very fast. The number of requestAnimationFrame callbacks is usually 60 times per second, So Happens just what is expected to happen.
I made some fixes in this fiddle. This animate function take care about timestamp. Also I made a gradient in the animation to alternate between their final and initial positions smoothly.
ImperfectCircle.prototype.animate = function (timestamp) {
var factor = 4;
var stepTime = 400;
for (var i = 0, l = this.points.length; i < l; i++) {
var point = this.points[i];
var direction = Math.floor(timestamp/stepTime)%2;
var stepProgress = timestamp % stepTime * 100 / stepTime;
stepProgress = (direction == 0 ? stepProgress: 100 -stepProgress);
point.x = point.initX + (Math.cos(point.angle) * stepProgress/100 * factor);
point.y = point.initY + (Math.sin(point.angle) * stepProgress/100 * factor);
}
}
Step by Step:
based on comments
// 1. Calculates the steps as int: Math.floor(timestamp/stepTime)
// 2. Modulo to know if even step or odd step: %2
var direction = Math.floor(timestamp/stepTime)%2;
// 1. Calculates the step progress: timestamp % stepTime
// 2. Convert it to a percentage: * 100 / stepTime
var stepProgress = timestamp % stepTime * 100 / stepTime;
// if odd invert the percentage.
stepProgress = (direction == 0 ? stepProgress: 100 -stepProgress);
// recompute position based on step percentage
// factor is for fine adjustment.
point.x = point.initX + (Math.cos(point.angle) * stepProgress/100 * factor);
point.y = point.initY + (Math.sin(point.angle) * stepProgress/100 * factor);
EDIT: So apparently, PI is finite in JavaScript (which makes sense). But that leaves me with a major problem. What's the next best way to calculate the angles I need?
Alright, first, my code:
http://jsfiddle.net/joshlalonde/vtfyj/34/
I'm drawing cubes that open up to a 120 degree angle.
So the coordinates are calculated based on (h)eight and theta (120).
On line 46, I have a for loop that contains a nested for loop used for creating rows/columns.
It's somewhat subtle, but I noticed that the lines aren't matching up exactly. The code for figuring out each cubes position is on line 49. One of the things in the first parameter (my x value) for the origin of the cube is off. Can anyone help figure out what it is?
var cube = new Cube(
origin.x + (j * -w * (Math.PI)) +
(i * w * (Math.PI))
, origin.y + j * (h / 2) +
i * (h / 2) +
(-k*h), h);
Sorry if that's confusing. I,j, and k refer to the variable being incremented by the for loops. So basically, a three dimensional for loop.
I think the problem lies with Math.PI.
The width isn't the problem, or so I believe. I originally used 3.2 (which I somehow guessed and it seemed to line up pretty good. But I have no clue what the magical number is). I'm guessing it has to do with the angle being converted to Radians, but I don't understand why Math.PI/180 isn't the solution. I tried multiple things. 60 (in degrees) * Math.PI/180 doesn't work. What is it for?
EDIT: It might just be a JavaScript related math problem. The math is theoretically correct but can't be calculated correctly. I'll accept the imperfection to spare myself from re-writing code in unorthodox manners. I can tell it would take a lot to circumvent using trig math.
There are 2 problems...
Change line 35 to var w=h*Math.sin(30);. The 30 here matches the this.theta / 4 in the Cube getWidthmethod since this.theta equals 120.
Use the following code to generate the position of your new cube. You don't need Math.Pi. You needed to use both the cube width and height in your calculation.
var cube = new Cube(
origin.x+ -j*w - i*h,
origin.y + -j*w/2 + i*h/2,
h);
Alright I found the solution!
It's really simple - I was using degrees instead of radians.
function Cube(x, y, h) {
this.x = x
this.y = y
this.h = h;
this.theta = 120*Math.PI/180;
this.getWidth = function () {
return (this.h * Math.sin(this.theta / 2));
};
this.width = this.getWidth();
this.getCorner = function () {
return (this.h / 2);
};
this.corner = this.getCorner();
}
So apparently Javascript trig functions use Radians, so that's one problem.
Next fix I made was to the offset of each point in the cube. It doesn't need one! (o.O idk why. But whatever it works. I left the old code just in case I discover why later on).
function draw() {
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
ctx.fillStyle = "#000";
ctx.fillRect(0, 0, canvas.width, canvas.height); // Draw a black canvas
var h = 32;
var width = Math.sin(60*Math.PI/180);
var w = h*width;
var row = 9; // column and row will always be same (to make cube)
var column = row;
var area = row * column;
var height = 1;
row--;
column--;
height--;
var origin = {
x: canvas.width / 2,
y: (canvas.height / 2) - (h * column/2) + height*h
};
var offset = Math.sqrt(3)/2;
offset = 1;
for (var i = 0; i <= row; i++) {
for (var j = 0; j <= column; j++) {
for (var k = 0; k <= height; k++) {
var cube = new Cube(
origin.x + (j * -w * offset) +
(i * w * offset)
, origin.y + (j * (h / 2) * offset) +
(i * (h / 2) * offset) +
(-k*h*offset), h);
var cubes = {};
cubes[i+j+k] = cube; // Store to array
if (j == column) {
drawCube(2, cube);
}
if (i == row) {
drawCube(1, cube);
}
if (k == height) {
drawCube(0,cube);
}
}
}
}
}
See the full Jsfiddle here: http://jsfiddle.net/joshlalonde/vtfyj/41/