Drawing any function's graph using Javascript - javascript

I'm taking a javascript class for my bachelor in CS, and the IDE we're using (it's an educative IDE developed by one of the graduates here, called codeboot) has a "turtle drawing" feature. Basically, it works using several basic commands;
Forward; fd(x), Back; bk(x), rotate right; rt(x), rotate left; lt(x), pen up; pu() and pen down; pd(). Where x represent pixels.
For example, to have it draw a circle, one would write something like:
for (var n = 0; n < 360; n++) {
fd(1);
rt(1);
}
I'm trying to have it draw graphs for basic functions,but the only way I found how to do it is to have it draw a single point for each 0.005x (so it looks like a continuous line when zoomed out), but it makes the execution extremely slow, and you can kind of see it's not legit. How would I go about having just a continuous line instead of individually plotting each point?
Screenshot of what it looks like using my first technique:
Screenshot of what it looks like using my first technique:
and the JS code for said technique:
var x = -9;
var traceDown = function(d) {
var y = Math.cos(x) * 30;
setpw(3);
pu();
fd(y);
pd();
fd(1);
pu();
bk(1 + (y));
rt(90);
fd(d);
lt(90);
pd();
x += 0.0005;
};
pu();
lt(90);
fd(90);
lt(90);
fd(45);
pd();
rt(180);
while (x < 9) {
traceDown(0.005);
}

Related

P5.js Drawing new shapes is causing the older shapes to disappear

i’m working on a complex visualisation project, using p5.js i want to create multiple clusters of shapes to visualise all sorts of data, so i’m looping through the data, checking the objects specified by user and drawing them to canvas, this happens in a nested for loop…the problem i’m facing is when an inner loop is done drawing a cluster of shapes and the overall loop continues and re-enters the inner loop again to draw the next cluster, the older cluster of drawn shapes disappears…i don’t understand why, i thought this had to do with that the shapes are related to the inner loop context/scope and tried to recreate the problem based on that but it appears that this is not the case.
this code for example works perfectly, it draws 5 circles on 20 lines, run by a nested loop
function setup() {
createCanvas(window.innerWidth, window.innerHeight).parent('myContainer');
}
function draw() {
background(220);
x = 100;
y = 100;
for (let i = 1; i <= 20; i++) {
for(let j=1;j<=5;j++){
fill(color(0, 255, 0));
stroke(0);
ellipse(x, y, 80);
x += 90;
}
x = 100;
y += 90;
}
}
while in my code, after the inner loop moves to the next cluster the previous shapes just disappear and only the last cluster is shown…i did a lot of console logging and debugging and made sure that every cluster is actually getting drawn but it just disappears when a new one is created…can anyone please help?
is there anyway to emphasise the library to draw a NEW separate shape…like this?
new ellipse(x,y,rad)
this is my project’s code, it shows where the bug happens
function setup() {
createCanvas(viewWidth, viewHeight).parent('myContainer');
}
function draw() {
//code...
var objects_len = objects.length;
for (var cluster_index = 1; cluster_index < cluster_len; cluster_index++) {
array_center = partition(cluster_index - 1, cluster_len - 1);
for (var object_index = 0; object_index < objects_len; object_index++) {
if (!objects[object_index].deleted) {
draw_simple_object(objects[object_index], data, cluster_index, array_center, cluster_len);
}
}
addClusterName(cluster_index, data, array_center);
}
function draw_simple_object(object, data, cluster_index, array_center, cluster_len) {
.
.
.
//this is where the drawing happens
var center_shape = [array_center[0] + object.centerOffsetX * array_center[2], array_center[1] + object.centerOffsetY * array_center[3]];
if (object.shape === 'ellipse' && enable) {
var size_min = Math.min(array_center[2], array_center[3]) * 2;
fill(colorFill);
stroke(object.colorBorder);
ellipse(center_shape[0], center_shape[1], size_min * width * proportion, size_min * length * proportion);
}
.
.
.
}
}
please help me
Can it be because you are calling the background() function inside the loop? (that part is actually not shown in your snippet)
Solved.
i was using resizeCanvas() in one of the function inside draw() which cleared all content off the canvas before resizing. removing resizeCanvas solves the problem.

Canvas Maze character's distance from walls

I am working on a 2D maze game with a torch effect in canvas without the use of any raytracing. Everything is working great, however the torch effect's algorithm is causing immense lags in several browsers and computers. (It is weird as well, that the game runs smoother on older computers. The funniest is, IExplorer runs the game without any lags, while mozzila dies on every move..)
My general idea for solving this problem was, to get how far the character is from the walls (4 functions) and make the rest of the maze grey.
Here is an example with the Northern wall detection:
http://webprogramozas.inf.elte.hu/~ce0ta3/beadando/maze_example.png
And an example how it is working at the moment and what I would like to achieve without lag issues.
http://webprogramozas.inf.elte.hu/~ce0ta3/beadando/ce0ta3_html5_maze.html
As I mentioned above, the algorithm that tracks the character's distance from the walls is causing incredible lags.
//Get the character's X,Y position as parameter
function distanceFromNorth (posX,posY)
{
distNorth = 0;
var l = false;
//Start getting charSize x 1 px lines from the character position towards the up, until we reach the max viewDistance or we find a black pixel in the charSize x 1 line.
for (var i = posY; i > posY - viewDistance && !l; i--)
{
var mazeWallData = context.getImageData(posX, i, charSize, 1);
var data = mazeWallData.data;
//Check if there are any black pixels in the line
for (var j = 0; j < 4 * charSize && !l; j += 4)
{
l = (data[j] === 0 && data[j + 1] === 0 && data[j + 2] === 0);
}
distNorth++;
}
return distNorth;
}
I am fairly sure, that the ctx.getImageData() is the most costly method in this linear search and if I only requested this method once for a charSize x viewDistance rectangle, and then check for black pixels in that huge array, then the lag could be reduced greatly. However, I still want to keep searching in lines, because finding only one black pixel will return false distNorth value.
I would be grateful if anyone could convert my code into the form I mentioned in the previous paragraph.
Assuming the image data isnt changing then you can precompute all the pixel values that have black pixel. Then use simple binary search on it to get the any black pixels in the given range.
Algorithm : -
cols[];
rows[];
for(int i=0;i<height;i++) {
for(int j=0;j<width;j++) {
if(pixel(j,i)==black) {
row[i].add(j);
col[j].add(i);
}
}
}
for query on (x,y) :
distance = binarysearch(col[x],y,y-distance) - y

canvas class javascript

I'm having trouble with my javascript code. I'm trying to create a moving set of circles where each circle has their own attributes. So far I've managed to input all the needed values into an array, but I can't figure out how to use them properly for drawing on canvas.
Here's the javascript:
var radius = 10;
var step = x = y = 0;
var r = g = b = 255;
var circleHolder = [];
var loop = setInterval(function(){update();}, 30);
function Circle(x, y, radius, r, g, b)
{
this.x = x;
this.y = y;
this.radius = radius;
this.r = r;
this.g = g;
this.b = b;
circleHolder.push(this);
}
Circle.prototype.draw = function()
{
Circle.prototype.ctx = document.getElementById("MyCanvas").getContext("2d");
Circle.prototype.ctx.clearRect(0,0,720,720); // clear canvas
Circle.prototype.ctx.beginPath();
Circle.prototype.ctx.strokeStyle = "rgb("+ this.r +", "+ this.g +", "+ this.b +")";
Circle.prototype.ctx.arc(this.x, this.y, this.radius, 0, 2 * Math.PI);
Circle.prototype.ctx.stroke();
}
Circle.prototype.update = function ()
{
step += .02;
step %= 2 * Math.PI;
this.x = parseInt((Math.sin(step)) * 150) + 360;
this.y = parseInt((Math.cos(step)) * 150) + 360;
this.radius += 16;
if (this.radius > 200)
{
for (i in circleHolder)
{
if (circleHolder[i]==this)
{
circleHolder.splice(i, 1);
}
}
}
}
function update()
{
var ci = new Circle(x, y, radius, r, g, b);
for (i in circleHolder)
{
ci = circleHolder[i];
ci.update();
ci.draw();
}
}
I'm pretty sure my problem lies within update() {} but I can't figure out how to do it properly.
EDIT: Okay, I've got it working with some changes! Check this Fiddle! I'm getting "ci not defined" error in the console though, and it has a strange bug: Changing the "if (this.radius > 128)" to higher integer it will make the circles spin faster, I don't know why. If you want you can try to change it to 256 and see what happens.
for (var i=0; i < allCircles; i++)
{
ci = circleHolder[i]; <----- This is causing the error
ci.update();
ci.draw();
}
it's not 100% clear to me what you're trying to do, but I tried to fix the main problem
One problem is your for loop.. you shouldn't use for in for arrays, do this instead:
for (var i=0 ; i<circleHolder.length ; i++)
{
ci = circleHolder[i];
ci.update();
ci.draw();
}
see this fiddle
Also I moved your get context and other things that should happen only once into the constructor, instead of having it in the update function.
You're also clearing the canvas before each draw, so the it will only show the last drawn circle per frame. (if you remove the clearRect it looks like one of those old spirographs).
You were also drawing the circles with (255,255,255)(white) so it wasn't showing until the color was changed.
Edit:
Really there are a few problems with this code:
The context shouldn't be inside a circle class if you plan on having many of them.
You should have some object which contains the canvas/context and an array of all circles.
Then have that object manage the updating/drawing.
For starters, unless there's something else going on, outside of this code:
You are using for ... in ... on an array, for-in is for objects, when used on arrays, most browsers will include methods like .splice and .forEach, and not just the numeric 0...n index.
function splice () {}.draw(); doesn't end well.
Also, what is the colour of your page's background? You're setting the rgb colour of each circle to 100% white. You're also clearing the canvas... ...which might well mean that the whole thing is transparent. So if you've got a transparent canvas, white circles and a white background, chances are great you're not going to be seeing anything at all, if this is even working without spitting out an error.
It might make a lot more sense to move your logic around in a way that lets you follow what's going on.
If you make a circle constructor, don't have it do anything but make a new circle.
Inside of your update, create a circle.
THEN put it inside of your circle collection (not in the circle constructor).
In a large application, you will typically call update on ALL objects, and then call draw on ALL objects, rather than updating and drawing one at a time.
Imagine a game that didn't bother to check if you had been hit by a bullet before drawing you and letting you move, for instance.
So inside of your loop, you should have an update and a draw.
Inside of the update, create your circles add them to the list and update the positions of them.
Inside of the draw, draw the circles.
In the future, this will give you the benefit of having things like collision-detection, without having to redraw everything, multiple times per frame.
Also, don't do DOM-access inside of a function that's going to be called many, many times (Circle.draw).
That will demolish your framerate in the future.
Instead, pass the function a dependency (the canvas).
// inside of the main game's scope
var screen = document.getElementById(...).getContext("2d");
// inside of your new draw function, in the animation/game loop
var i = 0, allCircles = circleHolder.length, currentCircle;
for (; i < allCircles; i += 1) {
currentCircle = circleHolder[i];
currentCircle.draw(screen);
}

Bilateral filter algorithm

I'm trying to implement a simple bilateral filter in javascript. This is what I've come up with so far:
// For each pixel
for (var y = kernelSize; y < height-kernelSize; y++) {
for (var x = kernelSize; x < width-kernelSize; x++) {
var pixel = (y*width + x)*4;
var sumWeight = 0;
outputData[pixel] = 0;
outputData[pixel+1] = 0;
outputData[pixel+2] = 0;
outputData[pixel+3] = inputData[pixel+3];
// For each neighbouring pixel
for(var i=-kernelSize; i<=kernelSize; i++) {
for(var j=-kernelSize; j<=kernelSize; j++) {
var kernel = ((y+i)*width+x+j)*4;
var dist = Math.sqrt(i*i+j*j);
var colourDist = Math.sqrt((inputData[kernel]-inputData[pixel])*(inputData[kernel]-inputData[pixel])+
(inputData[kernel+1]-inputData[pixel+1])*(inputData[kernel+1]-inputData[pixel+1])+
(inputData[kernel+2]-inputData[pixel+2])*(inputData[kernel+2]-inputData[pixel+2]));
var curWeight = 1/(Math.exp(dist*dist/72)*Math.exp(colourDist*colourDist*8));
sumWeight += curWeight;
outputData[pixel] += curWeight*inputData[pixel];
outputData[pixel+1] += curWeight*inputData[pixel+1];
outputData[pixel+2] += curWeight*inputData[pixel+2];
}
}
outputData[pixel] /= sumWeight;
outputData[pixel+1] /= sumWeight;
outputData[pixel+2] /= sumWeight;
}
}
inputData is from a html5 canvas object and is in the form of rgba.
My images are either coming up with no changes or with patches of black around edges depending on how i change this formula:
var curWeight = 1/(Math.exp(dist*dist/72)*Math.exp(colourDist*colourDist*8));
Unfortunately I'm still new to html/javascript and image vision algorithms and my search have come up with no answers. My guess is there is something wrong with the way curWeight is calculated. What am I doing wrong here? Should I have converted the input image to CIElab/hsv first?
I'm no Javasript expert: Are the RGB values 0..255? If so, Math.exp(colourDist*colourDist*8) will yield extremely large values - you'll probably want to scale colourDist to the range [0..1].
BTW: Why do you calculate the sqrt of dist and colourDist if you only need the squared distance afterwards?
First of all, your images turn out black/weird in the edges because you don't filter the edges. A short look at your code would show that you begin at (kernelSize,kernelSize) and finish at (width-kernelSize,height-kernelSize) - this means that you only filter a smaller rectangle inside the image where your have a margin of kernelSize on each side which is unfilterred. Without knowing your javscript/html5, I would assume that your outputData array is initialized with zero's (which means black) and then not touching them would leave them black. See my link the comment to your post for code that does handle the edges.
Other than that, follow #nikie's answer - your probably want to make sure the color distance is clamped to the range of [0,1] - youo can do this by adding the line colourDist = colourDist / (MAX_COMP * Math,sqrt(3)) (directly after the first line to calculate it). where MAX_COMP is the maximal value a color component in the image can have (usually 255)
I've found the error in the code. The problem was I was adding each pixel to itself instead of its surrounding neighbours. I'll leave the corrected code here in case anyone needs a bilateral filter algorithm.
outputData[pixel] += curWeight*inputData[kernel];
outputData[pixel+1] += curWeight*inputData[kernel+1];
outputData[pixel+2] += curWeight*inputData[kernel+2];

JavaScript: Collision detection

How does collision detection work in JavaScript?
I can't use jQuery or gameQuery - already using prototype - so, I'm looking for something very simple. I am not asking for complete solution, just point me to the right direction.
Let's say there's:
<div id="ball"></div>
and
<div id="someobject0"></div>
Now the ball is moving (any direction). "Someobject"(0-X) is already pre-defined and there's 20-60 of them randomly positioned like this:
#someobject {position: absolute; top: RNDpx; left: RNDpx;}
I can create an array with "someobject(X)" positions and test collision while the "ball" is moving... Something like:
for(var c=0; c<objposArray.length; c++){
........ and code to check ball's current position vs all objects one by one....
}
But I guess this would be a "noob" solution and it looks pretty slow.
Is there anything better?
Here's a very simple bounding rectangle routine. It expects both a and b to be objects with x, y, width and height properties:
function isCollide(a, b) {
return !(
((a.y + a.height) < (b.y)) ||
(a.y > (b.y + b.height)) ||
((a.x + a.width) < b.x) ||
(a.x > (b.x + b.width))
);
}
To see this function in action, here's a codepen graciously made by #MixerOID.
An answer without jQuery, with HTML elements as parameters:
This is a better approach that checks the real position of the elements as they are being shown on the viewport, even if they're absolute, relative or have been manipulated via transformations:
function isCollide(a, b) {
var aRect = a.getBoundingClientRect();
var bRect = b.getBoundingClientRect();
return !(
((aRect.top + aRect.height) < (bRect.top)) ||
(aRect.top > (bRect.top + bRect.height)) ||
((aRect.left + aRect.width) < bRect.left) ||
(aRect.left > (bRect.left + bRect.width))
);
}
The first thing to have is the actual function that will detect whether you have a collision between the ball and the object.
For the sake of performance it will be great to implement some crude collision detecting technique, e.g., bounding rectangles, and a more accurate one if needed in case you have collision detected, so that your function will run a little bit quicker but using exactly the same loop.
Another option that can help to increase performance is to do some pre-processing with the objects you have. For example you can break the whole area into cells like a generic table and store the appropriate object that are contained within the particular cells. Therefore to detect the collision you are detecting the cells occupied by the ball, get the objects from those cells and use your collision-detecting function.
To speed it up even more you can implement 2d-tree, quadtree or R-tree.
You can try jquery-collision. Full disclosure: I just wrote this and released it. I didn't find a solution, so I wrote it myself.
It allows you to do:
var hit_list = $("#ball").collision("#someobject0");
which will return all the "#someobject0"'s that overlap with "#ball".
Mozilla has a good article on this, with the code shown below.
2D collision detection
Rectangle collision
if (rect1.x < rect2.x + rect2.width &&
rect1.x + rect1.width > rect2.x &&
rect1.y < rect2.y + rect2.height &&
rect1.height + rect1.y > rect2.y) {
// Collision detected!
}
Circle collision
if (distance < circle1.radius + circle2.radius) {
// Collision detected!
}
bcm's answer, which has 0 votes at this time, is actually a great, under-appreciated answer. It uses good old Pythagoras to detect when objects are closer than their combined bounding circles. Simple collision detection often uses rectangular collision detection, which is fine if your sprites tend to be, well, rectangular. If they are circular (or otherwise less than rectangular), such as a ball, an asteroid, or any other shape where the extreme corners are usually transparent, you may find this efficient routine to be the most accurate.
But for clarity, here is a more fully realized version of the code:
function doCollide(x1, y1, w1, x2, y2, w2) {
var xd = x1 - x2;
var yd = y1 - y2;
var wt = w2 + w1;
return (xd * xd + yd * yd <= wt * wt);
}
Where the parameters to pass in are the x,y and width values of two different sprite objects.
This is a lightweight solution I've come across -
function E() { // Check collision
S = X - x;
D = Y - y;
F = w + W;
return (S * S + D * D <= F * F)
}
The big and small variables are of two objects, (x coordinate, y coordinate, and w width)
From here.
//Off the cuff, Prototype style.
//Note, this is not optimal; there should be some basic partitioning and caching going on.
(function () {
var elements = [];
Element.register = function (element) {
for (var i=0; i<elements.length; i++) {
if (elements[i]==element) break;
}
elements.push(element);
if (arguments.length>1)
for (var i=0; i<arguments.length; i++)
Element.register(arguments[i]);
};
Element.collide = function () {
for (var outer=0; outer < elements.length; outer++) {
var e1 = Object.extend(
$(elements[outer]).positionedOffset(),
$(elements[outer]).getDimensions()
);
for (var inner=outer; inner<elements.length; innter++) {
var e2 = Object.extend(
$(elements[inner]).positionedOffset(),
$(elements[inner]).getDimensions()
);
if (
(e1.left+e1.width)>=e2.left && e1.left<=(e2.left+e2.width) &&
(e1.top+e1.height)>=e2.top && e1.top<=(e2.top+e2.height)
) {
$(elements[inner]).fire(':collision', {element: $(elements[outer])});
$(elements[outer]).fire(':collision', {element: $(elements[inner])});
}
}
}
};
})();
//Usage:
Element.register(myElementA);
Element.register(myElementB);
$(myElementA).observe(':collision', function (ev) {
console.log('Damn, '+ev.memo.element+', that hurt!');
});
//detect collisions every 100ms
setInterval(Element.collide, 100);
This is a simple way that is inefficient, but it's quite reasonable when you don't need anything too complex or you don't have many objects.
Otherwise there are many different algorithms, but most of them are quite complex to implement.
For example, you can use a divide et impera approach in which you cluster objects hierarchically according to their distance and you give to every cluster a bounding box that contains all the items of the cluster. Then you can check which clusters collide and avoid checking pairs of object that belong to clusters that are not colliding/overlapped.
Otherwise, you can figure out a generic space partitioning algorithm to split up in a similar way the objects to avoid useless checks. These kind of algorithms split the collision detection in two phases: a coarse one in which you see what objects maybe colliding and a fine one in which you effectively check single objects.
For example, you can use a QuadTree (Wikipedia) to work out an easy solution...
Take a look at the Wikipedia page. It can give you some hints.
hittest.js; detect two transparent PNG images (pixel) colliding.
Demo and download link
HTML code
<img id="png-object-1" src="images/object1.png" />
<img id="png-object-2" src="images/object2.png" />
Init function
var pngObject1Element = document.getElementById( "png-object-1" );
var pngObject2Element = document.getElementById( "png-object-2" );
var object1HitTest = new HitTest( pngObject1Element );
Basic usage
if( object1HitTest.toObject( pngObject2Element ) ) {
// Collision detected
}

Categories

Resources