p5.js this._renderer.image is not a function issue - processing

var adobe1bar1;
function preload() {
adobe1bar1 = loadImage("1-1.png");
}
function setup() {
createCanvas(500, 500, WEBGL);
center = createVector(width / 2, height / 2, height / 2);
cameraZ = -500;
}
function draw() {
background(40); // backgraound white
var cameraX = map(mouseX, 0, width, -500, 500); // map
var cameraY = map(mouseY, 0, height, -500, 500);
console.log(cameraY, cameraX);
if (cameraZ < -500) {
cameraZ = -500;
}
if (cameraZ > 0) {
cameraZ = 0;
}
camera(center.x + cameraX, center.y + cameraY, center.z + cameraZ, center.x, center.y, center.z, 0, 1, 0);
translate(center.x, center.y, center.z);
translate(0, 0, 0);
image(adobe1bar1, -250, -250, 500, 500);
}
Here is my p5.js code.
When I use the image() function
The following error message keeps appearing.
Uncaught TypeError: this._renderer.image is not a function
Is there a solution?
When not using 'WEBGL',
it uploads images without error,
but the camera() function does not work.

image is not a WEBGL function.
Try to apply an image texture to a plane instead.
texture(adobe1bar1);
translate(-250, -250, 0);
plane(500);
https://github.com/processing/p5.js/wiki/Getting-started-with-WebGL-in-p5
EDIT:
To use transparent texture you need to enable blend mode which can be done by using:
fill(0,0,0,0);
In setup

Related

P5.JS - Filling a shape with a preloaded image, using an object, its properties and its functions

I am trying to create an object and for it to have a shape (an ellipse), an image, and a function that somehow fills the shape with a pre-loaded image.
I already found this post, but I can't get it to work if I try to make it neat and fit it all into an object.
To be clear, this is what it would look like:
let image;
let hypotheticalObject
function preload()
{
image = loadImage('Assets/randomimage.jpeg');
}
function setup()
{
hypotheticalObject = {
objectImage: image,
graphicsBuffer: createGraphics(100, 100),
xPos: width / 2,
yPos: height / 2,
size: 50,
colour: color(255, 255, 255),
shape: function()
{
fill(this.colour),
ellipse(this.xPos, this.yPos, this.size);
},
mask: function()
{
this.graphicsBuffer.shape();
},
render: function()
{
something something
}
}
function draw()
{
hypotheticalObject.render();
}
That's kind of how far I can get, as I can't figure out how to proceed.
There are a couple of issues with your code, namely this:
shape: function() {
// ...
},
mask: function() {
this.graphicsBuffer.shape();
},
You declare shape() on hypotheticalObject but then you try to call it on graphicsBuffer? This isn't how javascript works. You could do something like this:
hypotheticalObject.graphicsBuffer.shape = function() {
// When graphicsBuffer.shape() is called, the this keyword will be bound to graphicsBuffer
// Unfortunately we need a to access members from hypotheticalObject as well, so that is inconvenient:
this.fill(hypotheticalObject.colour),
this.ellipse(hypotheticalObject.xPos, hypotheticalObject.yPos, hypotheticalObject.size);
}
However I don't think that is the most idiomatic approach either. Here's a working solution that tries to stay close to your code:
let img;
let hypotheticalObject;
function preload() {
img = loadImage('https://www.paulwheeler.us/files/windows-95-desktop-background.jpg');
}
function setup() {
createCanvas(windowWidth, windowHeight);
hypotheticalObject = {
objectImage: img,
// The graphics you're using for the mask need to be the size of the image
graphicsBuffer: createGraphics(img.width, img.height),
xPos: width / 2,
yPos: height / 2,
size: 50,
init: function() {
this.maskedImage = createImage(this.objectImage.width, this.objectImage.height);
},
updateShape: function() {
this.graphicsBuffer.clear();
// The fill color is irrelevant so long as it is opaque
this.graphicsBuffer.fill(0);
// These drawing instructions need to happen on the p5.Graphics object
this.graphicsBuffer.ellipse(this.xPos, this.yPos, this.size);
},
render: function() {
this.updateShape();
// When you call mask on an image it changes the image so we need to make a copy
this.maskedImage.copy(
this.objectImage,
0, 0, this.objectImage.width, this.objectImage.height,
0, 0, this.objectImage.width, this.objectImage.height
);
this.maskedImage.mask(this.graphicsBuffer);
// Always draw the image at 0, 0 since the masked portion is already offset
image(this.maskedImage, 0, 0);
}
}
hypotheticalObject.init();
}
let xv = 2;
let yv = 2;
function draw() {
background(255);
hypotheticalObject.xPos += xv;
hypotheticalObject.yPos += yv;
if (hypotheticalObject.xPos > width) {
hypotheticalObject.xPos = width;
xv *= -1;
} else if (hypotheticalObject.xPos < 0) {
hypotheticalObject.xPos = 0;
xv *= -1;
}
if (hypotheticalObject.yPos > height) {
hypotheticalObject.yPos = height;
yv *= -1;
} else if (hypotheticalObject.yPos < 0) {
hypotheticalObject.yPos = 0;
yv *= -1;
}
hypotheticalObject.render();
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.js"></script>
Update: Animated Gifs
If you want this to work with animated gifs, you have to deal with an idiosyncrasy of p5.js: the fact that it updates gif images only when they are rendered with either the image() function or the texture() function. And that rendering code depends on the drawing context having timing information, and therefor does not work with off screen p5.Graphics buffers. Here's an example:
let img;
let hypotheticalObject;
function preload() {
img = loadImage('https://www.paulwheeler.us/files/Clouds.gif');
}
function setup() {
createCanvas(img.width, img.height);
hypotheticalObject = {
objectImage: img,
// The graphics you're using for the mask need to be the size of the image
graphicsBuffer: createGraphics(img.width, img.height),
xPos: width / 2,
yPos: height / 2,
size: 100,
init: function() {
this.maskedImage = createImage(this.objectImage.width, this.objectImage.height);
},
updateShape: function() {
this.graphicsBuffer.clear();
// The fill color is irrelevant so long as it is opaque
this.graphicsBuffer.fill(0);
// These drawing instructions need to happen on the p5.Graphics object
this.graphicsBuffer.ellipse(this.xPos, this.yPos, this.size);
},
render: function() {
this.updateShape();
// When you call mask on an image it changes the image so we need to make a copy
this.maskedImage.copy(
this.objectImage,
0, 0, this.objectImage.width, this.objectImage.height,
0, 0, this.objectImage.width, this.objectImage.height
);
this.maskedImage.mask(this.graphicsBuffer);
// Always draw the image at 0, 0 since the masked portion is already offset
image(this.maskedImage, 0, 0);
}
}
hypotheticalObject.init();
}
let xv = 2;
let yv = 2;
function draw() {
// In order to get a gif to animate it needs to be passed to either the image() function or the texture() function. And unfortunately it must be the global one, not the version on an off-screen p5.Graphics
// However we don't actually want to display this
image(img, width, height);
background(255);
hypotheticalObject.xPos += xv;
hypotheticalObject.yPos += yv;
if (hypotheticalObject.xPos > width) {
hypotheticalObject.xPos = width;
xv *= -1;
} else if (hypotheticalObject.xPos < 0) {
hypotheticalObject.xPos = 0;
xv *= -1;
}
if (hypotheticalObject.yPos > height) {
hypotheticalObject.yPos = height;
yv *= -1;
} else if (hypotheticalObject.yPos < 0) {
hypotheticalObject.yPos = 0;
yv *= -1;
}
hypotheticalObject.render();
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.js"></script>

Texture flickering in p5

I have a scene with a cube and a custom obj. I am attempting to apply a gradient as a texture to the cube via createGraphics, but every time I use createGraphics as a texture on the cube it flickers
The custom model does not have the same issue when using the createGraphics texture for it, it is exclusively a problem with the cube.
Here is my code:
var camy;
var camz;
var camoffset;
var horiZon;
var c1;
var c2;
function preload() {
fria = loadImage('nocity.png');
island = loadModel('untitled.obj');
}
function setup() {
canvas = createCanvas(windowWidth, windowHeight, WEBGL);
pixelDensity=1;
c1 = color(255, 255, 255);
c2 = color(0, 0, 0);
sunset = createGraphics(200, 200);
}
function windowResized() {
resizeCanvas(windowWidth,windowHeight);
}
function draw() {
background(0, 5, 100);
angleMode(DEGREES);
camoffset = 2500 - windowWidth;
horiZon = map(mouseY, 0, height, -35, -65);
camx = map(mouseX, 0, width, -500, 500);
camz = map(mouseY, height, 0, -1400 - camoffset, -2100 - camoffset);
camy = map(mouseY, height, 0, -1000 - camoffset, -400);
setGradient(0, 0, 200, 200, c1, c2);
perspective(PI / 3.0, width / height, 0.1, 10000);
camera(camx, camy, camz, 0, 0, 0, 0, 1, -0.25);
translate(-2.5, 6, 0);
rotateZ(180);
rotateY(180);
noStroke();
texture(fria);
model(island);
texture(sunset);
translate(0, 100, horiZon);
box(200, 200, 1);
}
function setGradient(x, y, w, h, c1, c2) {
noFill();
for (var i = y; i <= y + h; i++) {
var inter = map(i, y, y + h, 0, 1);
var c = lerpColor(c1, c2, inter);
sunset.stroke(c);
sunset.line(x, i, x + w, i);
}
}
I found a solution to this problem. When I removed the camera code, the flickering disappeared. So the solution I made was I instead used translation and rotation to emulate the camera motions that I wanted. It is quite hard to think of it this way, but you can figure out what translation you need to do with simple trig.

p5.js not drawing a 3d box

I am creating a simulator using P5.js. Within the simulator, I need a green box, however it does not seem to be appearing. The code is below:
var outputs = [];
function setup() {
createCanvas(600, 400, WEBGL);
background(200);
for (var i = 0; i < 1; i++) {
drop = new Water(width / 2, height / 2, 0, 1);
outputs[i] = drop;
}
}
function draw() {
push();
translate(200, 150, 0);
stroke(0, 100, 0);
fill(0, 255, 0);
box(150, 150, 150);
pop();
for (var i = 0; i < outputs.length; i++) {
outputs[i].update();
}
background(200);
}
Here is the water class:
function Water(x_, y_, z_, yVel_) {
this.r = random(0.25, 1);
this.xOff = random(-(this.r / 10), (this.r / 10));
this.zOff = random(-(this.r / 10), (this.r / 10));
this.x = x_ + this.xOff;
this.y = y_;
this.z = z_ + this.zOff;
this.yVel = yVel_;
this.pos = createVector(this.x, this.y, this.z);
this.show = function() {
push();
translate(this.pos.x, this.pos.y, this.pos.z);
noStroke();
fill(0, 0, 255);
sphere(this.r * 2);
pop();
}
this.update = function() {
this.vel = createVector(random(-(this.r / 10), (this.r / 10)),
this.yVel, random(-(this.r / 10),
(this.r / 10)));
this.pos.add(this.vel);
this.show();
}
}
This is a web based simulation, with another module which appears to be working fine.
Any help would be greatly appreciated. Thanks in advance!
Removing the parts that require the Water class, and moving the background function call to the top of draw, it seems to work just fine.
So, the problem is
Either that you forgot to put background on top
Or something's wrong with the Water class.
Here's your code with the mentioned problems fixed.
var outputs = [];
function setup() {
createCanvas(600, 400, WEBGL);
}
function draw() {
background(200);
push();
translate(200, 150, 0);
stroke(0, 100, 0);
fill(0, 255, 0);
box(150, 150, 150);
pop();
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.7.1/p5.min.js"></script>
Its not rendering because you're background is over your scene
function draw() {
background(200);
push();
translate(200, 150, 0);
stroke(0, 100, 0);
fill(0, 255, 0);
box(150, 150, 150);
pop();
for (var i = 0; i < outputs.length; i++) {
outputs[i].update();
}
}
What your doing is drawing the box and the drops and you cover it all up with your background
if you don't have a background you will see how p5.js renders animation
p5.js not moving it, its just looping through draw every frame and the background covers up the previous frame

p5 resize window without clearing the background

The following code makes use of the p5dom add-on to position the canvas in the centre of the window. To dynamically resize the canvas I'm using the windowResized() function. I want to keep the background function in setup. How do I prevent the background colour from clearing when I resize the window? Many thanks.
var cnv;
function centerCanvas() {
var x = (windowWidth - width) / 2;
var y = (windowHeight - height) / 2;
cnv.position(x, y);
}
function setup() {
cnv = createCanvas(windowWidth,windowHeight);
centerCanvas();
background(255, 0, 200);
}
function draw(){
}
function windowResized() {
centerCanvas();
resizeCanvas(windowWidth,windowHeight)
}
One thing you might do is draw everything to a buffer instead of directly to the screen. The createGraphics() function is your friend here. From the P5.js reference:
var pg;
function setup() {
createCanvas(100, 100);
pg = createGraphics(100, 100);
}
function draw() {
background(200);
pg.background(100);
pg.noStroke();
pg.ellipse(pg.width/2, pg.height/2, 50, 50);
image(pg, 50, 50);
image(pg, 0, 0, 50, 50);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.5.16/p5.js"></script>
You would want to draw everything to a buffer, and then when the screen is resized, redraw that buffer.
Simply by adding the background function to the draw one too.
Try this:
var cnv;
function centerCanvas() {
var x = (windowWidth - width) / 2;
var y = (windowHeight - height) / 2;
cnv.position(x, y);
}
function setup() {
cnv = createCanvas(windowWidth,windowHeight);
centerCanvas();
background(255, 0, 200);
}
function draw(){
//HERE:
background(255, 0, 200);
}
function windowResized() {
//(you can add it here too...)
centerCanvas();
resizeCanvas(windowWidth,windowHeight);
}
I would simply set the background in the windowResized function.
function windowResized() {
centerCanvas();
resizeCanvas(windowWidth,windowHeight)
background(255, 0, 200);
}
For an alternative method, you could use CSS to edit the dimensions using something similar to the following:
<style>
canvas {
width: 100%;
height: 100%;
}
</style>

Map translated coordinates back to original

Once a transformation of coordinates has taken place during a the rendering of a computer graphics scene, how do you map inputs on the rendered scene back to the original actor(s) coordinate systems?
Using this JSFiddle https://jsfiddle.net/bbz5s183/3/ as a starting point, implement the canvas click event handler so that.
It can identify if a star was clicked.
It will work consistently no matter how the canvas is resized.
JSFIDDLE SCRIPT CONTENT BELOW
var draggable = document.getElementById('draggable')
var canvas = document.getElementById('canvas');
var context = canvas.getContext('2d');
// Draw a star in a 1 x 1 coordinate plane.
function star(color) {
context.beginPath();
context.moveTo(0.5, 0);
context.lineTo(0.15, 1.0);
context.lineTo(1.0, 0.4);
context.lineTo(0, 0.4);
context.lineTo(0.85, 1.0);
context.closePath();
context.fillStyle = color;
context.fill();
}
// Draw a scene of stars in a coordinate plane defined by the canvas.
// This is initially 300 x 300, but can be resized to anything by dragging the gray border.
function render() {
context.setTransform(1, 0, 0, 1, 0, 0);
context.translate(canvas.width / 2, canvas.height / 2);
context.scale(canvas.width / 5, canvas.height / 5);
star('red');
context.setTransform(1, 0, 0, 1, 0, 0);
context.translate(canvas.width / 4, canvas.height / 4);
context.scale(canvas.width / 5, canvas.height / 5);
star('yellow');
}
// Pop an alert indicating which star (if any) was clicked on.
// NOTE: The logic MUST work consistently no matter how the canvas is resized.
canvas.addEventListener('click', function (event) {
event.stopImmediatePropagation();
// HELP ME !!!
// HELP ME !!!
// HELP ME !!!
// HELP ME !!!
});
// IGNORE: It allows the canvas to resized by dragging on it.
draggable.addEventListener('mousedown', function handleMouseDown(mousedown) {
document.addEventListener('mouseup', function handleMouseUp(mouseup) {
var currWidth = Number(canvas.getAttribute('width'));
var deltaWidth = mouseup.clientX - mousedown.clientX;
var currHeight = Number(canvas.getAttribute('height'));
var deltaHeight = mouseup.clientY - mousedown.clientY;
canvas.setAttribute('width', currWidth + deltaWidth);
canvas.setAttribute('height', currHeight + deltaHeight);
document.removeEventListener('mouseup', handleMouseUp);
render();
});
});
render();
Answered my own question: https://jsfiddle.net/bbz5s183/4/
JAVASCRIPT FOLLOWS
// Draw a scene of stars in a coordinate plane defined by the canvas.
// This is initially 300 x 300, but can be resized to anything by dragging the gray border.
function render() {
bounds = [];
/* RENDER RED ACTOR - BOUNDING BOX */
var red = {
name: 'red',
// Translate to 25% right, 25% down on canvas.
x: 0.25 * canvas.width,
y: 0.25 * canvas.height,
// Scale to fill 20% of canvas.
width: 0.2 * canvas.width,
height: 0.2 * canvas.height
};
context.setTransform(1, 0, 0, 1, 0, 0);
box('red', red);
bounds.push(red);
/* RENDER RED ACTOR - MODEL */
context.setTransform(1, 0, 0, 1, 0, 0);
context.translate(red.x, red.y);
context.scale(red.width, red.height);
star('red');
/* RENDER YELLOW ACTOR - BOUNDING BOX */
var yellow = {
name: 'yellow',
// Translate to 50% right, 50% down on canvas.
x: 0.50 * canvas.width,
y: 0.50 * canvas.height,
// Scale to fill 20% of canvas.
width: 0.2 * canvas.width,
height: 0.2 * canvas.height
};
context.setTransform(1, 0, 0, 1, 0, 0);
box('yellow', yellow);
bounds.push(yellow);
/* RENDER YELLOW ACTOR - MODEL */
context.setTransform(1, 0, 0, 1, 0, 0);
context.translate(yellow.x, yellow.y);
context.scale(yellow.width, yellow.height);
star('yellow');
}
// Pop an alert indicating which star (if any) was clicked on.
// NOTE: The logic MUST work consistently no matter how the canvas is resized.
canvas.addEventListener('click', function (event) {
var x = event.pageX - event.target.offsetLeft;
var y = event.pageY - event.target.offsetTop;
for (var i = 0; i < bounds.length; i++) {
if (boxIntersection(bounds[i], x, y)) {
alert(bounds[i].name);
};
}
event.stopImmediatePropagation();
return false;
});

Resources