Trying to write a simple web app to solve the following common calculus problem in JavaScript.
Suppose you wanted to make an open-topped box out of a flat piece of cardboard that is L long by W wide by cutting the same size
square (h × h) out of each corner and then folding the flaps to form the box,
as illustrated below:
You want to find out how big to make the cut-out squares in order to maximize the volume of the box.
Ideally I want to avoid using any calculus library to solve this.
My initial naive solution:
// V = l * w * h
function getBoxVolume(l, w, h) {
return (l - 2*h)*(w - 2*h)*h;
}
function findMaxVol(l, w) {
const STEP_SIZE = 0.0001;
let ideal_h = 0;
let max_vol = 0;
for (h = 0; h <= Math.min(l, w) / 2; h = h + STEP_SIZE) {
const newVol = getBoxVolume(l, w, h);
if (max_vol <= newVol) {
ideal_h = h;
max_vol = newVol;
} else {
break;
}
}
return {
ideal_h,
max_vol
}
}
const WIDTH_1 = 20;
const WIDTH_2 = 30;
console.log(findMaxVol(WIDTH_1, WIDTH_2))
// {
// ideal_h: 3.9237000000038558,
// max_vol: 1056.3058953402121
// }
The problem with this naive solution is that it only gives an estimate because you have to provide STEP_SIZE and it heavily limits the size of the problem this can solve.
You have an objective function: getBoxVolume(). Your goal is to maximize the value of this function.
Currently, you're maximizing it using something equivalent to sampling: you're checking every STEP_SIZE, to see whether you get a better result. You've identified the main problem: there's no guarantee the edge of the STEP_SIZE interval falls anywhere near the max value.
Observe something about your objective function: it's convex. I.e., it starts by going up (when h = 0, volume is zero, then it grows as h does), it reaches a maximum, then it goes down, eventually reaching zero (when h = min(l,w)/2).
This means that there's guaranteed to be one maximum value, and you just need to find it. This makes this problem a great case for binary search, because given the nature of the function, you can sample two points on the function and know which direction the maximum lies relative to those two points. You can use this, with three points at a time (left, right, middle), to figure out whether the max is between left and middle, or middle and right. Once these values get close enough together (they're within some fixed amount e of each other), you can return the value of the function there. You can even prove that the value you return is within some value e' of the maximum possible value.
Here's pseudocode:
max(double lowerEnd, upperEnd) {
double midPoint = (upperEnd + lowerEnd) / 2
double midValue = getBoxVolume(l, w, midpoint)
double slope = (getBoxVolume(l, w, midpoint + epsilon) - midValue) / epsilon
if (Math.abs(slope) < epsilon2) { // or, if you choose, if (upperEnd - lowerEnd < epsilon3)
return midpoint
}
if (slope < 0) { // we're on the downslope
return max(lowerEnd, midPoint)
}
else { // we're on the up-slope
return max(midpoint, upperEnd)
}
}
After realising that the derivative of the volume function is a second degree polynomial you can apply a quadratic formula to solve for x.
Using calculus, the vertex point, being a maximum or minimum of the function, can be obtained by finding the roots of the derivative
// V = l * w * h
function getBoxVolume(l, w, h) {
return (l - 2*h)*(w - 2*h)*h;
}
// ax^2 + bx + c = 0
function solveQuad(a, b, c) {
var x1 = (-1 * b + Math.sqrt(Math.pow(b, 2) - (4 * a * c))) / (2 * a);
var x2 = (-1 * b - Math.sqrt(Math.pow(b, 2) - (4 * a * c))) / (2 * a);
return { x1, x2 };
}
function findMaxVol(l, w) {
// V'(h) = 12h^2-4(l+w)h+l*w - second degree polynomial
// solve to get the critical numbers
const result = solveQuad(12, -4*(l + w), l*w)
const vol1 = getBoxVolume(l, w, result.x1);
const vol2 = getBoxVolume(l, w, result.x2);
let ideal_h = 0;
let max_vol = 0;
// check for max
if (vol1 > vol2) {
ideal_h = result.x1;
max_vol = vol1;
} else {
ideal_h = result.x2;
max_vol = vol2;
}
return {
ideal_h,
max_vol
}
}
const WIDTH_1 = 20;
const WIDTH_2 = 30;
console.log(findMaxVol(WIDTH_1, WIDTH_2))
// {
// ideal_h: 3.9237478148923493,
// max_vol: 1056.30589546119
// }
Related
Edit: I just changed my Control Point to the Intersection. That's why it couldn't fit anymore.
I know it's a very presumptuous. But I am working on a Web-Application and I need to calculate the intersection with a quadratic Beziere-Curve and a Line.
Linear Bezier-Curve: P=s(W-V)+V
Quadratic Bezier-Curve: P=t²(A-2B+C)+t(-2A+2B)+A
Because W, V, A, B, and C are points, I could make two equation. I rearranged the first equation to seperate s to solve the equation.
I'm pretty sure i did it correctly, but my intersection was not on the line. So i was wondering and made my own quadratic-Beziercurve by the correct formular and my intersection hits this curve. Now I am wondering what did I wrong?
That is my function:
intersectsWithLineAtT(lineStartPoint, lineEndPoint)
{
let result = []
let A = this.startPoint, B = this.controlPoint, C = this.endPoint, V = lineStartPoint, W = lineEndPoint
if (!Common.isLineIntersectingLine(A, B, V, W)
&& !Common.isLineIntersectingLine(B, C, V, W)
&& !Common.isLineIntersectingLine(A, C, V, W))
return null
let alpha = Point.add(Point.subtract(A, Point.multiply(B, 2)), C)
let beta = Point.add(Point.multiply(A, -2), Point.multiply(B, 2))
let gamma = A
let delta = V
let epsilon = Point.subtract(W, V)
let a = alpha.x * (epsilon.y / epsilon.x) - alpha.y
let b = beta.x * (epsilon.y / epsilon.x) - beta.y
let c = (gamma.x - delta.x) * (epsilon.y / epsilon.x) - gamma.y + delta.y
let underSquareRoot = b * b - 4 * a * c
if (Common.compareFloats(0, underSquareRoot))
result.push(-b / 2 * a)
else if (underSquareRoot > 0)
{
result.push((-b + Math.sqrt(underSquareRoot)) / (2 * a))
result.push((-b - Math.sqrt(underSquareRoot)) / (2 * a))
}
result = result.filter((t) =>
{
return (t >= 0 && t <= 1)
})
return result.length > 0 ? result : null
}
I hope someone can help me.
Lena
The curve/line intersection problem is the same as the root finding problem, after we rotate all coordinates such that the line ends up on top of the x-axis:
which involves a quick trick involving atan2:
const d = W.minus(V);
const angle = -atan2(d.y, d.x);
const rotated = [A,B,C].map(p => p.minus(V).rotate(angle));
Assuming you're working with point classes that understand vector operations. If not, easy enough to do with standard {x, y} objects:
const rotated = [A,B,C].map(p => {
p.x -= V.x;
p.y -= V.y;
return {
x: p.x * cos(a) - p.y * sin(a),
y: p.x * sin(a) + p.y * cos(a)
};
});
Then all we need to find out is which t values yield y=0, which is (as you also used) just applying the quadratic formula. And we don't need to bother with collapsing dimensions: we've reduced the problem to finding solutions in just the y dimension, so taking
const a = rotated[0].y;
const b = rotated[1].y;
const c = rotated[2].y;
and combining that with the fact that we know that Py = t²(a-b+c)+t(-2a+2b)+a we just work out that t = -b/2a +/- sqrt(b² - 4ac))/2a with the usual checks for negative, zero, and positive discriminant, as well as checking for division by zero.
This gives us with zero or more t value(s) for the y=0 intercept in our rotated case, and for the intersection between our curve and line in the unrotated case. No additional calculations required. Aside from "evaluating B(t) to get the actual (x,y) cooordinates", of course.
Im creating an object that randomly moves in a natural way using noise like this (works as intended):
The objects encounter a collision and their trajectory is manipulated, the movement path now changes to straight line (words as intended)
thisRabbit.x = _world.width * (noise(thisRabbit.t));
thisRabbit.y = _world.height * (noise(thisRabbit.t+5));
thisRabbit.t += 0.001;
The problem is after this movement , i want the object to start moving in a random direction again as it was initially. If i use the same function, the object jumps to the last location before the trajectory was modified.
let vx = this.acquiredFood[0] - this.x;
let vy = this.acquiredFood[1] - this.y;
let f = (this.genes.speed + 10) / Math.sqrt(vx*vx+vy*vy);
vx = vx * f;
vy = vy * f;
let newX = this.x + vx;
let newY = this.y + vy;
So how do i get the object to move as before, given a starting position
edit: snippet here: https://editor.p5js.org/vince.chinner/sketches/HPFKR8eIw
Your problem is that you used a factor from 0 to 1 generated with noise and an incremented seed to generate the position by multiplying directly the world dimentions. When reaching food, you cannot increment the seed as to be in the exact position where the movement to get your food led you (I found no inverse function for noise to get the seed from the return value).
What you need to do instead is use the noise to increment or decrement the coordinates, so that no matter where the seed is, you don't loose your current position.
Here are the different corrections I applied to the code, as there were also syntax errors, I can't really paste the whole stuff here for copyright reasons (you didn't share the whole code here and the sketch belongs to you)
MAIN CORRECTION:
used a var found because returning from the forEach callback doesn't make you leave the findFood function, but the callback one. And the forEach loop doesn't stop. Using this var prevents the further forEach tests to be made and allows you to return from findFood so that no further move is made after seeing food.
noise is now applied to a value of 4 and I subtract 2, so that x and y now change with a range of -2 to 2 each. Of course, with this method, you need to check against world dimentions or else the rabbit could leave the world. The seed increment has been changed too or else it would vary too slowly (adapt values as you wish)
findFood(){
var thisRabbit = this, found = false;
_world.food.forEach(f => {
if(!found){
let d = int(dist(f[0], f[1], thisRabbit.x, thisRabbit.y));
if(d < (thisRabbit.genes.vision / 2)+3){
thisRabbit.state = "foundFood";
this.acquiredFood = f;
found = true;
}
}
});
if(found){ return; }
thisRabbit.x += (noise(thisRabbit.t) * 4) - 2;
if(thisRabbit.x < 0){ thisRabbit.x = 0; }
if(thisRabbit.x > _world.width){ thisRabbit.x = _world.width; }
thisRabbit.y += (noise(thisRabbit.t + 5) * 4) - 2;
if(thisRabbit.y < 0){ thisRabbit.y = 0; }
if(thisRabbit.y > _world.height){ thisRabbit.y = _world.height; }
thisRabbit.t += 0.01;
}
SYNTAX ERRORS:
lines 23 / 24: assignment should be with a value (null or false)
this.genes = null;
this.acquiredFood = null;
lines 129 to 133: end you instructions with a ; instead of a ,
this.width = w;
this.height = h;
this.foodDensity = foodDensity;
this.food = [];
this.rabits = [];
line 156 to 160: there should be no space between rabbit and .t. Additionnally, because the coordinates are not directly linked to t, I would prefer to use random for starting position:
let x = this.width * random();
let y = this.height * random();
let _rabbit = new rabbit(x, y);
_rabbit.genes = genes;
_rabbit.t = t;
Question: What in the world is this piece of code doing?
Also: Is the way 'w' is being used some sort of existing algorithm? I'm trying to figure out the intent of the function, or at least describe what sorts of numbers it produces.
Context: I'm looking at Martin O'Leary's "Fantasy Map Generation" code - full source here, which in short summary generates fantasy maps on the canvas. There is some insightful explanations of how the higher level process works in a blog post, but this is too low level to get any coverage there. There is a particular function called 'rnorm' that gets used in a couple of places, and I'm lost at how it works. I've included it below, followed by a couple of instances where it comes up for some context. Any help on what this thing is doing would be great!
var rnorm = (function() {
var z2 = null;
function rnorm() {
if (z2 != null) {
var tmp = z2;
z2 = null;
return tmp;
}
var x1 = 0;
var x2 = 0;
var w = 2.0;
while (w >= 1) {
x1 = runif(-1, 1);
x2 = runif(-1, 1);
w = x1 * x1 + x2 * x2;
}
w = Math.sqrt(-2 * Math.log(w) / w);
z2 = x2 * w;
return x1 * w;
}
return rnorm;
})();
runif(), which is called in the code above, is a short function that generates a random number between two given values
function runif(lo, hi) {
return lo + Math.random() * (hi - lo);
}
This code is used to produce random vectors (actually the only place it's used during the generation process) -
function randomVector(scale) {
return [scale * rnorm(), scale * rnorm()];
}
But I think it's doing more than that because the following, when provided a direction of 'randomVector(4),' produces a gradual slope over the entire mesh heightmap: EDIT: no, it actually is having no effect on the gradual slope. That comes from some sneakyness using the fact that one side of the map is 0,0, and the other side of the map is width,height, which creates numbers that gradually increase.
function slope(mesh, direction) {
return mesh.map(function (x) {
return x[0] * direction[0] + x[1] * direction[1];
});
}
Let me know if there's anything else I should be providing. This is my first question here, so I may be a little soft on conventions.
I think it's horrible code. It appears to create a pair of values, z1 and z2, but instead of putting them in a tuple and returning that it returns z1 and on every second call the corresponding z2 value. I have no idea why they'd do such a thing, my only guess would be to avoid allocation of objects and make usage syntactically more convenient.
It should be simplified to
function rnorm() {
var x1 = 0;
var x2 = 0;
var w = 2.0;
while (w >= 1) {
x1 = runif(-1, 1);
x2 = runif(-1, 1);
w = x1 * x1 + x2 * x2;
}
w = Math.sqrt(-2 * Math.log(w) / w);
return [x1 * w, x2 * w];
}
function randomVector(scale) {
var [z1, z2] = rnorm();
return [scale * z1, scale * z2];
}
A modern compiler should be able to avoid array allocation for the returned literal and subsequent destructuring. If it's not, you can do it manually by inlining rnorm in randomVector, especially if this is the only place where it's called anyway.
I've been working on this problem for some time now with little promising results. I am trying to split up an image into connected regions of similar color. (basically split a list of all the pixels into multiple groups (each group containing the coordinates of the pixels that belong to it and share a similar color).
For example:
http://unsplash.com/photos/SoC1ex6sI4w/
In this image the dark clouds at the top would probably fall into one group. Some of the grey rock on the mountain in another, and some of the orange grass in another. The snow would be another - the red of the backpack - etc.
I'm trying to design an algorithm that will be both accurate and efficient (it needs to run in a matter of ms on midrange laptop grade hardware)
Below is what I have tried:
Using a connected component based algorithm to go through every pixel from top left scanning every line of pixels from left to right (and comparing the current pixel to the top pixel and left pixel). Using the CIEDE2000 color difference formula if the pixel at the top or left was within a certain range then it would be considered "similar" and part of the group.
This sort of worked - but the problem is it relies on color regions having sharp edges - if any color groups are connected by a soft gradient it will travel down that gradient and continue to "join" the pixels as the difference between the individual pixels being compared is small enough to be considered "similar".
To try to fix this I chose to set every visited pixel's color to the color of most "similar" adjacent pixel (either top or left). If there are no similar pixels than it retains it's original color. This somewhat fixes the issue of more blurred boundaries or soft edges because the first color of a new group will be "carried" along as the algorithm progresses and eventually the difference between that color and the current compared color will exceed the "similarity" threashold and no longer be part of that group.
Hopefully this is making sense. The problem is neither of these options are really working. On the image above what is returned are not clean groups but noisy fragmented groups that is not what I am looking for.
I'm not looking for code specifically - but more ideas as to how an algorithm could be structured to successfully combat this problem. Does anyone have ideas about this?
Thanks!
You could convert from RGB to HSL to make it easier to calculate the distance between the colors. I'm setting the color difference tolerance in the line:
if (color_distance(original_pixels[i], group_headers[j]) < 0.3) {...}
If you change 0.3, you can get different results.
See it working.
Please, let me know if it helps.
function hsl_to_rgb(h, s, l) {
// from http://stackoverflow.com/questions/2353211/hsl-to-rgb-color-conversion
var r, g, b;
if (s == 0) {
r = g = b = l; // achromatic
} else {
var hue2rgb = function hue2rgb(p, q, t) {
if (t < 0) t += 1;
if (t > 1) t -= 1;
if (t < 1 / 6) return p + (q - p) * 6 * t;
if (t < 1 / 2) return q;
if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
return p;
}
var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
var p = 2 * l - q;
r = hue2rgb(p, q, h + 1 / 3);
g = hue2rgb(p, q, h);
b = hue2rgb(p, q, h - 1 / 3);
}
return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)];
}
function rgb_to_hsl(r, g, b) {
// from http://stackoverflow.com/questions/2353211/hsl-to-rgb-color-conversion
r /= 255, g /= 255, b /= 255;
var max = Math.max(r, g, b),
min = Math.min(r, g, b);
var h, s, l = (max + min) / 2;
if (max == min) {
h = s = 0; // achromatic
} else {
var d = max - min;
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
switch (max) {
case r:
h = (g - b) / d + (g < b ? 6 : 0);
break;
case g:
h = (b - r) / d + 2;
break;
case b:
h = (r - g) / d + 4;
break;
}
h /= 6;
}
return [h, s, l];
}
function color_distance(v1, v2) {
// from http://stackoverflow.com/a/13587077/1204332
var i,
d = 0;
for (i = 0; i < v1.length; i++) {
d += (v1[i] - v2[i]) * (v1[i] - v2[i]);
}
return Math.sqrt(d);
};
function round_to_groups(group_nr, x) {
var divisor = 255 / group_nr;
return Math.ceil(x / divisor) * divisor;
};
function pixel_data_to_key(pixel_data) {
return pixel_data[0].toString() + '-' + pixel_data[1].toString() + '-' + pixel_data[2].toString();
}
function posterize(context, image_data, palette) {
for (var i = 0; i < image_data.data.length; i += 4) {
rgb = image_data.data.slice(i, i + 3);
hsl = rgb_to_hsl(rgb[0], rgb[1], rgb[2]);
key = pixel_data_to_key(hsl);
if (key in palette) {
new_hsl = palette[key];
new_rgb = hsl_to_rgb(new_hsl[0], new_hsl[1], new_hsl[2]);
rgb = hsl_to_rgb(hsl);
image_data.data[i] = new_rgb[0];
image_data.data[i + 1] = new_rgb[1];
image_data.data[i + 2] = new_rgb[2];
}
}
context.putImageData(image_data, 0, 0);
}
function draw(img) {
var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
context.drawImage(img, 0, 0, canvas.width, canvas.height);
img.style.display = 'none';
var image_data = context.getImageData(0, 0, canvas.width, canvas.height);
var data = image_data.data;
context.drawImage(target_image, 0, 0, canvas.width, canvas.height);
data = context.getImageData(0, 0, canvas.width, canvas.height).data;
original_pixels = [];
for (i = 0; i < data.length; i += 4) {
rgb = data.slice(i, i + 3);
hsl = rgb_to_hsl(rgb[0], rgb[1], rgb[2]);
original_pixels.push(hsl);
}
group_headers = [];
groups = {};
for (i = 0; i < original_pixels.length; i += 1) {
if (group_headers.length == 0) {
group_headers.push(original_pixels[i]);
}
group_found = false;
for (j = 0; j < group_headers.length; j += 1) {
// if a similar color was already observed
if (color_distance(original_pixels[i], group_headers[j]) < 0.3) {
group_found = true;
if (!(pixel_data_to_key(original_pixels[i]) in groups)) {
groups[pixel_data_to_key(original_pixels[i])] = group_headers[j];
}
}
if (group_found) {
break;
}
}
if (!group_found) {
if (group_headers.indexOf(original_pixels[i]) == -1) {
group_headers.push(original_pixels[i]);
}
if (!(pixel_data_to_key(original_pixels[i]) in groups)) {
groups[pixel_data_to_key(original_pixels[i])] = original_pixels[i];
}
}
}
posterize(context, image_data, groups)
}
var target_image = new Image();
target_image.crossOrigin = "";
target_image.onload = function() {
draw(target_image)
};
target_image.src = "http://i.imgur.com/zRzdADA.jpg";
canvas {
width: 300px;
height: 200px;
}
<canvas id="canvas"></canvas>
You can use "Mean Shift Filtering" algorithm to do the same.
Here's an example.
You will have to determine function parameters heuristically.
And here's the wrapper for the same in node.js
npm Wrapper for meanshift algorithm
Hope this helps!
The process you are trying to complete is called Image Segmentation and it's a well studied area in computer vision, with hundreds of different algorithms and implementations.
The algorithm you mentioned should work for simple images, however for real world images such as the one you linked to, you will probably need a more sophisticated algorithm, maybe even one that is domain specific (are all of your images contains a view?).
I have little experience in Node.js, however from Googling a bit I found the GraphicsMagic library, which as a segment function that might do the job (haven't verified).
In any case, I would try looking for "Image segmentation" libraries, and if possible, not limit myself only to Node.js implementations, as this language is not the common practice for writing vision applications, as opposed to C++ / Java / Python.
I would try a different aproach. Check out this description of how a flood fill algorithm could work:
Create an array to hold information about already colored coordinates.
Create a work list array to hold coordinates that must be looked at. Put the start position in it.
When the work list is empty, we are done.
Remove one pair of coordinates from the work list.
If those coordinates are already in our array of colored pixels, go back to step 3.
Color the pixel at the current coordinates and add the coordinates to the array of colored pixels.
Add the coordinates of each adjacent pixel whose color is the same as the starting pixel’s original color to the work list.
Return to step 3.
The "search approach" is superior because it does not only search from left to right, but in all directions.
You might look at k-means clustering.
http://docs.opencv.org/3.0-beta/modules/core/doc/clustering.html
All my searching comes up with more general arc/sin/cos usage or shooting to the mouse position.
I am looking to aim and fire a projectile with the keyboard and have done a lot of it from scratch, as a noob in a web class doing a project, but I am stuck on this. My current math got me to this mess in firing the shot in the direction the line is currently pointing... (code names cleaned for readability):
this.x = x + len * Math.cos(angle);
this.y = y + len * Math.sin(angle);
this.xmov = -((x + len * Math.cos(angle)) - x) / ((y + len * Math.sin(angle)) - y);
this.ymov = ((y + len * Math.sin(angle)) - y) / ((x + len * Math.cos(angle)) - x);
if (Math.abs(this.xmov) > Math.abs(this.ymov)) {
this.xmove = (this.xmov * Math.abs(this.ymov));
} else {
this.xmove = this.xmov;
}
if (Math.abs(this.ymov) > Math.abs(this.xmov)) {
this.ymove = (this.xmov * this.ymov);
} else {
this.ymove = this.ymov;
}
(And here is the full thing http://jsbin.com/ximatoq/edit. A and D to turn, S to fire (on release). Can also hold S while turning.)
... but, you'll see that it only works for 3/8's of it. What is the math to make this fire from a complete circle?
Use this as shoot function:
this.shoot = function() {
if (this.fire > 0) {
this.x = P1gun.x2;
this.y = P1gun.y2;
this.xmove = (P1gun.x2 - P1gun.x)/100;
this.ymove = (P1gun.y2 - P1gun.y)/100;
this.fire = 0;
this.firetravel = 1;
}
}
The /100 can be removed, but you have to reduce the projectile speed.
If you want to shoot gun2 change the P1gun to P2gun.
Normalising a vector.
To control the speed of something using a vector, first make the length of the vector 1 unit long (one pixel). This is commonly called normalising the vector, and sometimes it's called the unit vector. Then you can multiply that vector by any number to get the desired speed.
To normalise a vector first calculate its length, then divide it by that value.
function normalizeVector(v){
var len = Math.sqrt(v.x * v.x + v.y * v.y);
v.x /= len;
v.y /= len;
return v;
}
Trig
When you use trig to create a vector it is also a unit vector and does not need to be normalised.
function directioToUnitVector(angle){ // angle in radians
return {
x : cos(angle),
y : sin(angle)
}
Why normalise
Many many reasons, you build almost everything from unit vectors.
One example, if you have two points and want to move from one to the next at a speed of 10 pixels per second with a frame rate of 60frame per second.
var p1 = {};
var p2 = {};
p1.x = ? // the two points
p1.y = ?
p2.x = ?
p2.y = ?
// create a vector from p1 to p2
var v = {}
v.x = p2.x -p1.x;
v.y = p2.y -p1.y;
// Normalize the vector
normalizeVector(v);
var frameRate = 1/60; // 60 frames per second
var speed = 10; // ten pixels per second
function update(){
// scale vec to the speed you want. keeping the vec as a unit vec mean
// you can also change the speed, or use the time for even more precise
// speed control.
p1.x += v.x * (speed * frameRate);
p1.y += v.y * (speed * frameRate);
// draw the moving object at p1
requestAnimationFrame(update)
}
NOTE when normalizing you may get a vector that has no length. If your code is likely to create such a vector you need to check for the zero length and take appropriate action. Javascript does not throw an error when you divide by zero, but will return Infinity, with very strange results to your animations.