Not able to copy paste a section of image in P5.js - p5.js

I have been working on a project in which I have to select an area of image and then use CTRL+C and CTRL+V to copy and paste the selected area. I have tried creating a buffer using createGraphics() but did not work. I have also tried getting pixels one by one and creating image but that is not working either. Can somebody help? Below is my code.
if (this.isCtrlPressed == true && p5.key == 'v') {
let img = p5.createImage(this.dw, this.dh);
p5.draw = () => {
img.loadPixels();
for (let i = 0; i < parseInt(this.sw); i++) {
for (let j = 0; j < parseInt(this.sh); j++) {
img.set(i, j, p5.get(i, j));
}
}
img.updatePixels();
p5.stroke(255);
p5.image(img, 10, 10);
p5.noFill();
p5.rect(p5.mouseX, p5.mouseY, 10, 10);
return;
}

You're not too far off.
Bare in mind that you can also call get() with width, height arguments, not just the first two (x, y): this will make it easy to copy a section of the image.
The p5.Graphics you get using createGraphics() is handy indeed.
Remember to store the selection rectangle properties (x, y, width, height) to copy the image in the first place.
Here's a very rough sketch based on the CreateImage p5.js example:
/*
* #name Create Image
* #description The createImage() function provides a fresh buffer of pixels to
* play with. This example creates an image gradient.
*/
let img; // Declare variable 'img'.
// copy/paste selection rectangle
let selection = {x:0, y:0, w:0, h:0};
// p5.Image from main image using selection
let clipboard;
// where to paste the clipboard p5.Image
let pasteBuffer;
function setup() {
createCanvas(720, 400);
img = createImage(230, 230);
img.loadPixels();
for (let x = 0; x < img.width; x++) {
for (let y = 0; y < img.height; y++) {
let a = map(y, 0, img.height, 255, 0);
img.set(x, y, [0, 153, 204, a]);
}
}
img.updatePixels();
// selection drawing style
noFill();
stroke(255);
// setup buffer to paste into
pasteBuffer = createGraphics(width, height);
}
function draw() {
background(0);
// render original image
image(img, 0, 0);
// render pasted graphics
image(pasteBuffer, 0, 0);
// render selection rectangle
rect(selection.x, selection.y, selection.w, selection.h);
}
function mousePressed(){
// store selection start
selection.x = mouseX;
selection.y = mouseY;
}
function mouseDragged(){
// update selection dimension as the difference between the current mouse coordinates and the previous ones (selection x, y)
selection.w = mouseX - selection.x;
selection.h = mouseY - selection.y;
}
function keyPressed(keyEvent){
if (key == 'c') {
console.log("copy image", selection);
// use get() to "clone" a subsection of the main image
clipboard = img.get(selection.x, selection.y, selection.w, selection.h);
}
if (key == 'v') {
console.log("paste image");
if(clipboard){
// simply render the clipboard image
pasteBuffer.image(clipboard, mouseX, mouseY);
}
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.2/p5.min.js"></script>
Note the above isn't perfect (e.g. you need to use abs() to handle making selections from right to left, not just left to right), but hopefully it illustrates the usage of a selection rectangle, copying pixels and "pasting"/rendering them into a buffer.
Speaking of copying pixels, an alternative option could be using a p5.Image (using createImage() instead of createGraphics()) and copy() (instead of buffer.image()) (and you'd use the selection rectangle properties to know from where to where to paste pixels).

Related

How to have scalable image with viewport in p5js?

I'm making a website for my design studio and I would like to set image sizes with the p5js language.
Here, I made a function that allows each mouse click to display an image. The problem is that once the image is displayed, it appears at the indicated dimensions, adapt to the screen size but the ratio is not good. The image is either too wide or too extended.
so what do you need to do to display an image with the right original width/height ratio
All your feedback is appreciated.
Thanks in advance !
let works = []
function preload() {
for (let i = 0; i < 6; i++) {
works[i] = loadImage("img/work" + i + ".png")
}
}
function setup() {
canvas = createCanvas(windowWidth, windowHeight);
canvas.position(0, 0);
canvas.style('z-index', '1');
}
function windowResized() {
resizeCanvas(windowWidth, windowHeight);
canvas.position(0, 0);
canvas.style('z-index', '1');
}
function draw() {
cursor(CROSS);
}
var counter = 0
function mouseClicked() {
imageMode(CENTER);
counter++
image(works[counter%6], mouseX, mouseY, windowWidth/2, windowHeight/2);
}
The image function can take 3, 5, 7, or 9 parameters, and in each case it has different behavior:
image(img, x, y) - Draw the entire image with it's original size at the specified location
image(img, x, y, width, height) - Draw the entire image, scaled to the specified width and height without preserving aspect ratio, at the specified position.
image(img, dx, dy, dWidth, dHeight, sx, sy, sWidth, sHeight) - Draw a specific part of the image, scaled to the specified width and height without preserving aspect ratio, at the specified position.
The function of the dx, dy, dWidth, dHeight, sx, sy, sWidth, sHeight parameters is explained by this diagram:
In order to draw an image scaled relative to the window size constrained to the aspect ratio of the window, you will need to determine how best to crop your images.
let works = []
function preload() {
works[0] = loadImage("https://www.paulwheeler.us/files/no_idea_why.jpg");
}
function setup() {
canvas = createCanvas(windowWidth, windowHeight);
canvas.position(0, 0);
canvas.style('z-index', '1');
}
function windowResized() {
resizeCanvas(windowWidth, windowHeight);
canvas.position(0, 0);
canvas.style('z-index', '1');
}
function draw() {
cursor(CROSS);
}
var counter = 0
function mouseClicked() {
imageMode(CENTER);
counter++
let img = works[counter % works.length];
let windowAspect = width / height;
let imageAspect = img.width / img.height;
let w, h;
// This code naively crops the bottom or right edge of the image as necessary. Obviously there are other ways to limit the image size.
if (windowAspect >= imageAspect) {
// Our window is wider than our image, we need to constrain the height of the image
w = img.width;
h = w / windowAspect;
} else {
// Our window is narrower than or image, we need to constrain the width of the image
h = img.height;
w = h * windowAspect;
}
image(img, mouseX, mouseY, windowWidth / 2, windowHeight / 2, 0, 0, w, h);
}
<!DOCTYPE html>
<html lang="en">
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.3.1/p5.min.js"></script>
</head>
<body>
</body>
</html>

In p5.js, is it possible to load a gif in the setup or preload function, and then use that gif multiple times elsewhere?

I have a class (lets call it "Enemies"), I want them to attack me when close enough (It will display an animated gif, that looks like a bite).
I've gotten all of this to work, except the only way I could figure it out, was by putting loadImage("attack.gif"), in the class. That got laggy really quick, since every time an enemy would spawn, it would have to reload that gif.
I've tried to use a loaded gif from the setup(), in my class, but all of their attacks were in sync.
Is there another way to do this?
You can preload the gif (or gifs) and store them in a array which you can reuse later to draw in multiple places (e.g. multiple spawned enemies in your case).
Here's a basic example that demonstrates:
loading images into an array
spawning objects on screen (re)using the loaded images
let images;
let enemies = [];
function preload(){
// load images and store them in an array
images = [
loadImage("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAMAAAD04JH5AAAAPFBMVEX///8AAADf399fX19/f38/Pz+/v78fHx+fn58XFxeHh4cPDw+np6fX19fHx8evr6+3t7dvb2/39/dPT08jn1NmAAAA+UlEQVR4nO3ZSRKCMBBAUQnzPHj/u7qwOwtS0QhBwfpviaH9bDTG2w0AAAAAgNNLE2HC7zF6T0oAAQT8Q0CTC1M8Df61gywxek8TIcAq5anKXUsIIICACwaMmWhlept5OUvGCAH2o3iLqN8FBBBAwJcDJt3U1Dqsk+1O538/Z0mtU6aPAypnuu4JjfNKwJKKAAIIuEzAi+lxvPuFSQABBJwnoHY2JFu4U4ID7GYqzpYs+KiLAAIIOHPA7D+aWZsPCdh1Wk4AAQRcJ2CRQ5ai1yu9XrnnK/YfqWb9SuqfsoQ/xpoehSf+x1PHHtcTQAABPwsAAAAAAOAYD7+UFrmL/czUAAAAAElFTkSuQmCC"), loadImage("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAMAAAD04JH5AAAAclBMVEX///8AAAAMDAwUFBRtbW20tLTKysrr6+s0NDSMjIzo6OgrKytzc3Py8vJKSkolJSWSkpIODg5jY2OlpaW7u7ubm5urq6tEREQaGhr29vZRUVHg4ODDw8Pc3Nw/Pz9VVVWAgIDU1NQ3NzdcXFx6enovLy+wzkUiAAACjElEQVR4nO2a63qCMAxAlU5QEEEUuSMivP8rjptS2iid0qmfOT/XfM0BHGlLZjMEQRAE+ShMXQBTXn79aAgQ6tIE4u1cgG0sTWAvJrBHARRAART4bIFEvUm8Fsk/nxe3p1BH60RskJsIXX+FcnsKkp5GBAQv8nGyEQFHtoCDAiiAAihwP38gXyAAE592LbYhW6B0u1QJnX9nKS2y09d0mTIqv5n+R2KGH0pAW6IACqAACqDAewtYGwaPQGHEY+OsaQRK1WTJoLiMC1PLSQToyI4FULeVBR/3M4nA+nGBeztNFEABFEABcQFgM7WDBHZc2N2NnrgAcVYMWQ7F5Rkb54BF688CkkABFEABFEABWsDcvEBgUGRjm8YdAthtHTbItR2gQp7Z8kQh3mWw4if2ND5M83jPaVoZAIEN0CcCPEciTSCFBPg7IFEAeATAcas8gRz6DfDLJKLKEli+WqAEPgEDbzN5AgYgoPOnEfIELEiA/9YhT8CHBPgTIYkC0TsK+FzYdh8EgWma2lMNdsH5OqHihS1r6E1YhOGxxuurUn4+hhsvTcOxtgUItVg3OH3jRB4FLZrbDfbYwawb1IHy6T4gYPPTeJcL3/Obvv4fg7plV+yJBYDt+TsJHL9eIESBFwtAi/wvE+BXp1MJXFfjQF8fJXD2LYYSOEMcoB449JXCYl3f6JpD2EFqoaxHbLu51hRFU6tp/xIldEGtnho7HzmcFixU00WgsoP3692iKopVVczzZVka1S3xfVJQw8B6Ejj3fAbg49HI9hwFUAAFUOB/BYDDDeUwqUDBCwyO6dzSal7QPcY027oL6pJQk9fJjOEVdjVKjxqSJAE2f0+h13M2XCok3NiIIMgX8guBhVuAILp8rwAAAABJRU5ErkJggg=="),
];
}
function setup(){
createCanvas(600, 600);
imageMode(CENTER);
}
function draw(){
background(255);
// update + draw enemies
for(let i = 0; i < enemies.length; i++){
enemies[i].update();
}
text("click to spawn", 10, 15);
}
function mousePressed(){
// pick a random image
let randomImage = images[int(random(images.length))];
// make a new enemy
enemies.push(new Enemy(randomImage, mouseX, mouseY));
}
class Enemy {
constructor(skin, x, y) {
this.skin = skin;
this.position = createVector(x, y);
this.velocity = createVector(random(-1,1), random(-1,1));
}
update(){
// add velocity
this.position.add(this.velocity);
// check borders and flip direction (roughly)
if(this.position.x < 0 || this.position.x > width ||
this.position.y < 0 || this.position.y > height){
this.velocity.mult(-1);
}
// render
push();
blendMode(MULTIPLY);
image(this.skin, this.position.x, this.position.y);
pop();
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.3.0/p5.min.js"></script>
In the example above I'm using base64 encoded images to avoid CORS issues, however you should be fine loading your custom images instead.
The idea is to store the images globally so you can re-use them later when instantiating new enemies. (If you have a couple of images individual variables per image should do, otherwise an array will make it easier to manage)
Notice in Enemy constructor simply receives a reference to the previously loaded image:
// when declared
constructor(skin, x, y) {
this.skin = skin;
// when intantiated
new Enemy(randomImage, mouseX, mouseY)
The only thing left to do is to render the image as needed:
image(this.skin, this.position.x, this.position.y);
Update Based on the comment and shared code the is that each enemy shares the same gif which updates at the same rate with the same frame number in sync.
One option would be to fill the images array with multiple copies of the same image, essentially reloading the gif once per enemy, though that seems wasteful.
Unfortunately p5.Image.get() returns a snapshot of the current frame and there is no magic function to clone a loaded gif, however the undocummented gifProperties holds the necessary data to manually do so:
let images;
let enemies = [];
function preload(){
// load images and store them in an array
images = [
loadImage("data:image/gif;base64,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"),
];
}
function setup(){
createCanvas(600, 600);
imageMode(CENTER);
}
function draw(){
background(255);
// update + draw enemies
for(let i = 0; i < enemies.length; i++){
enemies[i].update();
}
text("click to spawn", 10, 15);
}
function mousePressed(){
// pick a random image
let randomImage = images[int(random(images.length))];
// offset the start frame of each enemy by one
let startFrame = enemies.length % randomImage.numFrames();
// make a new enemy
enemies.push(new Enemy(cloneGif(randomImage,startFrame), mouseX, mouseY));
}
function cloneGif(gif, startFrame){
let gifClone = gif.get();
// access original gif properties
gp = gif.gifProperties;
// make a new object for the clone
gifClone.gifProperties = {
displayIndex: gp.displayIndex,
// we still point to the original array of frames
frames: gp.frames,
lastChangeTime: gp.lastChangeTime,
loopCount: gp.loopCount,
loopLimit: gp.loopLimit,
numFrames: gp.numFrames,
playing: gp.playing,
timeDisplayed: gp.timeDisplayed
};
// optional tweak the start frame
gifClone.setFrame(startFrame);
return gifClone;
}
class Enemy {
constructor(skin, x, y) {
this.skin = skin;
this.x = x;
this.y = y;
}
update(){
image(this.skin, this.x, this.y);
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.3.0/p5.min.js"></script>
The cloneGif makes a new object (which means new start frame, etc.), however it should point to the original gif's frames (which hold the pixels / ImageData)
Now each enemy starts at a new frame.
I noticed that changing the delay seems to affect all enemies: once a delay is set to one it seems the underlying imagedata is copied at the same rate.
If you need to have different delay times you can manage still access the same gif frame ImageData, but rather than relying on p5.Image to control the gif playback (e.g. setFrame() / delay()) you'd need manually manage that and render to p5's canvas:
let images;
let enemies = [];
function preload(){
// load images and store them in an array
images = [
loadImage("data:image/gif;base64,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"),
];
}
function setup(){
createCanvas(600, 600);
imageMode(CENTER);
}
function draw(){
background(255);
// update + draw enemies
for(let i = 0; i < enemies.length; i++){
enemies[i].update();
}
text("click to spawn", 10, 15);
}
function mousePressed(){
// pick a random image
let randomImage = images[int(random(images.length))];
// make a new enemy
let enemy = new Enemy({frames: randomImage.gifProperties.frames, gifWidth: randomImage.width, gifH: randomImage.height}, mouseX, mouseY);
// pick a random start frame
enemy.currentFrame = int(random(images.length));
// pick a random per gif frame delay (e.g. the larger the number the slower the gif will play)
enemy.frameDelay = int(random(40, 240));
enemies.push(enemy);
}
class Enemy {
constructor(gifData, x, y) {
this.frames = gifData.frames;
this.offX = -int(gifData.gifWidth * 0.5);
this.offY = -int(gifData.gifHeight * 0.5);
this.currentFrame = 0;
this.numFrames = this.frames.length;
this.frameDelay = 100;
this.lastFrameUpdate = millis();
this.x = x;
this.y = y;
}
update(){
let millisNow = millis();
// increment frame
if(millisNow - this.lastFrameUpdate >= this.frameDelay){
this.currentFrame = (this.currentFrame + 1) % this.numFrames;
this.lastFrameUpdate = millisNow;
}
// render directly to p5's canvas context
drawingContext.putImageData(this.frames[this.currentFrame].image, this.x + this.offX, this.y + this.offY);
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.3.0/p5.min.js"></script>
Also notice the p5.Image does something nice about transparency (even when the original gif is missing it): that's something you'd need to manually address if going this lower level route.

How can I show only a part of an image in Processing?

I want to compare two images (same size) fr a presentation with a shifting line. On the left side of this line the one image is should be displayed while on the right side the other picture should stay visible.
This is what I tried (bitmap and ch are the images)
PImage bitmap;
PImage ch;
int framerate = 1000;
void setup() {
size(502, 316);
bitmap = loadImage("bitmap_zentriert.jpg"); // Load an image into the program
ch = loadImage("Karte_schweiz_zentriert.jpg"); // Load an image into the program
frameRate(40); //framerate
}
void draw() {
background(255);
image(ch, 10, 10); // the one image in the back
image(bitmap, 10, 10, bitmap.width, bitmap.height, 10, 10, mouseX, bitmap.height); //show part of the second image in front
rect(mouseX, 10, 1, bitmap.height-1); //make line
}
But the image "bitmap" is the whole image distorted.
How can I do that?
I'd recommend using a PGraphics buffer, which is essentially "Another sketch" that also acts as an Image for drawing purposes, and most definitely not looping at "a thousand frames per second". Only draw something when you have something new to draw, using the redraw function in combination with mouse move events:
PImage img1, img2;
PGraphics imagebuffer;
void setup() {
size(502, 316);
imagebuffer = createGraphics(width, height);
img1 = loadImage("first-image.jpg");
img2 = loadImage("second-image.jpg");
noLoop();
}
void mouseMoved() {
redraw();
}
void draw() {
image(img1, 0, 0);
if (mouseX>0) {
imagebuffer = createGraphics(mouseX, height);
imagebuffer.beginDraw();
imagebuffer.image(img2, 0, 0);
imagebuffer.endDraw();
image(imagebuffer, 0, 0);
}
}
In our setup we load the image and turn off looping because we'll be redrawing based on redraw, and then in response to mouse move events, we generate a new buffer that is only as wide as the current x-coordinate of the mouse, draw our image, which gets cropped "for free" because the buffer is only limited width, and then we draw that buffer as if it were an image on top of the image we already have.
There are many ways to do it, one thing I suggest is to create a 3rd image with the same width and height, then you load the two images pixels and insert in your 3rd image part of image1 pixels and then second part from image2, I wrote this code to test it out, works fine:
PImage img1, img2, img3;
void setup() {
size(500, 355);
img1 = loadImage("a1.png"); // Load an image into the program
img2 = loadImage("a2.png"); // Load an image into the program
img3 = createImage(width, height, RGB); //create your third image with same width and height
img1.loadPixels(); // Load the image pixels so you can access the array pixels[]
img2.loadPixels();
frameRate(40); // frame rate
}
void draw() {
background(255);
// Copy first half from first image
for(int i = 0; i < mouseX; i++)
{
for (int j = 0; j < height ; j++) {
img3.pixels[j*width+i] = img1.pixels[j*width+i];
}
}
// Copy second half from second image
for(int i = mouseX; i < width; i++)
{
for (int j = 0; j < height ; j++) {
img3.pixels[j*width+i] = img2.pixels[j*width+i];
}
}
// Update the third image pixels
img3.updatePixels();
// Simply draw that image
image(img3, 0, 0); // The one image in the back
// Draw the separation line
rect(mouseX, 0, 0, height); // Make line
}
Result :

Moving an objects while others stay static in a click controlled sketch

So I'm trying to build an animation that I can step through with mouse clicks. Adding individual objects click by click is easy. Sequence I want is as follows:
One object(a) drawn initially.
First mouse click adds an object(b).
Second mouse click adds an object(c).
Third mouse click, object(c) should move across the screen and disappear.
I'm having a problem on the last part of the sequence. I can't figure out how to make the object move and still maintain the static part of the sketch. The normal way of doing movement is to change the coordinates of the object with each loop through the draw() function, and use the background to cover up the previous objects. Can't do that in this case because I need object(a) and object(b) to be persistent.
Code below. Thanks for your help!
var count = 0;
function setup() {
createCanvas(200, 200);
a = new Object1(20, 40);
b = new Object1(20, 85);
c = new Object1(20, 130);
}
function draw() {
background(200);
a.display();
if (count == 1) {
b.display();
}
if (count == 2) {
b.display();
c.display();
}
if (count == 3) { //this is where I have a problem
}
if (count > 3) {
count = 0;
}
}
function Object1(ix, iy, itext) {
this.x = ix;
this.y = iy;
this.text = itext;
this.display = function() {
fill(160);
rect(this.x, this.y, 40, 40);
}
}
function mousePressed() {
count++;
}
Generally how you'd do this is by drawing the static part of your scene to an off-screen buffer, which you can create using the createGraphics() function. From the 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);
}
You'd draw the static part to the buffer, then draw the buffer to the screen, then draw the dynamic stuff on top of it each frame.
This has been discussed before, so I'd recommend doing a search for stuff like "processing pgraphics" and "p5.js buffer" to find a bunch more information.

How to crop an image in P5?

I am working on a program in P5, an offshoot of Processing, that allows the user to upload an image, draw a line on top of the image, and then crop everything outside of the drawn shape out of the image.
(The green line jitters around on purpose)
I managed to get the points of the drawn line into an array, as well as make a shape out of these points. However, the cropping of the image is still a problem.
Processing has this functionality in vertex:
(https://processing.org/reference/vertex_.html)
However, I don't believe P5 has this functionality. I really don't want to have to convert the entire sketch into Processing. Is there any way to do this in P5, or to quickly convert this sketch into processing?
// Make a variable to store the start image, as well as the drop image.
var img;
var cropX = [];
var cropY = [];
var pos = 25;
// Make an array for all paths.
var paths = [];
// Make a bool for whether or not I am painting.
var painting = false;
// Int for how long until drawing the next circle
var next = 10;
// Make vars for vectors that determine where the line is drawn.
var current;
var previous;
// Make ints for how much the lines dance around.
var shake = 10;
var minShake = shake * -1;
var maxShake = shake * 1;
// Make an int for the line thickness.
var thickness = 2;
var camera;
var tracing = false;
// Make vars to store the random values for colours into.
var rc1;
var rc2;
var rc3;
// Variable for the framerate.
var fr;
// Variable that disables drawing lines when you didn't upload an image yet.
var tracing = false;
//------------------------------------------------------------------------------------------------
function preload() {
//Load the starting image, and store it in img.
img = loadImage("assets/startscreen.png");
//Load the sound that plays when you export a screenshot.
soundFormats('mp3');
camera = loadSound('assets/camera.mp3');
}
//------------------------------------------------------------------------------------------------
function setup() {
// Set the framerate so the lines don't jump about too quickly.
fr = 20;
// Setup a canvas
var c = createCanvas(1680, 1050);
// Store a random value out of 255 into the random colour values.
rc1 = random(255);
rc2 = random(255);
rc3 = random(255);
// Apply the right framerate
frameRate(fr);
// Add an event named drop, that runs function gotFile when a file is dropped onto the canvas
c.drop(gotFile);
// Store 0,0 vectors in current and previous.
current = createVector(0, 0);
previous = createVector(0, 0);
};
//------------------------------------------------------------------------------------------------
function draw() {
// Colour the background dark grey.
background(200);
// Draw the loaded image at 0,0 coordinates.
image(img, 0, 0);
//------------------------------------------------------------------------------------------------
// Count if I've been painting for longer than the 'next' variable.
// Also check if tracing is enabled (if I've dropped an image or not).
// If these are true I can draw a new line.
if (millis() > next && painting && tracing) {
// Grab mouse position and store it in variables mouseX and mouseY.
current.x = mouseX;
current.y = mouseY;
// Add new particle
paths[paths.length - 1].add(current);
// Update the 'next' variable, to allow itself 200 extra millisecond for drawing the actual line.
next = millis() + 200;
// Move the mouse values used to draw the end of the line
// to a variable used to draw the start of the line,
// so that the line is continuous.
previous.x = current.x;
previous.y = current.y;
append(cropX, current.x);
append(cropY, current.y);
}
// Make an integer called i, with a value of 0.
// Add 1 to i for each item in the array paths.
// Run this once for each item in the array.
// Name each item in the array 'i' while working.
// Display each item in the array.
for (var i = 0; i < paths.length; i++) {
// Update the current object in the array.
paths[i].update();
// Display each item in the array.
paths[i].display();
}
noStroke();
noFill();
beginShape();
for (var i = 0; i < cropX.length; ++i) {
vertex(cropX[i], cropY[i]);
}
endShape(CLOSE);
}
//------------------------------------------------------------------------------------------------
var ready = false;
// Make a function called gotFile, using the variable file.
function gotFile(file) {
// Check if the dropped file is an image file
if (file.type === 'image') {
// Enable drawing lines.
tracing = true;
// if (ready) {
// img.remove();
// }
// Store the dropped image in the container which used to hold the startimage.
img = createImg(file.data).style("opacity: 100; position: absolute; top: -10; right: -10; z-index: 100;draggable=false;");
ready = true;
// Error message in case not an image file.
} else {
println('Not an image file!');
}
}
//------------------------------------------------------------------------------------------------
function mouseWheel(event) {
//event.delta can be +1 or -1 depending
//on the wheel/scroll direction
print(event.delta);
//move the square one pixel up or down
pos += event.delta;
//uncomment to block page scrolling
return false;
}
function mouseDragged() {
return false;
}
// If left mousebutton is pressed down,
function mousePressed() {
if (mouseButton == LEFT) {
// set the variable counting when to place a new line to 0,
next = 0;
// set painting to true,
painting = true;
// store the mouse coordinates in mouseX and mouseY,
previous.x = mouseX;
previous.y = mouseY;
// and add a new Path method to the array.
paths.push(new Path());
}
}
// When mouse is released, set painting to false, which disables any paths being drawn.
function mouseReleased() {
painting = false;
}
//------------------------------------------------------------------------------------------------
// Describe the Path function that should be pushed to the array.
function Path() {
// Create an array inside this function named particles.
this.particles = [];
}
// Add the variable position to the function Path as its function'()' variable.
Path.prototype.add = function(position) {
// Add a new particle to this particle array with a position and hue.
this.particles.push(new Particle(position, this.hue));
}
// Take the Path() function, and and add this command to it.
Path.prototype.update = function() {
// Make an integer called i, with a value of 0.
// Add 1 to i for each item in the array paths.
// Run this once for each item in the array.
// Name each item in the array 'i' while working.
// Display each item in the array.
for (var i = 0; i < this.particles.length; i++) {
this.particles[i].update();
}
}
// Display the Path array.
Path.prototype.display = function() {
// Loop through the array of particles backwards.
for (var i = this.particles.length - 1; i >= 0; i--) {
// Display each of these particles.
this.particles[i].display(this.particles[i + 1]);
}
}
// Particles along the path
function Particle(position, hue) {
// Set the position of Particles to the mouseposition by creating a vector.
this.position = createVector(position.x, position.y);
}
// Constantly update Particle.
Particle.prototype.update = function() {}
// Draw particle and connect it with a line
// Draw a line to another
Particle.prototype.display = function(other) {
stroke(255, 255);
// If we need to draw a line
if (other) {
stroke(rc1, rc2, rc3);
strokeWeight(thickness);
line(this.position.x + random(minShake, maxShake), this.position.y + random(minShake, maxShake), other.position.x + random(minShake, maxShake), other.position.y + random(minShake, maxShake));
}
if (keyIsDown(LEFT_ARROW) && !camera.isPlaying()) {
camera.play();
save('myRemix.jpg');
print(cropX);
print(cropY)
}
}
The vertex() function you pointed out isn't cropping the image.
To crop an image, you probably want to use the PImage.mask() function. This lets you overlay a mask image overtop your original image so that only parts of your original image are visible.
You can also use a PGraphics instance as a mask image, so you can draw whatever mask you want. Here's a small example that creates a PGraphics mask consisting of an ellipse, then masks the original photo with that mask:
PImage photo;
PGraphics maskImage;
void setup() {
size(100, 100);
photo = loadImage("pic.jpg");
maskImage = createGraphics(photo.width, photo.height);
maskImage.beginDraw();
maskImage.background(0);
maskImage.fill(255);
maskImage.ellipseMode(RADIUS);
maskImage.ellipse(photo.width/2, photo.height/2, 500, 500);
maskImage.endDraw();
photo.mask(maskImage);
}
void draw() {
background(255, 0, 0);
image(photo, 0, 0, width, height);
}
Your sketch would use a shape instead of an ellipse, but those are the basics.

Resources