I'm making a sketch with the p5.js library and ml5's poseNet. I have the variable noseX — which indicates the x-coordinate position of the nose on the canvas — and a preloaded array of 60 images. I would like to vertically divide the canvas into 60 sections. For each cnvSection noseX is on, I want the image img[i] corresponding to that cnvSection[i] to to be drawn on the canvas.
So basically if noseX is on cnvSection[5], draw img[5] on the canvas etc.
Here's my function, at the moment I have only been able to draw the vertical lines indicating the canvas sections.
let cnvWidth = 1440;
let cnvHeight = 900;
function nosePosition() {
let sectionWidth = cnvWidth / 60;
let cnvSection = [];
for (let i = 0; i < cnvWidth; i = i + sectionWidth) {
line(i, 0, i, cnvHeight);
}
}
Many thanks!
Try the following:
noseX = 50;
function setup() {
createCanvas(700, 400);
}
function draw() {
background(220);
nosePosition();
fill('red');
ellipse(noseX, height / 2, 6);
}
function nosePosition() {
let sectionWidth = width / 60;
for (let i = 0; i < width; i += sectionWidth) {
line(i, 0, i, height);
}
const noseSection = floor(noseX / sectionWidth);
rect(noseSection * sectionWidth, 0, sectionWidth, height);
}
To simplify I use a hardcoded x value for the nose. In your case that would come from ml5 posenet. You don't need the sections in an array. You can simlpy calculate in which section the nose currently is and then draw the image accordingly. I am drawing a rect - again, to simplify the example.
Maybe copy and paste this into the p5 web editor and play around with the noseX value and see if that works for you.
Related
function setup() {
createCanvas(5000, 2100);
randomX = random(100, 1000)
randomY = random(100, 1000)
randomSpeed = random(1, 10)
randomSize = random(10, 100)
}
function draw() {
background(0);
fill(255)
ellipse(randomX, randomY, randomSize)
randomX = randomX + randomSpeed
if (randomX > 5000) {
randomX = 0
}
}
In the draw() function, there is an ellipse i need to be drawn on the canvas a random amount of times, WITH A LIMIT, on the canvas to make the starry night effect, how do I do that?
If I understand you right, you want to draw a bunch of ellipses on the canvas in random positions. I've answered assuming that's what you're asking. Apologies if that's not what you want.
What this program does is create two lists that hold data about the ellipses. In setup() we choose a random number of ellipses to draw. We make that many random sizes and positions then put them into the lists. When it comes time to draw the ellipses, we loop through the lists containing information about them and use that to draw a number of ellipses.
const ellipseMinSize = 1;
const ellipseMaxSize = 10;
const ellipseMinAmount = 10;
const ellipseMaxAmount = 100;
// Create some lists so we can remember where the ellipses are and how big they are
var ellipseSizes = [];
var ellipsePositions = [];
function setup() {
createCanvas(500, 500);
// Choose an amount of ellipses to make
var ellipseAmount = random(ellipseMinAmount, ellipseMaxAmount);
for (var i = 0; i < ellipseAmount; i ++) {
// Choose a random size and position, then remember those
var ellipseSize = random(ellipseMinSize, ellipseMaxSize);
var ellipsePosition = createVector(random(0, width), random(0, height));
ellipseSizes.push(ellipseSize);
ellipsePositions.push(ellipsePosition);
}
}
function draw() {
background(0);
fill(255);
// Then loop through the remembered positions and sizes, and draw an ellipse with those parameters
for (var i = 0; i < ellipseSizes.length; i ++) {
var ellipseSize = ellipseSizes[i];
var ellipsePosition = ellipsePositions[i];
ellipse(ellipsePosition.x, ellipsePosition.y, ellipseSize, ellipseSize);
}
}
I've spent the past two weeks making a game with a few friends using the HTML canvas tag and JavaScript. None of us have any prior experience with a project of this scale, so considerations of browser/screen-size compatibility wasn't on our minds. The game runs and looks fine on our laptops (all have similar screen sizes), but it looked bad when we sent a link to another friend whose screen size differs greatly from what we had in mind.
To discuss the layout of the game in greater detail, it's set up with the canvas element acting as the actual game with a series of divs sitting below the canvas to represent things like a dialogue box or the pause menu (only one of these divs is shown at a time with the others being hidden).
The game is gridbased in that every object, from the wall tiles to enemies, has a size relative to some constant blockWidth (defined below) which itself is relative to the desired amount of squares on-screen, numSquares (also defined below).
Changing the canvas's height and width properties in JavaScript did successfully fix a ratio of the canvas size and ensure that the wall and floor textures loaded in their proper place. The player and other NPCs, however, appear at odd places onscreen, sometimes not showing up onload at all.
I'm not quite sure what to attribute this problem to, but I think it has something to do with canvas' coordinate system not mixing well with the admittedly poorly executed block system we put in place.
//some relevant misc variables
var numSquares = 30;
const blockWidth = 1132 / numSquares * 0.75;
screen.width = 1132;
screen.height = 600;
//some relevant player variables
stats.x = 678;
stats.y = 600;
stats.width = blockWidth * 3 * 0.37;
stats.height = blockWidth * 3;
Again, I suspect that the problem has something to do with the fact that the tiles that render correctly (i.e. wall and floor textures) have their coordinates in terms of blockWidth whereas the tiles that render incorrectly (i.e. the player) have their coordinates as regular numbers.
Is there a way to go about adjusting our game for different monitor sizes other than revamping the entire coordinate system?
try this meta which will solve cross platform screen (laptop, Computer, Tab, Mobile)problem:
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
Your main problem is using hard coded values for your variables. If stats.x = 678;and your screen is 480px wide (for example), the stats will fall out the screen.
Also: screen.width and screen.height is a hardcoded number. Probably what you need is something like: screen.width = window.innerWidth and screen.height = window.innerHeight
What I'm missing from your relevant misc variables: In your code you have this:
var numSquares = 30;
const blockWidth = 1132 / numSquares * 0.75;
Where 1132 is in fact the screen.width. This means that you don't have a grid as you say. This means you have only 1 (one) row of squares. Alternatively you have 30 squares per row, and in this case you have a grid.
Also, it would be nice to see how you draw your grid. I would do it like this:
for (let y = 0; y < ch; y += cw / numSquares) {
for (let x = 0; x < cw; x += cw / numSquares) {
let o = { x: x, y: y, w: blockWidth, h: blockWidth };
drawRect(o);
}
}
Where drawRect is a custom function to draw a rect. Since your blockWidth = screen.width / numSquares * 0.75; I'm assuming you let a gap between rects, but this is assuming.
As I've commented before you can't give your stats hard coded values. You will need to calculate these values in function of your grid cells. For example your stats.x may be the x of the 5'th column of your grid and the stats.y may be the y of the 3-rd row. But this is again assuming and I may be wrong.
Next comes a code example. Please take a look and let me know if this is what you need.
const screen = document.getElementById("canvas");
const ctx = screen.getContext("2d");
let cw = (screen.width = window.innerWidth);
let ch = (screen.height = window.innerHeight);
let stats = {};
let numSquares = 30;
let blockWidth;
generateGrid();
function Init() {
// a function to re-generate the grid and re-draw the stats when screen.width and/or screen.height changed
cw = screen.width = window.innerWidth;
ch = screen.height = window.innerHeight;
generateGrid();
drawStats(5, 3);
}
// recalculate everything on resize
setTimeout(function() {
Init();
addEventListener("resize", Init, false);
}, 15);
// some useful functions
function drawRect(o) {
ctx.beginPath();
ctx.fillStyle = "rgba(0,0,0,.05)";
ctx.fillRect(o.x, o.y, o.w, o.h);
}
function drawStats(x, y) {
stats.x = x * cw / numSquares;
stats.y = y * cw / numSquares;
stats.width = blockWidth * 3 * 0.37;
stats.height = blockWidth * 3;
ctx.fillStyle = "red";
ctx.beginPath();
ctx.fillRect(stats.x, stats.y, stats.width, stats.height);
}
function generateGrid() {
blockWidth = cw / numSquares * 0.75;
for (let y = 0; y < ch; y += cw / numSquares) {
for (let x = 0; x < cw; x += cw / numSquares) {
let o = { x: x, y: y, w: blockWidth, h: blockWidth };
drawRect(o);
}
}
}
*{margin:0;padding:0}
<canvas id="canvas"></canvas>
If this is not what you need, please update your question, and add more explanations and more code.
I'm trying to draw a Perlin noise image in p5.js. I followed Daniel Shiffman's tutorials and he gives an example about how to set up 2D Perlin noise here (for convenience I have loaded this into this JSFiddle sketch).
Now I don't need the entire canvas filled with Perlin noise, but I just need a (smaller) image of perlin noise that I can use like an image file in the canvas. So, in the setup function I used createImage() and then the exact same algorithm to load the Perlin noise into the image. However, when I display this now, the noise looks totally distorted.
Here is my code:
// noise code originally by
// Daniel Shiffman
// http://codingtra.in
// http://patreon.com/codingtrain
// Code for: https://youtu.be/ikwNrFvnL3g
var inc = 0.01;
var noiseImg;
function setup() {
createCanvas(640, 360);
pixelDensity(1);
background("red");
var yoff = 0;
noiseImg = createImage(200, 200);
noiseImg.loadPixels();
for (var y = 0; y < height; y++) {
var xoff = 0;
for (var x = 0; x < width; x++) {
var index = (x + y * width) * 4;
var r = noise(xoff, yoff) * 255;
noiseImg.pixels[index + 0] = r;
noiseImg.pixels[index + 1] = r;
noiseImg.pixels[index + 2] = r;
noiseImg.pixels[index + 3] = 255;
xoff += inc;
}
yoff += inc;
}
noiseImg.updatePixels();
}
function draw() {
image(noiseImg, 0, 0);
}
JSFiddle
Does anyone know, why it is distorted although the noise algorithm hasn't changed and what I can do about it?
Thanks!
The width and height variables are for the overall campus, in your case 640 and 360 respectively. You use these variables to loop over every pixel in that space, but then you're setting the pixel array of an image that's only 200 by 200 pixels. (Or in your JSFiddle, 300 by 300 pixels.)
That's what's causing the distortion: you're drawing a 640x360 drawing of Perlin noise to a 200x200 image. This is resulting in some undefined behavior, which manifests as the distortion you're seeing.
To fix the problem, just loop over the bounds of the image, not the sketch itself.
I tried both of these in canvas and nothing showed, also I doubt it is even efficient :/. I am trying to make rain that comes down the screen.. Wondering what is the most efficient way of doing this. I am a beginner at animation and would really appreciate help.
I suspect that creating a rain object would be best, each with the quality of coming down the screen then coming to the top and then an array with them...maybe with random x values withing the canvas width and y values of 0 but I don't know how to implement that. Please help!
xofRain = 20;
startY = 0;
ctx.beginPath();
ctx.moveTo(xofRain, startY);
ctx.lineTo(xofRain, startY + 20);
ctx.closePath();
ctx.fillStyle = "black";
ctx.fill();
function rain(xofRain){
startY = canvas.height();
ctx.moveTo(xofRain, startY);
ctx.beginPath();
ctx.lineTo(xofRain, startY + 3);
ctx.closePath();
ctx.fillStyle = "blue";
ctx.fill();
}
Here comes your answer, this snow rain is created using pure HTML5 Canvas, the technique used to achieve this animation is called "Double Buffer Animation". First it is good to know what is Double Buffer animation technique.
Double Buffer Technique: This is an advanced technique to make animation clear and with less flickers in it. In this technique 2 Canvas is used, one is displayed on webpage to show the result and second one is used to create animation screens in backed process.
How this will help full, suppose we have to create a animation with very high number of move, as in our Snow Fall example, there are number of Flakes are moving with there own speed, so keep them moving, we have to change position of each flake and update it on the canvas, this is quite heavy process to deal with.
So Now instead of updating each Flake directly on our page canvas, we will create a buffer Canvas, where all these changes take place and we just capture a Picture from Buffer canvas after 30ms and display it on our real canvas.
This way our animation will be clear and without flickers. So here is a live example of it.
http://aspspider.info/erishaan8/html5rain/
Here is the code of it:
<!DOCTYPE html>
<html>
<head>
<meta charset=utf-8 />
<title>HTML5 Rain</title>
<!--[if IE]>
<script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
<style>
article, aside, figure, footer, header, hgroup,
menu, nav, section { display: block; }
</style>
<script type="text/javascript">
var canvas = null;
var context = null;
var bufferCanvas = null;
var bufferCanvasCtx = null;
var flakeArray = [];
var flakeTimer = null;
var maxFlakes = 200; // Here you may set max flackes to be created
function init() {
//Canvas on Page
canvas = document.getElementById('canvasRain');
context = canvas.getContext("2d");
//Buffer Canvas
bufferCanvas = document.createElement("canvas");
bufferCanvasCtx = bufferCanvas.getContext("2d");
bufferCanvasCtx.canvas.width = context.canvas.width;
bufferCanvasCtx.canvas.height = context.canvas.height;
flakeTimer = setInterval(addFlake, 200);
Draw();
setInterval(animate, 30);
}
function animate() {
Update();
Draw();
}
function addFlake() {
flakeArray[flakeArray.length] = new Flake();
if (flakeArray.length == maxFlakes)
clearInterval(flakeTimer);
}
function blank() {
bufferCanvasCtx.fillStyle = "rgba(0,0,0,0.8)";
bufferCanvasCtx.fillRect(0, 0, bufferCanvasCtx.canvas.width, bufferCanvasCtx.canvas.height);
}
function Update() {
for (var i = 0; i < flakeArray.length; i++) {
if (flakeArray[i].y < context.canvas.height) {
flakeArray[i].y += flakeArray[i].speed;
if (flakeArray[i].y > context.canvas.height)
flakeArray[i].y = -5;
flakeArray[i].x += flakeArray[i].drift;
if (flakeArray[i].x > context.canvas.width)
flakeArray[i].x = 0;
}
}
}
function Flake() {
this.x = Math.round(Math.random() * context.canvas.width);
this.y = -10;
this.drift = Math.random();
this.speed = Math.round(Math.random() * 5) + 1;
this.width = (Math.random() * 3) + 2;
this.height = this.width;
}
function Draw() {
context.save();
blank();
for (var i = 0; i < flakeArray.length; i++) {
bufferCanvasCtx.fillStyle = "white";
bufferCanvasCtx.fillRect(flakeArray[i].x, flakeArray[i].y, flakeArray[i].width, flakeArray[i].height);
}
context.drawImage(bufferCanvas, 0, 0, bufferCanvas.width, bufferCanvas.height);
context.restore();
}
</script>
</head>
<body onload="init()">
<canvas id="canvasRain" width="800px" height="800px">Canvas Not Supported</canvas>
</body>
</html>
Also if you find this help full, accept as Answer and make it up. o_O
Cheers!!!
I'm not sure what "most efficient" is. If it was me I'd do it in WebGL but whether or not that's efficient is not clear to me.
In either case I'd try to use a stateless formula. Creating and updating state for every raindrop is arguably slow.
const ctx = document.querySelector("canvas").getContext("2d");
const numRain = 200;
function render(time) {
time *= 0.001; // convert to seconds
resizeCanvasToDisplaySize(ctx.canvas);
const width = ctx.canvas.width;
const height = ctx.canvas.height;
ctx.fillStyle = "black";
ctx.fillRect(0, 0, width, height);
resetPseudoRandom();
const speed = time * 500;
ctx.fillStyle = "#68F";
for (let i = 0; i < numRain; ++i) {
const x = pseudoRandomInt(width);
const y = (pseudoRandomInt(height) + speed) % height;
ctx.fillRect(x, y, 3, 8);
}
requestAnimationFrame(render);
}
requestAnimationFrame(render);
let randomSeed_ = 0;
const RANDOM_RANGE_ = Math.pow(2, 32);
function pseudoRandom() {
return (randomSeed_ =
(134775813 * randomSeed_ + 1) %
RANDOM_RANGE_) / RANDOM_RANGE_;
};
function resetPseudoRandom() {
randomSeed_ = 0;
};
function pseudoRandomInt(n) {
return pseudoRandom() * n | 0;
}
function resizeCanvasToDisplaySize(canvas) {
const width = canvas.clientWidth;
const height = canvas.clientHeight;
if (canvas.width !== width || canvas.height !== height) {
canvas.width = width;
canvas.height = height;
}
}
body { margin: 0; }
canvas { width: 100vw; height: 100vh; display: block; }
<canvas></canvas>
Note that I could have used ctx.moveTo(x, y); ctx.lineTo(x, y + 8); for each line and then at the end of the loop called ctx.stroke(). I didn't do that because I'm assuming it would be less efficient than using ctx.fillRect. In order for the canvas to draw lines it actually has to allocate a dynamic path (you call ctx.beginPath). It then has to record all the lines you add. Then it has to expand those lines into vertices of various kinds to rasterize the lines. You can basically see the various algorithms it uses here. Conversely none of that has to happen with ctx.fillRect. No allocations have to happen (not saying they don't happen, just saying they don't have to). The canvas can just use a single pre-allocated quad and draw it on the GPU by passing the correct matrix to draw whatever rectangle you ask of it. Of course they're might be more overhead calling ctx.fillRect 200 times rather than ctx.moveTo, ctx.lineTo 200s + ctx.stroke once but really that's up to the browser.
The rain above may or may not be a good enough rain effect. That wasn't my point in posting really. The point is efficiency. Pretty much all games that have some kind of rain effect do some kind of stateless formula for their rain. A different formula would generate different or less repetitive rain. The point is it being stateless.
Say I have this image:
I'd like to recognize the position of the red ball in the image, I could measure the size of the ball(in pixel) in ahead.
I know that I could draw the image to a canvas, then I could get the pixel color data with context.getImageData, but then what should I do? which algorithm sould I use? I'm new to image processing, thanks a lot.
Here's code dedicated to getting that ball position. The output position will logged to the console so have your JS console open! This code has some values in it that you can play with. I chose some that work for your image such as the rough diameter of the ball being 14 pixels and the threshold for each colour component.
I saved the image as "test.jpg" but you can change the code to the correct image path on line 11.
<!DOCTYPE html>
<html>
<body>
<canvas width="800" height="600" id="testCanvas"></canvas>
<script type="text/javascript">
var img = document.createElement('img');
img.onload = function () {
console.log(getBallPosition(this));
};
img.src = 'test.jpg';
function getBallPosition(img) {
var canvas = document.getElementById('testCanvas'),
ctx = canvas.getContext('2d'),
imageData,
width = img.width,
height = img.height,
pixelData,
pixelRedValue,
pixelGreenValue,
pixelBlueValue,
pixelAlphaValue,
pixelIndex,
redThreshold = 128,
greenThreshold = 40,
blueThreshold = 40,
alphaThreshold = 180,
circleDiameter = 14,
x, y,
count,
ballPosition,
closestBallCount = 0,
closestBallPosition;
// Draw the image to the canvas
canvas.width = width;
canvas.height = height;
ctx.drawImage(img, 0, 0);
// Get the image data
imageData = ctx.getImageData(0, 0, width, height);
pixelData = imageData.data;
// Find the ball!
for (y = 0; y < height; y++) {
// Reset the pixel count
count = 0;
// Loop through the pixels on this line
for (x = 0; x < width; x++) {
// Set the pixel data starting point
pixelIndex = (y * width * 4) + (x * 4);
// Grab the red pixel value
pixelRedValue = pixelData[pixelIndex];
pixelGreenValue = pixelData[pixelIndex + 1];
pixelBlueValue = pixelData[pixelIndex + 2];
pixelAlphaValue = pixelData[pixelIndex + 3];
// Check if the value is within out red colour threshold
if (pixelRedValue >= redThreshold && pixelGreenValue <= greenThreshold && pixelBlueValue <= blueThreshold && pixelAlphaValue >= alphaThreshold) {
count++;
} else {
// We've found a pixel that isn't part of the red ball
// so now check if we found any red data
if (count === circleDiameter) {
// We've found our ball
return {
x: x - Math.floor(circleDiameter / 2),
y: y
};
} else {
// Any data we found was not our ball
if (count < circleDiameter && count > closestBallCount) {
closestBallCount = count;
closestBallPosition = {
x: x - Math.floor(circleDiameter / 2),
y: y
};
}
count = 0;
}
}
}
}
return closestBallPosition;
}
</script>
</body>
</html>
Well i would go and cluster pixels of that color. For example, you could have a look up table where you store red (or in the range of a treshold) pixels (coordinates being the look up key) and an integer value being the cluster id whenever you encounter a pixel without any known red neighbours it starts a new cluster, all other red pixels get the cluster id of a red pixel they are the neighbour of. Depending of you algorithms kernel:
A) XXX B) X
XOX XOX
XXX X
you might need to deal (case B) with a pixel connecting two prior not connected clusters. You would have to replace the cluster id of one of that clusters.
After that you have clusters of pixels. These you can analyse. In case of a round shape i would look for the median in x and y for each cluster and check if all the pixels of that cluster are in the radius.
This will fail if the red ball (or part of it) is in front of another red object. You would than need more complex algorithms.