Getting pixel values of images in p5js - javascript

I have been working on some code that is supposed to draw a dot(ellipse) on each pixel that is black. I am new to the get function, and suspect that I might have made a mistake using it. Does anyone know why my code cannot successfully "dot" the map of India?
function setup() {
createCanvas(400, 400);
}
window.onload = function() {
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
var img = document.getElementById("map");
ctx.drawImage(img, 10, 10, 150000, 1800000);
}
function draw() {
for (var x = 0; x < 100; x++){
for (var y = 0; y < 100; y++){
if(black(get(x,y))==255){
ellipse(x , y, 10, 100);
}
}
}
}
<style>
p {
position:absolute;
z-index:3;
}
img {
position:absolute;
z-index: -1;
}
</style>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- PLEASE NO CHANGES BELOW THIS LINE (UNTIL I SAY SO) -->
<script language="javascript" type="text/javascript" src="libraries/p5.min.js"></script>
<script language="javascript" type="text/javascript" src="p5js-temp-sadfsfdsad8981306098870070843.js"></script>
<!-- OK, YOU CAN MAKE CHANGES BELOW THIS LINE AGAIN -->
<!-- This line removes any default padding and style.
You might only need one of these values set. -->
<style> body { padding: 0; margin: 0; } </style>
</head>
<body>
<img id="map" width="220" height="277" src="https://geology.com/world/india-map.gif" alt="The map">
<!--<img src="https://geology.com/world/india-map.gif">-->
<p id="area"></p> <br>
<p id="perimeter"></p>
</body>
</html>
Edit: I changed the code to
window.onload = function() {
var c = canvas;
var ctx = c.getContext("2d");
var img = document.getElementById("map");
ctx.drawImage(img, 10, 10, 150000, 1800000);
}
function draw() {
for (var x = 0; x < 100; x++){
for (var y = 0; y < 100; y++){
if(black(get(x,y))==255){
ellipse(x , y, 10, 100);
}
}
}
}
<style> body { padding: 0; margin: 0; } </style>
</head>
<body>
<img id="map" width="220" height="277" src="https://geology.com/world/india-map.gif" alt="The map">
<!--<img src="https://geology.com/world/india-map.gif">-->
<p id="area"></p> <br>
<p id="perimeter"></p>
</body>
</html>
<style>
p{
position:absolute;
z-index:3;
}
img{
position:absolute;
z-index: -1;
}
</style>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script language="javascript" type="text/javascript" src="libraries/p5.min.js"></script>
<script language="javascript" type="text/javascript" src="p5js-temp-sadfsfdsad1466343293693433275.js"></script>
<!-- This line removes any default padding and style.
You might only need one of these values set. -->
However, I get a black rectangle on the map and no dots. Does anyone know why this is happening?

A somewhat minor tweak of Nick Parson's excellent answer, but one that uses pixels rather than get. It runs almost instantly, which shows that get() is the culprit.
//tweak of Code from Nick Parsons
let img;
function preload() {
img = loadImage('india-map.gif');
}
function setup() {
createCanvas(400, 400);
}
function draw() {
background(220);
image(img, 0, 0, width, height);
loadPixels();
const d = pixelDensity();
for (let x = 0; x < width; x++) {
for (let y = 0; y < height; y++) {
const i = 4 * d*(y * d*width + x);
const [r, g, b] = [pixels[i], pixels[i + 1], pixels[i + 2]]; // get colors
if (r <= 80 && b <= 80 && g <= 80) { // if r g b all less than 80 then color will appear black
noStroke();
fill(255, 0, 0);
ellipse(x, y, 1);
}
}
}
noLoop();
}

Since you're using p5js you should use the methods and functions it has to offer. There is no need to get the canvas like you are when using p5js. p5js provides a reference to it when you type canvas.
At the moment you are using an image tag to display your image. Instead, you want to display the image to the canvas. You can do this by using p5's preload method to load the image. Then, once it has been loaded, you can reference it in your draw method.
Your draw method will constantly run on a loop. However, you don't need a loop as you only need to do your computations once. Thus you can use p5's method noLoop() to stop draw from looping.
Lastly, to get the color of a particular pixel you need to use get(x, y). This will give you an array of red, green, and blue values. A black pixel is where all three of these values are 0. However, your image has pixels which are not strictly black. For example, if your r g b (red, green, blue) color values are 1, 1, 1 your color would still look black. Thus, to check if a given color is black, you need to check if all these values are less than a particular number such as 80.
If you incorporate all these ideas into your code you should end up with something like this:
let img;
function preload() {
img = loadImage('india-map.gif');
}
function setup() {
createCanvas(400, 400);
}
function draw() {
background(220);
image(img, 0, 0, width, height);
for(let x = 0; x < width; x++) {
for(let y = 0; y < height; y++) {
const [r, g, b] = get(x, y); // get colors
if(r <= 80 && b <= 80 && g <= 80) { // if r g b all less than 80 then color will appear black
noStroke(); // remove black outline from thing we are about to draw (the ellipse)
fill(255, 0, 0); // make the ellipse red
ellipse(x, y, 1); // draw the ellipse at the pixle
}
}
}
noLoop();
}
You can find a working example here. But please note, this is for demonstration purposes. Please take the time to understand the code. Also, as we are looking at every pixel's color on the canvas using get this takes a LONG time to compute once ran. (consider looking at every 2nd pixel and drawing the ellipse a little larger)

Related

P5JS make sure the setup is not cleared but the draw function is on every frame

I'm trying to create a wobbly circle with multiple ellipses in the background, however I can't add the for loop to the draw function otherwise it will be running in every frame, and if I add it to setup then the wobbly circle leaves a trace. And if I use clear() then it removes my background. How can I solve this, please?
function setup() {
createCanvas(600, 600);
for (var i = 0; i < 1000; i++){
fill(255);
ellipse(random(0, width), random(0, width), random(5, 5), random(5, 5));
}
x = width/2; // Circle center x
y = height/2; // Circle center y
r = 100; // Circle radius
verts = 200; // Number of vertices for drawing the circle
wobbleSlider = createSlider(0, 200, 100, 1); // How much the circle radius can vary
smthSlider = createSlider(1, 500, 70, 1); // How smooth the noise function is (higher is smoother)
t = 1;
}
function draw() {
let wobble = wobbleSlider.value()
let smth = smthSlider.value();
//clear();
t += 0.01
beginShape()
for(let i = 0; i < verts; i++){
let f = noise(50*cos(i/verts*2*PI)/smth + t, 50*sin(i/verts*2*PI)/smth + t)
vertex(x + (r+wobble*f) * cos( (i/verts) * 2 * PI ), y + (r+wobble*f) * sin( (i/verts) * 2 * PI ) )
}
endShape(CLOSE)
}
html, body {
margin: 0;
padding: 0;
}
canvas {
display: block;
}
<!DOCTYPE html>
<html lang="en">
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/addons/p5.sound.min.js"></script>
<link rel="stylesheet" type="text/css" href="style.css">
<meta charset="utf-8" />
</head>
<body>
<script src="sketch.js"></script>
</body>
</html>
enter image description here
What you want is some sort of layering system that allows you to have a fixed background and draw over it things that chage. You can achieve this with graphics. It basically allows you to create an independent "canvas" and draw over it, then you can render it like an image in your animation.
First you use createGraphics which is similar to createCanvas with the difference that you can store the result in a variable like: let myGraphics = createGraphics(600,600). Then you draw over it like in your canvas. For example ellipse(...) becomes myGraphics.ellipse(...). Then you can render it using image like image(myGraphics, 0, 0).
See the working code below
If this answers your question dont forget to mark it as the answer using the green check at the left of the answer so others can reference from it in the future :)
let background;
function setup() {
createCanvas(600, 600);
background = createGraphics(600, 400);
for (var i = 0; i < 1000; i++){
background.fill(255);
background.ellipse(random(0, width), random(0, width), random(5, 5), random(5, 5));
}
x = width/2; // Circle center x
y = height/2; // Circle center y
r = 100; // Circle radius
verts = 200; // Number of vertices for drawing the circle
wobbleSlider = createSlider(0, 200, 100, 1); // How much the circle radius can vary
smthSlider = createSlider(1, 500, 70, 1); // How smooth the noise function is (higher is smoother)
t = 1;
}
function draw() {
clear();
image(background,0,0);
let wobble = wobbleSlider.value()
let smth = smthSlider.value();
//clear();
t += 0.01
beginShape()
for(let i = 0; i < verts; i++){
let f = noise(50*cos(i/verts*2*PI)/smth + t, 50*sin(i/verts*2*PI)/smth + t)
vertex(x + (r+wobble*f) * cos( (i/verts) * 2 * PI ), y + (r+wobble*f) * sin( (i/verts) * 2 * PI ) )
}
endShape(CLOSE)
}
html, body {
margin: 0;
padding: 0;
}
canvas {
display: block;
}
<!DOCTYPE html>
<html lang="en">
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/addons/p5.sound.min.js"></script>
<link rel="stylesheet" type="text/css" href="style.css">
<meta charset="utf-8" />
</head>
<body>
<script src="sketch.js"></script>
</body>
</html>

FATAL ERROR syntax error, unexpected '<', expecting end of file on line number 1

Can anyone please help me identify why this code isn't working? It's copied directly from a youtube tutorial so I can't see where I've gone wrong!
Thank you!
I basically want to create matrix rain, but my own version of symbols within it. Would someone be able to correctly identify what's wrong with this code? I have tried to input it into http://phptester.net/
<DOCTYPE html>
<html>
<head>
<title>Matrix Rain</title>
<style>
/*basic resets */
* {margin:0; padding: 0;}
/* adding a black bg to the background to make things clearer */
body {background: black;}
canvas {display: block;}
</style>
</head>
<body>
<canvas id="c"></canvas>
<script type="text/javascript">
var c = document.getElementbyId("c");
var ctx = c.getCont("2d");
//making the canvas full screen
c.height = window.innerHeight;
c.width = windows.innerWidth;
var ganoti = "诶比西迪艾弗艾尺艾勒艾娜吉吾艾儿"
//converting the string into an array of single characters
chinese = chinese.split ("");
var font_size = 10;
var columns = c.width/font_size; //number of columns for rain
//an array of drops - one for column
var drops = [];
//x below is the x coordinate
//1 = y co-ordinate of the drop(same for every drop initially)
for (var = x; x < columns; x++)
drops[x] = 1;
//drawing the characters
function draw () {
//Black BG for the canvas
//translucent BG to show trail
ctx.fillStyle - "rgba(0,0,0,0.05)";
ctx.fillRect(0,0, c.wdith, c.height);
ctx.fillstyle = "DB7093";
ctx.font = font_size + "px arial";
//looping over drops
for (var i = 0; i < drops.length; i++) {
var text = ganoti[Math.floor(Math.random()*ganoti.lenth)];
//x = i*font_size, y = value of drops[i]*font_size
ctx.fillText(text, i*font_size, drops[i]*font_size);
//sending the drop back to the top randomly after it has crossed
screen // it should be in comment line
//adding a randomness to the reset to make the drops scattered on the
Y axis // it should be in comment line
if (drops[i]*font_size > c.height && Math.random() > 0.975)
drops[i] = 0;
//incrementing Y coordinate
drops[i]++
}
}
setInterval(draw, 33);
</script>
</body>
</html>
}
}
phptester.net requires PHP, your code is HTML, besides has redundant double } in the end.

How to stop a rectangle from moving off of the canvas in Javascript using the p5 library

Here is my Javascript:
var x;
var y;
var ymod = 0;
function setup() {
createCanvas(600, 600);
x = 10;
y = 12;
}
function draw() {
background(48, 48, 48);
noStroke();
fill(255, 255, 255);
rect(x, y, 10, 30);
y = y + ymod;
}
function keyPressed() {
if (keyCode === UP_ARROW) {
ymod = -1;
} else if (keyCode === DOWN_ARROW) {
ymod = 1;
}
}
Here is my HTML:
<!DOCTYPE html>
<head>
<meta charset="UTF-8">
<title>Game</title>
<script src="libraries/p5.js" type="text/javascript"></script>
<script src="libraries/p5.dom.js" type="text/javascript"></script>
<script src="libraries/p5.sound.js" type="text/javascript"></script>
</head>
<body>
<script src="sketch.js" type="text/javascript"></script>
</body>
</html>
I would like the rectangle to stop when it hits the edges of the canvas (ie: y=0 and y=570). I have tried to add
if (y=0) {
ymod = 0;
}
to the end of the draw function but, it breaks everything.
This can be done using the constrain function in p5.js, to constrain y between 0 and 570, like this:
y = constrain(y, 0, 570);
The reason why if (y = 0) breaks everything is because a single equals sign sets y to 0 instead of comparing. To test if y = 0, you'll need to use a triple equals sign instead (y === 0).

Javascript canvas animating

I want to make a loading bar for my web application and I want to use a html canvas for this. This is the script that I use to fill up the canvas:
<script>
var canvas = document.getElementById("bar");
var c = canvas.getContext("2d");
var xPos = 0;
draw = function() {
if(xPos < 300){
c.rect(0, 0, xPos, 30);
c.fill(255,0,0);
xPos += 0.5;
}
};
</script>
I tested this code on a online code converter (khan academy) and it worked (of course without the first 2 lines and c. in front of most things), and that is also my trouble I don't know where I have to put c. in front of?
I simplified the page a little bit:
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" type="text/css" href="test.css">
</head>
<body>
<canvas id="bar"></canvas>
<script>
var canvas = document.getElementById("bar");
var c = canvas.getContext("2d");
c.fillStyle = "#ff0000"
draw = function(){
if(xPos < 300){
c.fillRect(0, 0, xPos, 30);
xPos += 0.5;
}
};
</script>
</body>
</html>
Whatever you are trying to draw... this:
draw = function(){
if(xPos < 300) {
c.fillRect(0, 0, xPos, 30);
xPos += 0.5;
}
};
... it is a definition of variable in global context (context of window object), then assigning a function to it. That's all - it only defines the behavior.
What you need also needs to execute that (a sidenote: to execute it after the canvas is actually created - when you put code in a script tag after canvas tag - it's sufficient and you did it already).
To execute the function use:
draw();
Or don't wrap code in function at all (unless it's to be called multiple times).
Or use a syntax construct to execute the function created in place like this:
(draw = function(){
if(xPos < 300) {
c.fillRect(0, 0, xPos, 30);
xPos += 0.5;
setTimeout(draw,15); // use this to achieve animation effect
}
})();
var xPos = 0;
var canvas = document.getElementById("bar");
var c = canvas.getContext("2d");
c.fillStyle = "#FF0000";
var draw;
(draw = function(){
if(xPos < 300) {
c.fillRect(0, 0, xPos, 30);
xPos += 0.5;
setTimeout(draw,15);
}
})();
#bar {
width: 300px;
height: 50px;
}
<canvas id="bar"></canvas>
Edit: I've been thinking of what you might need, as it's not entirely abvious what you want. I have created this jsfiddle. Maybe it'll be of any help.
Hmmm...
You got some things mixed up. Try this:
<html>
<canvas id = "cvs1" width = "300" height = "30"></canvas>
</html>
And for the script:
var c = document.getElementById("cvs1").getContext("2d");
c.fillStyle = "#ff0000" //Set Fill Color(Set to red)
if(xPos < 300){
c.fillRect(xPos, 0, 30, 30);
xPos += 0.5;
}
If not:
What you did was use fill and rect seperately. You need to set the color, and then use the fillRect() function to draw the rectangle.
EDIT: You got the x,y,width,height as width,height,x,y. Fixed answer.
Good luck!
You need to call draw for every animation step. You could do this using setTimeout, setInterval or requestAnimationFrame :
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" type="text/css" href="test.css">
</head>
<body>
<canvas id="bar"></canvas>
<script>
var canvas = document.getElementById("bar");
var c = canvas.getContext("2d");
c.fillStyle = "#ff0000";
xPos=0;
draw = function(){
if(xPos < 300){
c.fillRect(0, 0, xPos, 30);
xPos += 0.5;
requestAnimationFrame(draw);
}
};
requestAnimationFrame(draw);
</script>
</body>
</html>

Ecg graph with html5 canvas

My need is to draw a ECG graph on canvas for socket data per every data iteration.
I tried to look into several graph plugins which use canvas to plot graphs, tried http://www.flotcharts.org/ but didn't succeed.
I Tried to plot graph using below basic html5 canvas drawline with sample data.
var fps = 60;
var n = 1;
drawWave();
function drawWave() {
setTimeout(function() {
requestAnimationFrame(drawWave2);
ctx.lineWidth = "2";
ctx.strokeStyle = 'green';
// Drawing code goes here
n += 1;
if (n >= data.length) {
n = 1;
}
ctx.beginPath();
ctx.moveTo(n - 1, data[n - 1] * 2);
ctx.lineTo(n, data[n] * 2);
ctx.stroke();
ctx.clearRect(n + 1, 0, 10, canvas.height);
}, 1000 / fps);
}
But it is not giving me the exact graph view as attached image. I'm not able to understand how to achieve graph like ecg graph. Please help me to get rid of this problem.
The characteristics with an ECG is that is plots the signal horizontally headed by a blank gap. When the end of the right side is reached is returns to left side and overdraw the existing graph.
DEMO
Setup
var ctx = demo.getContext('2d'),
w = demo.width,
h = demo.height,
/// plot x and y, old plot x and y, speed and scan bar width
speed = 3,
px = 0, opx = 0,
py = h * 0.8, opy = py,
scanBarWidth = 20;
ctx.strokeStyle = '#00bd00';
ctx.lineWidth = 3;
/// for demo we use mouse as signal input source
demo.onmousemove = function(e) {
var r = demo.getBoundingClientRect();
py = e.clientY - r.top;
}
loop();
The main loop:
The loop will plot whatever the signal amplitude is at any moment. You can inject a sinus or some other signal or read from an actual sensor over Web socket etc.
function loop() {
/// move forward at defined speed
px += speed;
/// clear ahead (scan bar)
ctx.clearRect(px,0, scanBarWidth, h);
/// draw line from old plot point to new
ctx.beginPath();
ctx.moveTo(opx, opy);
ctx.lineTo(px, py);
ctx.stroke();
/// update old plot point
opx = px;
opy = py;
/// check if edge is reached and reset position
if (opx > w) {
px = opx = -speed;
}
requestAnimationFrame(loop);
}
To inject a value simply update py (outside loop).
It would be far more helpful, if you included an image of what it does produce, rather than stating that it doesn't do that. Anyhow, it looks like you're only drawing a single line per frame. You need to run a loop with lineTo inside, iterating through all values of n.
Something along the lines of the below except from a sound-synthesizer. Just pay attention to the fact that there's a drawing-loop. In my case, there are often 40,000 or 50,000 samples that need to be drawn on a canvas of only a few hundred pixels wide. It seems like redundant drawing in my case, but doing th intuitive thing, of a single point per pixel results in an inaccurate image. The output of this looks something (88200 samples per 1024 pixels)
function drawFloatArray(samples, canvas)
{
var i, n = samples.length;
var dur = (n / 44100 * 1000)>>0;
canvas.title = 'Duration: ' + dur / 1000.0 + 's';
var width=canvas.width,height=canvas.height;
var ctx = canvas.getContext('2d');
ctx.strokeStyle = 'yellow';
ctx.fillStyle = '#303030';
ctx.fillRect(0,0,width,height);
ctx.moveTo(0,height/2);
ctx.beginPath();
for (i=0; i<n; i++)
{
x = (i*width) / n;
y = (samples[i]*height/2)+height/2;
ctx.lineTo(x, y);
}
ctx.stroke();
ctx.closePath();
}
Since your live data is streaming non-stop, you need a plan to deal with a graph that overflows the canvas.
Here's one solution that pans the canvas to always show only the most recent data.
Demo: http://jsfiddle.net/m1erickson/f5sT4/
Here's starting code illustrating this solution:
<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
<style>
body{ background-color: ivory; }
#canvas{border:1px solid red;}
</style>
<script>
$(function(){
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
// capture incoming socket data in an array
var data=[];
// TESTING: fill data with some test values
for(var i=0;i<5000;i++){
data.push(Math.sin(i/10)*70+100);
}
// x is your most recent data-point in data[]
var x=0;
// panAtX is how far the plot will go rightward on the canvas
// until the canvas is panned
var panAtX=250;
var continueAnimation=true;
animate();
function animate(){
if(x>data.length-1){return;}
if(continueAnimation){
requestAnimationFrame(animate);
}
if(x++<panAtX){
ctx.fillRect(x,data[x],1,1);
}else{
ctx.clearRect(0,0,canvas.width,canvas.height);
// plot data[] from x-PanAtX to x
for(var xx=0;xx<panAtX;xx++){
var y=data[x-panAtX+xx];
ctx.fillRect(xx,y,1,1)
}
}
}
$("#stop").click(function(){continueAnimation=false;});
}); // end $(function(){});
</script>
</head>
<body>
<button id="stop">Stop</button><br>
<canvas id="canvas" width=300 height=300></canvas>
</body>
</html>
I think this web component is both prettier and easier to use. It using canvas as a backend of draw. If you going use it all you need to do is call bang() on every appearing beat
document.body.innerHTML += '<ecg-line></ecg-line>';
ecgLine((bang) => setInterval(() => bang(), 1000));
Do you mean something like this?
var canvas = document.getElementById("dm_graphs");
var ctx = canvas.getContext("2d");
ls = 0;
d = 7; // sesitivity
function updateFancyGraphs() {
var gh = canvas.height;
var gh2 = gh / 2;
ctx.drawImage(canvas, -1, 0);
ctx.fillRect(graphX, 0, 1, canvas.height);
var size = Math.max(-gh, Math.min((3 * (Math.random() * 10 - 5)) * d, gh));
ctx.beginPath();
ctx.moveTo(graphX - 1, gh2 + ls / 2);
ctx.lineTo(graphX, gh2 + size / 2);
ctx.stroke();
ls = size;
}
function resizeCanvas() {
var w = window.innerWidth || document.body.offsetWidth;
canvas.width = w / 1.5;
canvas.height = window.innerHeight / 1.5;
graphX = canvas.width - 1;
ctx.lineWidth = 1; // 1.75 is nicer looking but loses a lot of information.
ctx.strokeStyle = "Lime";
ctx.fillStyle = "black";
}
window.addEventListener("resize", resizeCanvas);
resizeCanvas();
z = setInterval(() => updateFancyGraphs(), 20)
body {
min-height: 100ev;
}
body,
html,
canvas {
font: 15px sans-serif;
height: 100hv;
width: 100%;
margin: 0;
padding: 0;
background: #113355;
color: white;
overflow: hidden;
}
#dm_status,
footer {
text-align: center;
}
#dm_graphs {
image-rendering: optimizeSpeed;
background: rgba(0, 0, 0, 0.7);
}
<html>
<head>
<title>Zibri's Graph</title>
<meta name="viewport" content="width=device-width">
</head>
<body>
<canvas id="dm_graphs"></canvas>
</body>
</html>

Categories

Resources