In p5.js, I am trying to process each pixel of an image for a personal project so I thought I would start out slow and just try to make each pixel black. For some reason the screen is just staying white and I have no idea why the pixels aren't being updated. Here's the code:
var Canvas;
var srcImg;
var defaultImg = "http://i.imgur.com/ARg0OOy.jpg";
function preload() {
srcImg = loadImage(defaultImg);
}
function setup () {
createCanvas(srcImg.width,srcImg.height);
noLoop();
}
function draw() {
srcImg.loadPixels();
for (var x = 0; x < srcImg.width; x++) {
for (var y = 0; y < srcImg.height; y++) {
var loc = x + y*srcImg.width;
srcImg.pixels[loc] = color(224,29,29);
}
}
console.log(loc);
console.log(srcImg.width);
console.log(srcImg.height);
console.log(srcImg.width * srcImg.height);
srcImg.updatePixels();
//image(srcImg, 0,0,srcImg.width, srcImg.height);
}
Also, if I uncomment the last line, I see the original picture and it is cut off at the top (and it hasn't turned every pixel black). You can see for yourself here. Any thoughts on why this is happening?
Edit: I tried even doing one row of pixels to be a vibrant red color and the reason I'm getting a white screen is because no matter what color I set the pixels to, they become white... Also, when I tried making the whole row this red color, it stopped at about 1/4 the way through as shown here (and is still white). I don't know why this is happening.
You're making all of the pixels black, but then you're drawing srcImage on top of those black pixels. So all you see is srcImage.
Try commenting out the image(srcImg, 0,0,srcImg.width, srcImg.height); line to see the black pixels.
Related
I am trying to generate a square grid like pattern in p5js that covers as much of the browser window as possible.I am using p5js in instance mode as I using this with react and I am using chrome in Win10.
Here is my code:-
var size = 15;
var height = window.innerHeight;
var width = window.innerWidth;
Sketch = (p) => {
p.setup = () => {
p.createCanvas(width,height)
p.frameRate(60);
p.noLoop();
}
p.draw = () => {
p.background(250);
p.stroke(0);
p.noFill();
for(let i =0;i*size +size <width;i++) {
for(let j=0;j*size +size<height;j++) {
p.rect(i*size,j*size,size,size);
}
}
}
p.mouseDragged = (e) => {
p.stroke(0);
let x = Math.floor(e.clientY/size);
let y = Math.floor(e.clientX/size);
p.fill(220);
p.rect(y*size,x*size,size,size);
}
}
I call p.noLoop() so it doesnt refreshes everytime and I also have a button that calls p.redraw() to change everything to default. Here is the grid and behaviour I get:
The borders of grids are of varying sizes, first they decrease then increase then decrease and so on. Also, the area around which I drag my mouse has even more weird borders(This gets resolved when I click somewhere else so is this a GPU Aliasing rendering issue?). How do I create grid with same borders throughout my screen?
Edit: When I render even a single box, it has issues. The left and upper border are fine. However the right and down borders have an extra pixel of grayish borders which seems to be the problem. How do I fix this?
Also, How does strokeWeight and rect work in p5js? If I do strokeWeight(10) and rect(3,2,50,50), does that create a 50 by 50 rectangle with 10 pixels borders all around or the borders are included in the rectangle size?
I am attempting to do some simple motion with threejs. My goal is to have a box you can move around that cannot walk through walls. As of right now my motion code looks like this
function handleMotion(mesh) {
var xClone = mesh.clone();
xClone.position.x += mesh.velocity.x;
if (checkColl(xClone, collisionMeshList, [mesh.uuid])) {
mesh.velocity.x = 0;
}
var yClone = mesh.clone();
yClone.position.y += mesh.velocity.y;
if (checkColl(yClone, collisionMeshList, [mesh.uuid])) {
mesh.velocity.y = 0;
}
var zClone = mesh.clone();
zClone.position.z += mesh.velocity.z;
if (checkColl(zClone, collisionMeshList, [mesh.uuid])) {
mesh.velocity.z = 0;
}
mesh.position.add(mesh.velocity);
}
It checks each axis of motion to ensure that it is not going to move into a wall on the next frame. The problem is it does not prevent the cube from moving into the wall, however, once it moves into the wall it prevents the cube move moving out.
I also tried adding a "sim step" just to check if it could be increased by increasing the number of calculations it did however led to no change. The code is basically acting like the clones are at the same position as the mesh however I have confirmed they are indeed offset.
EDITED, see end of question.
In my application I have two canvas elements. One shows layered, transparent pngs, the other one gets an image from a file input and masks it. The chosen image is transparent where it is not masked. This image is then converted to a dataUrl, transformed to fit into the first canvas and added as the top layer of the first canvas.
Everything works as expected on desktop browsers: Chrome OSX, Safari OSX. I only add it in on load, so I made sure no race conditions can occur.
On Android Chrome and Safari iOS the canvas converted todataURL is rendered transparent. If I add a non-transparent image to the second canvas, the rendered image will show even on mobile devices.
To check I added the supposedly transparent canvas to the body. It shows correctly on desktop, but is transparent on mobile Browsers. Here the simplified JS. I am using fabric.js for convenience, but the problem is the same without the lib. I even once added a background color. Then only the color will show. Any ideas why todataurl on mobile browsers renders only transparent pixels?
<body>
<canvas id="canv"></canvas>
<script src="fabric.js"></script>
<script>
// main canvas
var c = new fabric.Canvas('canv');
c.setWidth(200);
c.setHeight(200);
var i = document.createElement('img');
i.src = 'dummy.jpg';
// i.src = 'dummy1.png';
i.onload = function(e) {
//document.body.appendChild(i);
scale = 1; // resizes the image
var ci = new fabric.Image(i);
ci.set({
left: 0,
top: 0,
scaleX: scale,
scaleY: scale,
originX: 'left',
originY: 'top'
}).setCoords();
// temporary canvas, will be converted to dataurl, contains transformed image
var tmpCanvas = new fabric.Canvas();
tmpCanvas.setWidth(100);
tmpCanvas.setHeight(100);
ci.scaleToWidth(100);
tmpCanvas.add(ci);
tmpCanvas.renderAll();
// create image from temporary canvas
var customImage = new fabric.Image.fromURL(tmpCanvas.toDataURL({ format: 'png' }), function (cImg) {
// add it to original canvas
c.clear();
c.add(cImg);
c.renderAll();
data = c.toDataURL({ format: 'png' });
// resized image
var newc = new fabric.StaticCanvas().setWidth(300).setHeight(300);
var newImg = new fabric.Image.fromURL(data, function (c1Img) {
newc.add(c1Img);
newc.renderAll();
// append to body to check if canvas is rendered correctly
document.body.appendChild(newc.lowerCanvasEl);
});
});
}
</script>
EDIT: I solved the problem, but could not find the problem on the Javascript side.
The problem was that I copied a temporary canvas onto another canvas. The scale and position of the added canvas was computed by finding the bounding box of non transparent pixels in a png, which was generated exactly for this purpose. A mask in short.
The bounding box was calculated in another temporary canvas at the start of the app (based on this answer). Although all sizes of the mask and its canvas were set correctly and the canvas was never added to the DOM, when loaded on a small screen the results of the bounding box differed from from the full screen results. After much testing i found this was true on Desktop too.
Because I already spent so much time on the problem, I decided to try to calculate the bounds in PHP and put it into a data attribute. Which worked great!
For those interested in the PHP solution:
function get_bounding_box($imgPath) {
$img = imagecreatefrompng($imgPath);
$w = imagesx($img);
$h = imagesy($img);
$bounds = [
'left' => $w,
'right' => 0,
'top' => $h,
'bottom' => 0
];
// get alpha of every pixel, if it is not fully transparent, write it to bounds
for ($yPos = 0; $yPos < $h; $yPos++) {
for ($xPos = 0; $xPos < $w; $xPos++) {
// Check, ob Pixel nicht vollständig transparent ist
$rgb = imagecolorat($img, $xPos, $yPos);
if (imagecolorsforindex($img, $rgb)['alpha'] < 127) {
if ($xPos < $bounds['left']) {
$bounds['left'] = $xPos;
}
if ($xPos > $bounds['right']) {
$bounds['right'] = $xPos;
}
if ($yPos < $bounds['top']) {
$bounds['top'] = $yPos;
}
if ($yPos > $bounds['bottom']) {
$bounds['bottom'] = $yPos;
}
}
}
}
return $bounds;
}
The problem was that I copied a temporary canvas onto another canvas. The scale and position of the added canvas was computed by finding the bounding box of non transparent pixels in a png, which was generated exactly for this purpose. A mask in short.
The bounding box was calculated in another temporary canvas at the start of the app (based on this answer). Although all sizes of the mask and its canvas were set correctly and the canvas was never added to the DOM, when loaded on a small screen the results of the bounding box differed from from the full screen results. After much testing i found this was true on Desktop too.
Because I already spent so much time on the problem, I decided to try to calculate the bounds in PHP and put it into a data attribute. Which worked great!
For those interested in the PHP solution:
function get_bounding_box($imgPath) {
$img = imagecreatefrompng($imgPath);
$w = imagesx($img);
$h = imagesy($img);
$bounds = [
'left' => $w,
'right' => 0,
'top' => $h,
'bottom' => 0
];
// get alpha of every pixel, if it is not fully transparent, write it to bounds
for ($yPos = 0; $yPos < $h; $yPos++) {
for ($xPos = 0; $xPos < $w; $xPos++) {
// Check, ob Pixel nicht vollständig transparent ist
$rgb = imagecolorat($img, $xPos, $yPos);
if (imagecolorsforindex($img, $rgb)['alpha'] < 127) {
if ($xPos < $bounds['left']) {
$bounds['left'] = $xPos;
}
if ($xPos > $bounds['right']) {
$bounds['right'] = $xPos;
}
if ($yPos < $bounds['top']) {
$bounds['top'] = $yPos;
}
if ($yPos > $bounds['bottom']) {
$bounds['bottom'] = $yPos;
}
}
}
}
return $bounds;
}
I can't figure out why this doesn't work. What I have here is my own black canvas made from a DIV. In that canvas, I want the user to define the first point which I'm successful at, but after clicking the first point, when the mouse moves, the box must size properly and follow the mouse, much like drawing a rectangular box in a paint program. This is where I have difficulty.
Is there a way I can solve this such that it works at minimum and without using Jquery? all the better if I can get a solution for Internet Explorer 7 (or 8 at least).
<div ID="CANVAS" style="background:#000;width:600px;height:400px"></div>
<script>
var startx=-1,starty=-1,points=0,box;
var canvas=document.getElementById('CANVAS');
canvas.onclick=dopoint;
canvas.onmousemove=sizebox;
function dopoint(e){
if (points==0){
var area=canvas.getBoundingClientRect();
box=document.createElement('DIV');
box.style.position='relative';
box.style.border='2px solid yellow';
canvas.appendChild(box);
startx=e.clientX-area.left;
starty=e.clientY-area.top;
box.style.left=startx+'px';
box.style.top=starty+'px';
box.style.width='10px';
box.style.height='10px';
}
points=1-points;
}
function sizebox(e){
if (points==1){
var x=e.clientY,y=e.clientY; //here I'm thinking subtract old point from new point to get distance (for width and height)
if (x>startx){
box.style.left=startx+'px';
box.style.width=(x-startx)+'px';
}else{
box.style.left=x+'px';
box.style.width=(startx-x)+'px';
}
if (y>starty){
box.style.top=starty+'px';
box.style.height=(y-starty)+'px';
}else{
box.style.top=y+'px';
box.style.height=(starty-y)+'px';
}
}
}
</script>
Your code was almost good, except few small things. I have corrected it and wrote some comments on the lines that I've changed.
https://jsfiddle.net/1brz1gpL/3/
var startx=-1,starty=-1,points=0,box;
var canvas=document.getElementById('CANVAS');
canvas.onclick=dopoint;
canvas.onmousemove=sizebox;
function dopoint(e){
if (points==0){
var area=canvas.getBoundingClientRect();
box=document.createElement('DIV');
box.style.position='absolute'; // here was relative and changed to absolute
box.style.border='2px solid yellow';
canvas.appendChild(box);
startx=e.clientX; // removed -area.left
starty=e.clientY; // removed -area.right
box.style.left=startx+'px';
box.style.top=starty+'px';
box.style.width='0px'; // updated to 0px instead of 10 so it won't "jump" after you move the mouse with less then 10px
box.style.height='0px'; // same
}
points=1-points;
}
function sizebox(e){
if (points==1){
var x=e.clientX,y=e.clientY; // here was x = e.clientY and changed to x = clientX
if (x>=startx){
box.style.left=startx+'px';
box.style.width=(x-startx)+'px'; // here it was x+startx and changed to x-startx
}else{
box.style.left=x+'px';
box.style.width=(startx-x)+'px';
}
if (y>starty){
box.style.top=starty+'px';
box.style.height=(y-starty)+'px';
}else{
box.style.top=y+'px';
box.style.height=(starty-y)+'px';
}
}
}
I decided to day to embark on element and I can say so far it have been nightmare to get it work. All I want is to plot a sine graph. So after good reading I still cannot either get origins nor get it plot. Below is what I have tried (my first time ever with that tag so excuse my ignorance). What makes me wonder is the guy here have it but the codes are hard to understand for beginner like me.
HTML
<!DOCTYPE html>
<html>
<head>
<title>Graphing</title>
<link type="text/css" rel="Stylesheet" href="graph.css" />
<script type="text/JavaScript" src="graph.js" ></script>
</head>
<body>
<canvas id="surface">Canvas not Supported</canvas>
</body>
</html>
CSS
#surface
{
width:300;
height:225;
border: dotted #FF0000 1px;
}
JavScript
window.onload = function()
{
var canvas = document.getElementById("surface");
var context = canvas.getContext("2d");
arr = [0,15, 30,45,60, 90,105, 120, 135, 150, 165, 180 ];
var x=0;
var y = 0;
for(i=0; i<arr.length; i++)
{
angle = arr[i]*(Math.PI/180); //radians
sine = Math.sin(angle);
context.moveTo(x,y);
context.lineTo(angle,sine);
context.stroke();
//set current varibles for next move
x = angle;
y = sine;
}
}
Since the range of sin x is [-1,1], it will only return numbers between -1 and 1, and that means all you will be drawing is a dot on the screen.
Also I see that you have an array ranging from 0 to 180. I believe you are trying to draw the curve with x from 0 degree to 180 degree? You don't really need to do this (anyway 12 points are not enough to draw a smooth line). Just do it with a for loop, with lines being the number of fragments.
First we start off by moving the point to the left of the canvas:
context.moveTo(0, 100 /*somewhere in the middle*/); //initial point
In most cases the first point won't be in the middle. But for sine it is. (You might want to fix it later though.)
for (var i = 0; i < lines; i++) {
//draw line
}
That's the loop drawing the curve. But what should we put inside? Well you can just take the number returned by the sine function and scale it up, flip it upside down, and shift it down half the way. I do that because the coordinate system in JavaScript is 0,0 in the top left instead of in the bottom left.
var sine = Math.sin(i/scale*2)*scale;
context.lineTo(i*frag, -sine+scale);
//i * frag = the position of x scaled up
//-sine + scale = the position of y, flipped, scaled, shifted down
//i/scale*2 = random scale I put in... you might want to figure out the
// correct scale with some math
So that's it. Viola, you have successfully plotted a graph in JavaScript.
Oh yes, don't forget to actually tell it to draw it on the canvas after the for loop has done its job:
context.stroke();
The demo: http://jsfiddle.net/DerekL/hK5rC/
PS: I see that you are trying to resize the canvas using CSS. Trust me, it won't work. :) You will have to define the dimension in HTML.