i'm trying to create a game using Kinect where you have to use your hand movements to wipe away an image to make it disappear revealing another image beneath it within 30 seconds. Now I have already done the code for the loosing condition where if you do not wipe away the image under 30 seconds, the loosing screen will pop up.
However, I am not sure how to code the part to detect when the entire PNG image has been "wiped away". Does this involve using get()? I am not sure how to approach this.
Imagine there are 2 Pimages moondirt.png and moonsurface.png
The Kinect controls the wiping and making Pimage moondirt.png transparent to reveal moonsurface.png
void kinect() {
//----------draw kinect------------
// Draw moon surface
image(moonSurface, 0, 0, width, height);
// Draw the moon dirt
image(moonDirt, 0, 0, width, height);
// Threshold the depth image
int[] rawDepth = kinect.getRawDepth();
for (int i=0; i < rawDepth.length; i++) {
if (rawDepth[i] >= minDepth && rawDepth[i] <= maxDepth) {
depthImg.pixels[i] = color(255);
maskingImg.pixels[i] = color(255);
} else {
depthImg.pixels[i] = color(0);
}
}
//moonDirt.resize(640, 480); //(640, 480);
moonDirt.loadPixels();
for (int i=0; i < rawDepth.length; i++) {
if ( maskingImg.pixels[i] == color(255) ) {
moonDirt.pixels[i] = color( 0, 0, 0, 0 );
}
}
moonDirt.updatePixels();
image(moonDirt, 0, 0, width, height);
color c = moonDirt.get(width, height);
updatePixels();
//--------timer-----
if (countDownTimer.complete() == true){
if (timeLeft > 1 ) {
timeLeft--;
countDownTimer.start();
} else {
state = 4;
redraw();
}
}
//show countDown TIMER
String s = "Time Left: " + timeLeft;
textAlign(CENTER);
textSize(30);
fill(255,0,0);
text(s, 380, 320);
}
//timer
class Timer {
int startTime;
int interval;
Timer(int timeInterval) {
interval = timeInterval;
}
void start() {
startTime = millis();
}
boolean complete() {
int elapsedTime = millis() - startTime;
if (elapsedTime > interval) {
return true;
}else {
return false;
}
}
}
I see the confusion in this section:
moonDirt.loadPixels();
for (int i=0; i < rawDepth.length; i++) {
if ( maskingImg.pixels[i] == color(255) ) {
moonDirt.pixels[i] = color( 0, 0, 0, 0 );
}
}
moonDirt.updatePixels();
image(moonDirt, 0, 0, width, height);
color c = moonDirt.get(width, height);
You are already using pixels[] which is more efficient than get() which is great.
Don't forget to call updatePixels() when you're done. You already do that for moonDirt, but not for maskingImg
If you want to find out if an image has been cleared (where clear means transparent black (color(0,0,0,0)) in this case).
It looks like you're already familiar with functions that take parameters and return values. The count function will need to:
take 2 arguments: the image to process and the colour to check and count
return the total count
iterate through all pixels: if any pixels match the 2nd argument, the total count increments
Something like this:
/**
* countPixels - counts pixels of of a certain colour within an image
* #param image - the PImage to loop through
* #param colorToCount - the colour to count pixels present in the image
* return int - the number of found pixels (between 0 and image.pixels.length)
*/
int countPixels(PImage image,color colorToCount){
// initial transparent black pixel count
int count = 0;
// make pixels[] available
image.loadPixels();
// for each pixel
for(int i = 0 ; i < image.pixels.length; i++){
// check if it's transparent black
if(image.pixels[i] == colorToCount){
// if so, increment the counter
count++;
}
}
// finally return the count
return count;
}
Within your code you could use it like so:
...
// Threshold the depth image
int[] rawDepth = kinect.getRawDepth();
for (int i=0; i < rawDepth.length; i++) {
if (rawDepth[i] >= minDepth && rawDepth[i] <= maxDepth) {
depthImg.pixels[i] = color(255);
maskingImg.pixels[i] = color(255);
} else {
depthImg.pixels[i] = color(0);
}
}
maskingImg.updatePixels();
//moonDirt.resize(640, 480); //(640, 480);
moonDirt.loadPixels();
for (int i=0; i < rawDepth.length; i++) {
if ( maskingImg.pixels[i] == color(255) ) {
moonDirt.pixels[i] = color( 0, 0, 0, 0 );
}
}
moonDirt.updatePixels();
image(moonDirt, 0, 0, width, height);
int leftToReveal = moonDirt.pixels.length;
int revealedPixels = countPixels(moonDirt,color(0,0,0,0));
int percentageClear = round(((float)revealedPixels / leftToReveal) * 100);
println("revealed " + revealedPixels + " of " + leftToReveal + " pixels -> ~" + percentageClear + "% cleared");
...
You have the option to set the condition for all pixels to be cleared or a ratio/percentage (e.g. if more 90% is clear, that's good enough) to then change the game state accordingly.
Related
I was steered over to this forum when I asked my lecturer for advice on a piece of code for a group project. The general idea is that there are two images on top of each other, the user can wipe the top image away to reveal the one underneath.
Using some other projects from this forum, I have managed to get the basics running, however I am struggling to get the code to the starting point once the user lets go of the mouse.
I would also appreciate any advice regarding how to convert this to using a touch screen. I have looked at the multitouch code within the processing app, however it does not allow me to add images to this, and if I try and use the computer software it does not seem to like the multitouch. Is there any way around this?
The code I currently have is below, I will be greatful so any advice or input- thanks in advance!
PImage img, front;
int xstart, ystart, xend, yend;
int ray;
void setup()
{
size(961, 534);
img = loadImage("back.jpg");
front = loadImage("front.jpg");
xstart = 0;
ystart = 0;
xend = img.width;
yend = img.height;
ray = 50;
}
void draw()
{
{
img.loadPixels();
front.loadPixels();
// loop over image pixels
for (int x = xstart; x < xend; x++)
{
for (int y = ystart; y < yend; y++ )
{
int loc = x + y*img.width;
float dd = dist(mouseX, mouseY, x, y);
// pixels distance less than ray
if (mousePressed && dd < 50)
{
// set equal pixel
front.pixels[loc] = img.pixels[loc];
}
else
{
if (!mousePressed)
{
// reset- this is what I have not been able to work as of yet
front.pixels[loc] = ;
}
}
}
}
img.updatePixels();
front.updatePixels();
// show front image
image(front, 0, 0);
}
}
I recommend to use a mask instead of changing the pixels of the image. Create an empty image and associated it as mask to the the image:
img = loadImage("back.jpg");
front = loadImage("front.jpg");
mask = createImage(img.width, img.height, RGB);
img.mask(mask);
If you now draw both images, then you can only "see" the front image:
image(front, 0, 0);
image(img, 0, 0);
Set the color of the mask (255, 255, 255) instead of changing the pixel of front:
mask.pixels[loc] = color(255, 255, 255);
and reapply the mask to the image
img.mask(mask);
When the mouse button is released, the pixels of the mask have to be changed back to (0, 0, 0) or simply create a new and empty mask:
mask = createImage(img.width, img.height, RGB);
See the example where I applied the suggestions to your original code:
PImage img, front, mask;
int xstart, ystart, xend, yend;
int ray;
void setup() {
size(961, 534);
img = loadImage("back.jpg");
front = loadImage("front.jpg");
mask = createImage(img.width, img.height, RGB);
img.mask(mask);
xstart = 0;
ystart = 0;
xend = img.width;
yend = img.height;
ray = 50;
}
void draw() {
img.loadPixels();
front.loadPixels();
// loop over image pixels
for (int x = xstart; x < xend; x++) {
for (int y = ystart; y < yend; y++ ) {
int loc = x + y*img.width;
float dd = dist(mouseX, mouseY, x, y);
if (mousePressed && dd < 50) {
mask.pixels[loc] = color(255, 255, 255);
}
else {
if (!mousePressed) {
//mask = createImage(img.width, img.height, RGB);
mask.pixels[loc] = color(0, 0, 0);
}
}
}
}
mask.updatePixels();
img.mask(mask);
// show front image
image(front, 0, 0);
image(img, 0, 0);
}
I am trying to extract user silhouette and put it above my images. I was able to make a mask and cut user from rgb image. But the contour is messy.
The question is how I can make the mask more precise (to fit real user). I've tried ERODE-DILATE filters, but they don't do much. Maybe I need some Feather filter like in Photoshop. Or I don't know.
Here is my code.
import SimpleOpenNI.*;
SimpleOpenNI context;
PImage mask;
void setup()
{
size(640*2, 480);
context = new SimpleOpenNI(this);
if (context.isInit() == false)
{
exit();
return;
}
context.enableDepth();
context.enableRGB();
context.enableUser();
context.alternativeViewPointDepthToImage();
}
void draw()
{
frame.setTitle(int(frameRate) + " fps");
context.update();
int[] userMap = context.userMap();
background(0, 0, 0);
mask = loadImage("black640.jpg"); //just a black image
int xSize = context.depthWidth();
int ySize = context.depthHeight();
mask.loadPixels();
for (int y = 0; y < ySize; y++) {
for (int x = 0; x < xSize; x++) {
int index = x + y*xSize;
if (userMap[index]>0) {
mask.pixels[index]=color(255, 255, 255);
}
}
}
mask.updatePixels();
image(mask, 0, 0);
mask.filter(DILATE);
mask.filter(DILATE);
PImage rgb = context.rgbImage();
rgb.mask(mask);
image(rgb, context.depthWidth() + 10, 0);
}
It's good you're aligning the RGB and depth streams.
There are few things that could be improved in terms of efficiency:
No need to reload a black image every single frame (in the draw() loop) since you're modifying all the pixels anyway:
mask = loadImage("black640.jpg"); //just a black image
Also, since you don't need the x,y coordinates as you loop through the user data, you can use a single for loop which should be a bit faster:
for(int i = 0 ; i < numPixels ; i++){
mask.pixels[i] = userMap[i] > 0 ? color(255) : color(0);
}
instead of:
for (int y = 0; y < ySize; y++) {
for (int x = 0; x < xSize; x++) {
int index = x + y*xSize;
if (userMap[index]>0) {
mask.pixels[index]=color(255, 255, 255);
}
}
}
Another hacky thing you could do is retrieve the userImage() from SimpleOpenNI, instead of the userData() and apply a THRESHOLD filter to it, which in theory should give you the same result as above.
For example:
int[] userMap = context.userMap();
background(0, 0, 0);
mask = loadImage("black640.jpg"); //just a black image
int xSize = context.depthWidth();
int ySize = context.depthHeight();
mask.loadPixels();
for (int y = 0; y < ySize; y++) {
for (int x = 0; x < xSize; x++) {
int index = x + y*xSize;
if (userMap[index]>0) {
mask.pixels[index]=color(255, 255, 255);
}
}
}
could be:
mask = context.userImage();
mask.filter(THRESHOLD);
In terms of filtering, if you want to shrink the silhouette you should ERODE and bluring should give you a bit of that Photoshop like feathering.
Note that some filter() calls take arguments (like BLUR), but others don't like the ERODE/DILATE morphological filters, but you can still roll your own loops to deal with that.
I also recommend having some sort of easy to tweak interface (it can be fancy slider or a simple keyboard shortcut) when playing with filters.
Here's a rough attempt at the refactored sketch with the above comments:
import SimpleOpenNI.*;
SimpleOpenNI context;
PImage mask;
int numPixels = 640*480;
int dilateAmt = 1;
int erodeAmt = 1;
int blurAmt = 0;
void setup()
{
size(640*2, 480);
context = new SimpleOpenNI(this);
if (context.isInit() == false)
{
exit();
return;
}
context.enableDepth();
context.enableRGB();
context.enableUser();
context.alternativeViewPointDepthToImage();
mask = createImage(640,480,RGB);
}
void draw()
{
frame.setTitle(int(frameRate) + " fps");
context.update();
int[] userMap = context.userMap();
background(0, 0, 0);
//you don't need to keep reloading the image every single frame since you're updating all the pixels bellow anyway
// mask = loadImage("black640.jpg"); //just a black image
// mask.loadPixels();
// int xSize = context.depthWidth();
// int ySize = context.depthHeight();
// for (int y = 0; y < ySize; y++) {
// for (int x = 0; x < xSize; x++) {
// int index = x + y*xSize;
// if (userMap[index]>0) {
// mask.pixels[index]=color(255, 255, 255);
// }
// }
// }
//a single loop is usually faster than a nested loop and you don't need the x,y coordinates anyway
for(int i = 0 ; i < numPixels ; i++){
mask.pixels[i] = userMap[i] > 0 ? color(255) : color(0);
}
//erode
for(int i = 0 ; i < erodeAmt ; i++) mask.filter(ERODE);
//dilate
for(int i = 0 ; i < dilateAmt; i++) mask.filter(DILATE);
//blur
mask.filter(BLUR,blurAmt);
mask.updatePixels();
//preview the mask after you process it
image(mask, 0, 0);
PImage rgb = context.rgbImage();
rgb.mask(mask);
image(rgb, context.depthWidth() + 10, 0);
//print filter values for debugging purposes
fill(255);
text("erodeAmt: " + erodeAmt + "\tdilateAmt: " + dilateAmt + "\tblurAmt: " + blurAmt,15,15);
}
void keyPressed(){
if(key == 'e') erodeAmt--;
if(key == 'E') erodeAmt++;
if(key == 'd') dilateAmt--;
if(key == 'D') dilateAmt++;
if(key == 'b') blurAmt--;
if(key == 'B') blurAmt++;
//constrain values
if(erodeAmt < 0) erodeAmt = 0;
if(dilateAmt < 0) dilateAmt = 0;
if(blurAmt < 0) blurAmt = 0;
}
Unfortunately I can't test with an actual sensor right now, so please use the concepts explained, but bare in mind the full sketch code isn't tested.
This above sketch (if it runs) should allow you to use keys to control the filter parameters (e/E to decrease/increase erosion, d/D for dilation, b/B for blur). Hopefully you'll get satisfactory results.
When working with SimpleOpenNI in general I advise recording an .oni file (check out the RecorderPlay example for that) of a person for the most common use case. This will save you some time on the long run when testing and will allow you to work remotely with the sensor detached. One thing to bare in mind, the depth resolution is reduced to half on recordings (but using a usingRecording boolean flag should keep things safe)
The last and probably most important point is about the quality of the end result. Your resulting image can't be that much better if the source image isn't easy to work with to begin with. The depth data from the original Kinect sensor isn't great. The Asus sensors feel a wee bit more stable, but still the difference is negligible in most cases. If you are going to stick to one of these sensors, make sure you've got a clear background and decent lighting (without too much direct warm light (sunlight, incandescent lightbulbs, etc.) since they may interfere with the sensor)
If you want a more accurate user cut and the above filtering doesn't get the results you're after, consider switching to a better sensor like KinectV2. The depth quality is much better and the sensor is less susceptible to direct warm light. This may mean you need to use Windows (I see there's a KinectPV2 wrapper available) or OpenFrameworks(c++ collections of libraries similar to Processing) with ofxKinectV2
I've tried built-in erode-dilate-blur in processing. But they are very inefficient. Every time I increment blurAmount in img.filter(BLUR,blurAmount), my FPS decreases by 5 frames.
So I decided to try opencv. It is much better in comparison. The result is satisfactory.
import SimpleOpenNI.*;
import processing.video.*;
import gab.opencv.*;
SimpleOpenNI context;
OpenCV opencv;
PImage mask;
int numPixels = 640*480;
int dilateAmt = 1;
int erodeAmt = 1;
int blurAmt = 1;
Movie mov;
void setup(){
opencv = new OpenCV(this, 640, 480);
size(640*2, 480);
context = new SimpleOpenNI(this);
if (context.isInit() == false) {
exit();
return;
}
context.enableDepth();
context.enableRGB();
context.enableUser();
context.alternativeViewPointDepthToImage();
mask = createImage(640, 480, RGB);
mov = new Movie(this, "wild.mp4");
mov.play();
mov.speed(5);
mov.volume(0);
}
void movieEvent(Movie m) {
m.read();
}
void draw() {
frame.setTitle(int(frameRate) + " fps");
context.update();
int[] userMap = context.userMap();
background(0, 0, 0);
mask.loadPixels();
for (int i = 0; i < numPixels; i++) {
mask.pixels[i] = userMap[i] > 0 ? color(255) : color(0);
}
mask.updatePixels();
opencv.loadImage(mask);
opencv.gray();
for (int i = 0; i < erodeAmt; i++) {
opencv.erode();
}
for (int i = 0; i < dilateAmt; i++) {
opencv.dilate();
}
if (blurAmt>0) {//blur with 0 amount causes error
opencv.blur(blurAmt);
}
mask = opencv.getSnapshot();
image(mask, 0, 0);
PImage rgb = context.rgbImage();
rgb.mask(mask);
image(mov, context.depthWidth() + 10, 0);
image(rgb, context.depthWidth() + 10, 0);
fill(255);
text("erodeAmt: " + erodeAmt + "\tdilateAmt: " + dilateAmt + "\tblurAmt: " + blurAmt, 15, 15);
}
void keyPressed() {
if (key == 'e') erodeAmt--;
if (key == 'E') erodeAmt++;
if (key == 'd') dilateAmt--;
if (key == 'D') dilateAmt++;
if (key == 'b') blurAmt--;
if (key == 'B') blurAmt++;
//constrain values
if (erodeAmt < 0) erodeAmt = 0;
if (dilateAmt < 0) dilateAmt = 0;
if (blurAmt < 0) blurAmt = 0;
}
I am using Processing in Java to perpetually draw a line graph. This requires a clearing rectangle to draw over drawn lines to make room for the new part of the graph. Everything works fine, but when I call a method, the clearing stops working as it did before. Basically the clearing works by drawing a rectangle in front of where the line is currently at
Below are the two main methods involved. The drawGraph function works fine until I call the redrawGraph which redraws the graph based on the zoom. I think the center variable is the cause of the problem but I cannot figure out why.
public void drawGraph()
{
checkZoom();
int currentValue = seismograph.getCurrentValue();
int lastValue = seismograph.getLastValue();
step = step + zoom;
if(step>offset){
if(restartDraw == true)
{
drawOnGraphics(step-zoom, lastY2, step, currentValue);
image(graphGraphics, 0, 0);
restartDraw = false;
}else{
drawOnGraphics(step-zoom, lastValue, step, currentValue);
image(graphGraphics, 0, 0);
}
} // draw graph (connect last to current point // increase step - x axis
float xClear = step+10; // being clearing area in front of current graph
if (xClear>width - 231) {
xClear = offset - 10; // adjust for far side of the screen
}
graphGraphics.beginDraw();
if (step>graphSizeX+offset) { // draw two clearing rectangles when graph isn't split
// left = bg.get(0, 0, Math.round(step-graphSizeX), height - 200); // capture clearing rectangle from the left side of the background image
// graphGraphics.image(left, 0, 0); // print left clearing rectangle
// if (step+10<width) {
// right = bg.get(Math.round(step+10), 0, width, height - 200); // capture clearing rectangle from the right side of the background image
// // print right clearing rectangle
// }
} else { // draw one clearing rectangle when graph is split
center = bg.get(Math.round(xClear), lastY2, offset, height - 200); // capture clearing rectangle from the center of the background image
graphGraphics.image(center, xClear - 5, 0);// print center clearing rectangle
}
if (step > graphSizeX+offset) { // reset set when graph reaches the end
step = 0+offset;
}
graphGraphics.endDraw();
image(graphGraphics, 0 , 0);
System.out.println("step: " + step + " zoom: " + zoom + " currentValue: "+ currentValue + " lastValue: " + lastValue);
}
private void redrawGraph() //resizes graph when zooming
{
checkZoom();
Object[] data = seismograph.theData.toArray();
clearGraph();
step = offset;
int y2, y1 = 0;
int zoomSize = (int)((width - offset) / zoom);
int tempCount = 0;
graphGraphics.beginDraw();
graphGraphics.strokeWeight(2); // line thickness
graphGraphics.stroke(242,100,66);
graphGraphics.smooth();
while(tempCount < data.length)
{
try
{
y2 = (int)data[tempCount];
step = step + zoom;
if(step > offset && y1 > 0 && step < graphSizeX+offset){
graphGraphics.line(step-zoom, y1, step, y2);
}
y1 = y2;
tempCount++;
lastY2 = y2;
}
catch (Exception e)
{
System.out.println(e.toString());
}
}
graphGraphics.endDraw();
image(graphGraphics, 0, 0);
restartDraw = true;
}
Any help and criticisms are welcome. Thank you for your valuable time.
I'm not sure if that approach is the best. You can try something as simple as this:
// Learning Processing
// Daniel Shiffman
// http://www.learningprocessing.com
// Example: a graph of random numbers
float[] vals;
void setup() {
size(400,200);
smooth();
// An array of random values
vals = new float[width];
for (int i = 0; i < vals.length; i++) {
vals[i] = random(height);
}
}
void draw() {
background(255);
// Draw lines connecting all points
for (int i = 0; i < vals.length-1; i++) {
stroke(0);
strokeWeight(2);
line(i,vals[i],i+1,vals[i+1]);
}
// Slide everything down in the array
for (int i = 0; i < vals.length-1; i++) {
vals[i] = vals[i+1];
}
// Add a new random value
vals[vals.length-1] = random(height);//use seismograph.getCurrentValue(); here instead
}
You can easily do the same using a PGraphics buffer as your code suggests:
// Learning Processing
// Daniel Shiffman
// http://www.learningprocessing.com
// Example: a graph of random numbers
float[] vals;
PGraphics graph;
void setup() {
size(400,200);
graph = createGraphics(width,height);
// An array of random values
vals = new float[width];
for (int i = 0; i < vals.length; i++) {
vals[i] = random(height);
}
}
void draw() {
graph.beginDraw();
graph.background(255);
// Draw lines connecting all points
for (int i = 0; i < vals.length-1; i++) {
graph.stroke(0);
graph.strokeWeight(2);
graph.line(i,vals[i],i+1,vals[i+1]);
}
graph.endDraw();
image(graph,0,0);
// Slide everything down in the array
for (int i = 0; i < vals.length-1; i++) {
vals[i] = vals[i+1];
}
// Add a new random value
vals[vals.length-1] = random(height);//use seismograph.getCurrentValue(); here instead
}
The main idea is to cycle the newest data in an array and simply draw the values from this shifting array. As long as you clear the previous frame (background()) the graph should look ok.
so, here is the question, i need to draw the position indicator corresponding to my hand position and then perform some manipulations on an image
here is the screen capture:
the left half of the screen is the image, and the right half of the screen is my camera,
the program will draw the position indicator corresponding to my hand position,
my problem is that the cursor cannot be disappeared and it will draw many times!
here is the code:
import gab.opencv.*;
import processing.video.*;
import java.awt.*;
PImage img;
PImage select;
PImage cur;
OpenCV opencv;
Capture cam;
int prevPositionX, prevPositionY, currPositionX, currPositionY;
int mode = -1; //mode 1 = s (select) mode 2 = c (copy) mode 3 = d (draw)
int select_ind = -1;
//store every dectected things
Rectangle[] hand;
//store the biggest hand
Rectangle bhand;
void setup() {
size(1280, 480);
img = loadImage("test.jpg");
cur = loadImage("cursor.png");
stroke(255,10,0);
opencv = new OpenCV(this, 640, 480);
opencv.loadCascade("aGest.xml");
cam = new Capture(this, 640, 480);
cam.start();
image(img, 0, 0, img.width, img.height);
}
void draw(){
if (cam.available()==true) {
cam.read();
}
opencv.loadImage(cam);
hand = opencv.detect();
pushMatrix();
scale(-1.0, 1.0);
image(cam, -1280, 0);
popMatrix();
int handcount = -1;
int handsize = -1;
//calculate the biggest hand
for( int i=0; i < hand.length; i++ ) {
if(handsize < (hand[i].width * hand[i].height)){
handsize = hand[i].width * hand[i].height;
handcount = 1;
bhand = hand[i];
}
}
if(handcount > 0){
rect(1280 - bhand.x, bhand.y, -bhand.width, bhand.height);
noFill();
//draw the position indicator
image(cur, 480 - bhand.x, bhand.y, 16, 16);
prevPositionX = currPositionX;
prevPositionY = currPositionY;
currPositionX = 480 - bhand.x + 4;
currPositionY = bhand.y;
//select mode
if (mode == 1){
}
//copy mode
else if (mode == 2){
}
//draw mode
else if (mode == 3){
line(prevPositionX,prevPositionY,currPositionX,currPositionY);
}
}
}
void keyPressed(){
if(key=='s'||key=='S')
mode = 1;
else if(key=='c'||key=='C')
mode = 2;
else if(key=='d'||key=='D')
mode = 3;
else if(key=='i'||key=='I')
image(img, 0, 0, img.width, img.height);
}
void keyReleased(){
if(select_ind > -1 && mode == 2){
//to be done
}
mode = -1;
}
i am working with the drawing mode which is to draw a line on the image,
and i know the problem but i do not know how to solve it,
i need to add this : image(img, 0, 0, img.width, img.height); to the first
of draw() function, but the line will also be deleted. i want to keep
the line like the screen capture.
Please give me a hand and sorry for the bad english. Thanks
If you need to persist just part of the draw, you need to redraw it every frame, while still "clearing" the background using image(img, 0, 0, img.width, img.height). This means store coordinates of lines and redraw it every time, also note that you can hide the cursor... SomeThing like this:
// YOU GOT ADD (CLICK) AT LEAST 2 POINTS TO SEE IT WORKING ;)
ArrayList<PVector> positions = new ArrayList<PVector>();
void setup(){
size(600, 600);
//if you don't want to see the cursor...
noCursor();
}
void draw(){
//clear screen
background(255);
//always draw at mousePosition
//a "custom cursor"
//that will not persist as screen is beeing cleared
fill(200,80,220);
noStroke();
ellipse(mouseX, mouseY, 20, 20);
stroke(80);
PVector prevP = null;
for (PVector p:positions) {
if(prevP != null) {
line(prevP.x, prevP.y, p.x, p.y);
}
prevP = p.get();
}
}
void mouseClicked() {
positions.add(new PVector(mouseX, mouseY));
}
EDIT:
Also you can use PGraphics as layers to persist just part of the drawing without redrawing all the stuff over and over... :)
I'm trying to write an interactive game of life where i can manually insert gliders in the game field.
How i want it to work is i have a glider button and after i press it i can move my cursor to where i want glider to be set on the grid and after i click on the grid glider integrates in the game of life.
I'm using processing, and i'm using this sketch as a start up code. http://www.openprocessing.org/sketch/95216
This is the code to create new cells on mouse press (one at the time)
// Create new cells manually on pause
if (pause && !gliderSelected && mousePressed) {
// Map and avoid out of bound errors
int xCellOver = int(map(mouseX, 0, width, 0, width/cellSize));
xCellOver = constrain(xCellOver, 0, width/cellSize-1);
int yCellOver = int(map(mouseY, 0, height, 0, height/cellSize));
yCellOver = constrain(yCellOver, 0, height/cellSize-1);
// Check against cells in buffer
if (cellsBuffer[xCellOver][yCellOver]==1) { // Cell is alive
cells[xCellOver][yCellOver]=0; // Kill
fill(dead); // Fill with kill color
}
else { // Cell is dead
cells[xCellOver][yCellOver]=1; // Make alive
fill(alive); // Fill alive color
}
}
else if (pause && !mousePressed) { // And then save to buffer once mouse goes up
// Save cells to buffer (so we operate with one array keeping the other intact)
for (int x=0; x<width/cellSize; x++) {
for (int y=0; y<height/cellSize; y++) {
cellsBuffer[x][y] = cells[x][y];
}
}
}
Glider shape is :
OXO
OOX
XXX
where O is a dead cell and X is an alive cell.
//create gliders on press
if (pause && gliderSelected && mousePressed) {
// Map and avoid out of bound errors
int xCellOver = int(map(mouseX, 0, width, 0, width/cellSize));
xCellOver = constrain(xCellOver, 0, width/cellSize-1);
int yCellOver = int(map(mouseY, 0, height, 0, height/cellSize));
yCellOver = constrain(yCellOver, 0, height/cellSize-1);
//here i thought of maybe creating an array of cells that map the glider and then running a loop to change the grid cell status according to the glider array
}
I'm not sure how to make an array that will store the glider cell locations. Each cell is a 10 pixel square, so i know how to map it if i wanted to just build it, but not sure how to stick it in the array and then integrate it in the grid.
There are two different things in play here, how to change the cells from dead to alive in a grid, and also how to display the change before you do it. The array "gliderArray" stores your glider shape, and that is applied over the grid by going over the array and replacing the underlying grid with whatever is in the array...
As for the display, you either have to make a different state for the cells where it is displayed that they are going to change, or redraw their rectangles... This solution is the second way...
void draw() {
//Draw grid
for (int x=0; x<width/cellSize; x++) {
for (int y=0; y<height/cellSize; y++) {
if (cells[x][y]==1) {
fill(alive); // If alive
}
else {
fill(dead); // If dead
}
rect (x*cellSize, y*cellSize, cellSize, cellSize);
}
}
// Iterate if timer ticks
if (millis()-lastRecordedTime>interval) {
if (!pause) {
iteration();
lastRecordedTime = millis();
}
}
//Glider shape is :
//OXO
//OOX
//XXX
//where O is a dead cell and X is an alive cell.
int [][] gliderArray = new int [][] {
{ 0, 1, 0 }
,
{ 0, 0, 1 }
,
{ 1, 1, 1 }
};
// Create new cells manually on pause
if (pause) {
// Map and avoid out of bound errors
int xCellOver = int(map(mouseX, 0, width, 0, width/cellSize));
xCellOver = constrain(xCellOver, 0, width/cellSize-1);
int yCellOver = int(map(mouseY, 0, height, 0, height/cellSize));
yCellOver = constrain(yCellOver, 0, height/cellSize-1);
if (glider) {
// Map again because glider takes +- 1 cells on each direction
xCellOver = constrain(xCellOver, 1, width/cellSize-2);
yCellOver = constrain(yCellOver, 1, height/cellSize-2);
}
if (mousePressed) {
// Check against cells in buffer
if (!glider) {
if (cellsBuffer[xCellOver][yCellOver]==1) { // Cell is alive
cells[xCellOver][yCellOver]=0; // Kill
fill(dead); // Fill with kill color
}
else { // Cell is dead
cells[xCellOver][yCellOver]=1; // Make alive
fill(alive); // Fill alive color
}
}
else {
for (int i=-1; i<=1; i++) {
for (int j=-1; j<=1; j++) {
cells[xCellOver+j][yCellOver+i] = gliderArray[i+1][j+1];
}
}
}
}
else {
for (int x=0; x<width/cellSize; x++) {
for (int y=0; y<height/cellSize; y++) {
cellsBuffer[x][y] = cells[x][y];
if (glider && x >= xCellOver-1 && x <= xCellOver+1 && y >= yCellOver-1 && y <= yCellOver+1) {
for (int i=-1; i<=1; i++) {
for (int j=-1; j<=1; j++) {
if (x == xCellOver+j && y == yCellOver+i) fill(gliderArray[i+1][j+1] == 1? color(255, 125, 0) : dead);
}
}
rect (x*cellSize, y*cellSize, cellSize, cellSize);
}
else if (x == xCellOver && y == yCellOver) {
fill(cellsBuffer[x][y] == 1? color(0,0,255) : color(255, 125, 0));
rect (x*cellSize, y*cellSize, cellSize, cellSize);
}
}
}
}
}
}
You will also need a global boolean:
boolean glider = false;
and another check in void keyPressed()
if (key == 'g') glider = !glider;